diff --git a/abi-compliance-checker-2.4/.github/FUNDING.yml b/3rdparty/abi-compliance-checker-2.4/.github/FUNDING.yml similarity index 100% rename from abi-compliance-checker-2.4/.github/FUNDING.yml rename to 3rdparty/abi-compliance-checker-2.4/.github/FUNDING.yml diff --git a/abi-compliance-checker-2.4/INSTALL b/3rdparty/abi-compliance-checker-2.4/INSTALL similarity index 100% rename from abi-compliance-checker-2.4/INSTALL rename to 3rdparty/abi-compliance-checker-2.4/INSTALL diff --git a/abi-compliance-checker-2.4/LICENSE b/3rdparty/abi-compliance-checker-2.4/LICENSE similarity index 100% rename from abi-compliance-checker-2.4/LICENSE rename to 3rdparty/abi-compliance-checker-2.4/LICENSE diff --git a/abi-compliance-checker-2.4/Makefile b/3rdparty/abi-compliance-checker-2.4/Makefile similarity index 100% rename from abi-compliance-checker-2.4/Makefile rename to 3rdparty/abi-compliance-checker-2.4/Makefile diff --git a/abi-compliance-checker-2.4/Makefile.pl b/3rdparty/abi-compliance-checker-2.4/Makefile.pl similarity index 100% rename from abi-compliance-checker-2.4/Makefile.pl rename to 3rdparty/abi-compliance-checker-2.4/Makefile.pl diff --git a/abi-compliance-checker-2.4/README.md b/3rdparty/abi-compliance-checker-2.4/README.md similarity index 100% rename from abi-compliance-checker-2.4/README.md rename to 3rdparty/abi-compliance-checker-2.4/README.md diff --git a/abi-compliance-checker-2.4/abi-compliance-checker.pl b/3rdparty/abi-compliance-checker-2.4/abi-compliance-checker.pl similarity index 100% rename from abi-compliance-checker-2.4/abi-compliance-checker.pl rename to 3rdparty/abi-compliance-checker-2.4/abi-compliance-checker.pl diff --git a/abi-compliance-checker-2.4/doc/Changelog.html b/3rdparty/abi-compliance-checker-2.4/doc/Changelog.html similarity index 100% rename from abi-compliance-checker-2.4/doc/Changelog.html rename to 3rdparty/abi-compliance-checker-2.4/doc/Changelog.html diff --git a/abi-compliance-checker-2.4/doc/Xml-Descriptor.html b/3rdparty/abi-compliance-checker-2.4/doc/Xml-Descriptor.html similarity index 100% rename from abi-compliance-checker-2.4/doc/Xml-Descriptor.html rename to 3rdparty/abi-compliance-checker-2.4/doc/Xml-Descriptor.html diff --git a/abi-compliance-checker-2.4/doc/index.html b/3rdparty/abi-compliance-checker-2.4/doc/index.html similarity index 100% rename from abi-compliance-checker-2.4/doc/index.html rename to 3rdparty/abi-compliance-checker-2.4/doc/index.html diff --git a/abi-compliance-checker-2.4/modules/Internals/ABIDump.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/ABIDump.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/ABIDump.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/ABIDump.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Basic.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Basic.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Basic.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Basic.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/CallConv.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/CallConv.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/CallConv.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/CallConv.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Descriptor.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Descriptor.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Descriptor.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Descriptor.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/ElfTools.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/ElfTools.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/ElfTools.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/ElfTools.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Filter.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Filter.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Filter.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Filter.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/GccAst.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/GccAst.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/GccAst.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/GccAst.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Input.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Input.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Input.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Input.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Logging.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Logging.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Logging.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Logging.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Mangling.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Mangling.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Mangling.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Mangling.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Path.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Path.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Path.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Path.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/RegTests.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/RegTests.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/RegTests.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/RegTests.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Scripts/Sections.js diff --git a/abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Scripts/Tabs.js diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/CmpSystems.css diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/HeadersDiff.css diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/Report.css b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/Report.css similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Styles/Report.css rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/Report.css diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/SymbolsList.css diff --git a/abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Styles/Tabs.css diff --git a/abi-compliance-checker-2.4/modules/Internals/SysCheck.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/SysCheck.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/SysCheck.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/SysCheck.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/SysFiles.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/SysFiles.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/SysFiles.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/SysFiles.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/TUDump.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/TUDump.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/TUDump.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/TUDump.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/TypeAttr.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/Utils.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/Utils.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/Utils.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/Utils.pm diff --git a/abi-compliance-checker-2.4/modules/Internals/XmlDump.pm b/3rdparty/abi-compliance-checker-2.4/modules/Internals/XmlDump.pm similarity index 100% rename from abi-compliance-checker-2.4/modules/Internals/XmlDump.pm rename to 3rdparty/abi-compliance-checker-2.4/modules/Internals/XmlDump.pm diff --git a/abi-compliance-checker-2.4/modules/RulesBin.xml b/3rdparty/abi-compliance-checker-2.4/modules/RulesBin.xml similarity index 100% rename from abi-compliance-checker-2.4/modules/RulesBin.xml rename to 3rdparty/abi-compliance-checker-2.4/modules/RulesBin.xml diff --git a/abi-compliance-checker-2.4/modules/RulesSrc.xml b/3rdparty/abi-compliance-checker-2.4/modules/RulesSrc.xml similarity index 100% rename from abi-compliance-checker-2.4/modules/RulesSrc.xml rename to 3rdparty/abi-compliance-checker-2.4/modules/RulesSrc.xml diff --git a/abi-dumper-2.1/.github/FUNDING.yml b/3rdparty/abi-dumper-2.1/.github/FUNDING.yml similarity index 100% rename from abi-dumper-2.1/.github/FUNDING.yml rename to 3rdparty/abi-dumper-2.1/.github/FUNDING.yml diff --git a/abi-dumper-2.1/Dockerfile b/3rdparty/abi-dumper-2.1/Dockerfile similarity index 100% rename from abi-dumper-2.1/Dockerfile rename to 3rdparty/abi-dumper-2.1/Dockerfile diff --git a/abi-dumper-2.1/INSTALL b/3rdparty/abi-dumper-2.1/INSTALL similarity index 100% rename from abi-dumper-2.1/INSTALL rename to 3rdparty/abi-dumper-2.1/INSTALL diff --git a/abi-dumper-2.1/LICENSE b/3rdparty/abi-dumper-2.1/LICENSE similarity index 100% rename from abi-dumper-2.1/LICENSE rename to 3rdparty/abi-dumper-2.1/LICENSE diff --git a/abi-dumper-2.1/Makefile b/3rdparty/abi-dumper-2.1/Makefile similarity index 100% rename from abi-dumper-2.1/Makefile rename to 3rdparty/abi-dumper-2.1/Makefile diff --git a/abi-dumper-2.1/Makefile.pl b/3rdparty/abi-dumper-2.1/Makefile.pl similarity index 100% rename from abi-dumper-2.1/Makefile.pl rename to 3rdparty/abi-dumper-2.1/Makefile.pl diff --git a/abi-dumper-2.1/README.md b/3rdparty/abi-dumper-2.1/README.md similarity index 100% rename from abi-dumper-2.1/README.md rename to 3rdparty/abi-dumper-2.1/README.md diff --git a/abi-dumper-2.1/abi-dumper.pl b/3rdparty/abi-dumper-2.1/abi-dumper.pl similarity index 100% rename from abi-dumper-2.1/abi-dumper.pl rename to 3rdparty/abi-dumper-2.1/abi-dumper.pl diff --git a/3rdparty/pyinstaller-4.3/.gitignore b/3rdparty/pyinstaller-4.3/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..027bf3e89837ce0ea91185d97eff72fa96c735f5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/.gitignore @@ -0,0 +1,113 @@ +*.py[co] +__pycache__/ +/*.egg +/*.egg-info/ +*.egg-info/ +/*.egg-link +/.eggs/ +/.pytest_cache/ +/pip-wheel-metadata/ +/.coverage +/.tox + +# / +/AES.pyd +/AES.so +/*.bak +/build/ +/dist/ +/*.patch +/MANIFEST + +# Temporary vim files +*.swp + +# Temporary rope files +.ropeproject + +# PyCharm settings +.idea + +# Spyder settings +.spyproject + +# /tests/ +/tests/build* +/tests/dist* +/tests/logdict*.log +/tests/test*.exe +/tests/warn*.txt +/tests/python_exe.build +/tests/report.xml + +/tests/**/build/ +/tests/**/dist/ + +# Ignore generated spec-files +# NB: You can still add .spec-files to be tracked using `git add` +/tests/old_suite/**/*.spec + +# /tests/functional/data +/tests/functional/data/sphinx/_build + +# /tests/old_suite/basic/ +/tests/old_suite/basic/python_exe.build + +# /tests/old_suite/basic/ctypes/ +/tests/old_suite/basic/ctypes/testctypes.dylib +/tests/old_suite/basic/ctypes/testctypes.so +/tests/old_suite/basic/ctypes/testctypes-win.dll +/tests/old_suite/basic/ctypes/testctypes-win.exp +/tests/old_suite/basic/ctypes/testctypes-win.obj +/tests/old_suite/basic/ctypes/testctypes-win.lib + +tests/old_suite/basic/test_pyz_as_external_file.exe.pkg + +# /tests/old_suite/libraries/ +/tests/old_suite/libraries/tinysample.png + +/old/e2etests/common/hanoi[1-9] + +# /doc/ +/doc/source/_definitions.txt +/doc/_pyi*-options.tmp +/doc/man/_pyi*.tmp +/doc/_build + +# Bootloader source +/bootloader/.lock-waf* +/bootloader/.lock-wscript +/bootloader/build/ +/bootloader/_sdks +/bootloader/.vagrant/ +/bootloader/waf-*/ +/bootloader/waf3-*/ +/bootloader/.waf-*/ +/bootloader/.waf3-*/ + +# Mac OS X +.DS_store + +# Windows debug symbols +*.pdb + +# py.test cache folder +.cache +*.prof + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# VSCode projects +*.code-workspace +*.vscode + +# Coverage +.coverage +htmlcov \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/COPYING.txt b/3rdparty/pyinstaller-4.3/COPYING.txt new file mode 100644 index 0000000000000000000000000000000000000000..1bece4eda317b5a42c4443ab28d36ba9fed6c1d8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/COPYING.txt @@ -0,0 +1,602 @@ +================================ + The PyInstaller licensing terms +================================ + + +Copyright (c) 2010-2021, PyInstaller Development Team +Copyright (c) 2005-2009, Giovanni Bajo +Based on previous work under copyright (c) 2002 McMillan Enterprises, Inc. + + +PyInstaller is licensed under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + + +Bootloader Exception +-------------------- + +In addition to the permissions in the GNU General Public License, the +authors give you unlimited permission to link or embed compiled bootloader +and related files into combinations with other programs, and to distribute +those combinations without any restriction coming from the use of those +files. (The General Public License restrictions do apply in other respects; +for example, they cover modification of the files, and distribution when +not linked into a combined executable.) + + +Bootloader and Related Files +---------------------------- + +Bootloader and related files are files which are embedded within the +final executable. This includes files in directories: + +./bootloader/ +./PyInstaller/loader + + +Run-time Hooks +---------------------------- + +Run-time Hooks are a different kind of files embedded within the final +executable. To ease moving them into a separate repository, or into the +respective project, these file are now licensed under the Apache License, +Version 2.0. + +Run-time Hooks are in the directory +./PyInstaller/hooks/rthooks + + +About the PyInstaller Development Team +-------------------------------------- + +The PyInstaller Development Team is the set of contributors +to the PyInstaller project. A full list with details is kept +in the documentation directory, in the file +``doc/CREDITS.rst``. + +The core team that coordinates development on GitHub can be found here: +https://github.com/pyinstaller/pyinstaller. As of 2020, it consists of: + +* Hartmut Goebel +* Legorooj +* Bryan Jones +* Brenainn Woodsend + + +Our Copyright Policy +-------------------- + +PyInstaller uses a shared copyright model. Each contributor maintains copyright +over their contributions to PyInstaller. But, it is important to note that these +contributions are typically only changes to the repositories. Thus, +the PyInstaller source code, in its entirety is not the copyright of any single +person or institution. Instead, it is the collective copyright of the entire +PyInstaller Development Team. If individual contributors want to maintain +a record of what changes/contributions they have specific copyright on, they +should indicate their copyright in the commit message of the change, when they +commit the change to the PyInstaller repository. + +With this in mind, the following banner should be used in any source code file +to indicate the copyright and license terms: + + +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +For run-time hooks, the following banner should be used: + +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + + +================================ +GNU General Public License +================================ + +https://gnu.org/licenses/gpl-2.0.html + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + +================================ +Apache License 2.0 +================================ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/3rdparty/pyinstaller-4.3/MANIFEST.in b/3rdparty/pyinstaller-4.3/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..a42cd5ecc8b262ee3a672ec34c39074f3d17e059 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/MANIFEST.in @@ -0,0 +1,33 @@ +graft bootloader +graft doc +graft PyInstaller +graft tests + +include *.rst +include *.txt +include *.toml +include pyinstaller.py +include pyinstaller-gui.py + +prune bootloader/build +prune bootloader/.waf-* +prune bootloader/.waf3-* +prune bootloader/waf-* +prune bootloader/waf3-* +prune bootloader/_sdks +prune bootloader/.vagrant +exclude bootloader/.lock-waf* + +prune doc/source +prune doc/_build +recursive-exclude doc *.tmp +include news/_template.rst +prune news + +prune old +prune scripts +prune tests/scripts +prune .github + +exclude .* *.yml *~ .directory +global-exclude *.py[co] diff --git a/3rdparty/pyinstaller-4.3/PKG-INFO b/3rdparty/pyinstaller-4.3/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..b487988815a129de39d081ac21c92e020041ef04 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PKG-INFO @@ -0,0 +1,213 @@ +Metadata-Version: 2.1 +Name: pyinstaller +Version: 4.3 +Summary: PyInstaller bundles a Python application and all its dependencies into a single package. +Home-page: http://www.pyinstaller.org/ +Author: Hartmut Goebel, Giovanni Bajo, David Vierra, David Cortesi, Martin Zibricky +License: GPLv2-or-later with a special exception which allows to use PyInstaller to build and distribute non-free programs (including commercial ones) +Description: PyInstaller Overview + ==================== + + PyInstaller bundles a Python application and all its dependencies into a single + package. The user can run the packaged app without installing a Python + interpreter or any modules. + + + **Help keeping PyInstaller alive:** + Maintaining PyInstaller is a huge amount of work. + PyInstaller development can only continue + if users and companies provide sustainable funding. See + http://www.pyinstaller.org/funding.html for how to support PyInstaller. + + + :Documentation: https://pyinstaller.readthedocs.io/ + :Website: http://www.pyinstaller.org/ + :Code: https://github.com/pyinstaller/pyinstaller + :Donate, Fund: http://www.pyinstaller.org/funding.html + + + PyInstaller reads a Python script written by you. It analyzes your code + to discover every other module and library your script needs in order to + execute. Then it collects copies of all those files -- including the active + Python interpreter! -- and puts them with your script in a single folder, or + optionally in a single executable file. + + + PyInstaller is tested against Windows, Mac OS X, and GNU/Linux. + However, it is not a cross-compiler: + to make a Windows app you run PyInstaller in Windows; to make + a GNU/Linux app you run it in GNU/Linux, etc. + PyInstaller has been used successfully + with AIX, Solaris, FreeBSD and OpenBSD, + but is not tested against them as part of the continuous integration tests. + + + Main Advantages + --------------- + + - Works out-of-the-box with any Python version 3.6-3.9. + - Fully multi-platform, and uses the OS support to load the dynamic libraries, + thus ensuring full compatibility. + - Correctly bundles the major Python packages such as numpy, PyQt5, + PySide2, Django, wxPython, matplotlib and others out-of-the-box. + - Compatible with many 3rd-party packages out-of-the-box. (All the required + tricks to make external packages work are already integrated.) + - Libraries like PyQt5, PySide2, wxPython, matplotlib or Django are fully + supported, without having to handle plugins or external data files manually. + - Works with code signing on OS X. + - Bundles MS Visual C++ DLLs on Windows. + + + Installation + ------------ + + PyInstaller is available on PyPI. You can install it through `pip`:: + + pip install pyinstaller + + + Requirements and Tested Platforms + ------------------------------------ + + - Python: + + - 3.6-3.9 + - tinyaes_ 1.0+ (only if using bytecode encryption). + Instead of installing tinyaes, ``pip install pyinstaller[encryption]`` instead. + + - Windows (32bit/64bit): + + - PyInstaller should work on Windows 7 or newer, but we only officially support Windows 8+. + + - We don't support Python installed from the Windows store when not using virtual environments due to + `permission errors `_ + that can't easily be fixed. + + - GNU/Linux (32bit/64bit) + + - ldd: Console application to print the shared libraries required + by each program or shared library. This typically can be found in + the distribution-package `glibc` or `libc-bin`. + - objdump: Console application to display information from + object files. This typically can be found in the + distribution-package `binutils`. + - objcopy: Console application to copy and translate object files. + This typically can be found in the distribution-package `binutils`, + too. + + - Mac OS X (64bit): + + - Mac OS X 10.13 (High Sierra) or newer. + + + Usage + ----- + + Basic usage is very simple, just run it against your main script:: + + pyinstaller /path/to/yourscript.py + + For more details, see the `manual`_. + + + Untested Platforms + --------------------- + + The following platforms have been contributed and any feedback or + enhancements on these are welcome. + + - FreeBSD + + - ldd + + - Solaris + + - ldd + - objdump + + - AIX + + - AIX 6.1 or newer. PyInstaller will not work with statically + linked Python libraries. + - ldd + + - PowerPC GNU/Linux (Debian) + + + Before using any contributed platform, you need to build the PyInstaller + bootloader, as we do not ship binary packages. Download PyInstaller + source, and build the bootloader:: + + cd bootloader + python ./waf all + + Then install PyInstaller:: + + python setup.py install + + or simply use it directly from the source (pyinstaller.py). + + + Support + --------------------- + + See http://www.pyinstaller.org/support.html for how to find help as well as + for commercial support. + + + Funding + --------------------- + + Maintaining PyInstaller is a huge amount of work. + PyInstaller development can only continue + if users and companies provide sustainable funding. See + http://www.pyinstaller.org/funding.html for how to support PyInstaller. + + + Changes in this Release + ------------------------- + + You can find a detailed list of changes in this release + in the `change log`_ section of the manual. + + + .. _tinyaes: https://github.com/naufraghi/tinyaes-py + .. _`manual`: https://pyinstaller.readthedocs.io/en/v4.3/ + .. _`change log`: https://pyinstaller.readthedocs.io/en/v4.3/CHANGES.html + +Keywords: packaging, app, apps, bundle, convert, standalone, executable,pyinstaller, cxfreeze, freeze, py2exe, py2app, bbfreeze +Platform: UNKNOWN +Classifier: Development Status :: 6 - Mature +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: Other Audience +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: GNU General Public License v2 (GPLv2) +Classifier: Natural Language :: English +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: POSIX +Classifier: Operating System :: POSIX :: AIX +Classifier: Operating System :: POSIX :: BSD +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: SunOS/Solaris +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Topic :: Software Development +Classifier: Topic :: Software Development :: Build Tools +Classifier: Topic :: Software Development :: Interpreters +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Topic :: System :: Installation/Setup +Classifier: Topic :: System :: Software Distribution +Classifier: Topic :: Utilities +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +Provides-Extra: hook_testing +Provides-Extra: encryption diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..bf09fa83673748bdc93e17184dcc67d121b3cd97 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/__init__.py @@ -0,0 +1,73 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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__') + +import os +import sys + +from . import compat +from .utils.git import get_repo_revision + + +# Note: Keep this variable as plain string so it could be updated automatically +# when doing a release. +__version__ = '4.3' + + +# Absolute path of this package's directory. Save this early so all +# submodules can use the absolute path. This is required e.g. if the +# current directory changes prior to loading the hooks. +PACKAGEPATH = os.path.abspath(os.path.dirname(__file__)) + +HOMEPATH = os.path.dirname(PACKAGEPATH) + + +# Update __version__ as necessary. +if os.path.exists(os.path.join(HOMEPATH, 'setup.py')): + # PyInstaller is run directly from source without installation or + # __version__ is called from 'setup.py' ... + if compat.getenv('PYINSTALLER_DO_RELEASE') == '1': + # Suppress the git revision when doing a release. + pass + elif 'sdist' not in sys.argv: + # and 'setup.py' was not called with 'sdist' argument. + # For creating source tarball we do not want git revision + # in the filename. + try: + __version__ += get_repo_revision() + except Exception: + # Write to stderr because stdout is used for eval() statement + # in some subprocesses. + sys.stderr.write('WARN: failed to parse git revision') +else: + # PyInstaller was installed by `python setup.py install'. + import pkg_resources + __version__ = pkg_resources.get_distribution('PyInstaller').version + + +## Default values of paths where to put files created by PyInstaller. +## Mind option-help in build_main when changes these +# Folder where to put created .spec file. +DEFAULT_SPECPATH = os.getcwd() +# Folder where to put created .spec file. +# Where to put the final app. +DEFAULT_DISTPATH = os.path.join(os.getcwd(), 'dist') +# Where to put all the temporary work files, .log, .pyz and 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' +if compat.machine: + PLATFORM += '-' + compat.machine diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/__main__.py b/3rdparty/pyinstaller-4.3/PyInstaller/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..009cc4a6609228f3d26f4387f50f7dd71a892393 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/__main__.py @@ -0,0 +1,124 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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. +""" + +import os +import argparse +import platform + + +from . import __version__ +from . import log as logging + +# note: don't import anything else until this function is run! +from .compat import check_requirements, is_conda + +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(_SmartFormatter, self)._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.') + +def run(pyi_args=None, pyi_config=None): + """ + pyi_args allows running PyInstaller programatically without a subprocess + pyi_config allows checking configuration once when running multiple tests + """ + check_requirements() + + import PyInstaller.building.makespec + import PyInstaller.building.build_main + import PyInstaller.log + + try: + parser = argparse.ArgumentParser(formatter_class=_SmartFormatter) + __add_options(parser) + PyInstaller.building.makespec.__add_options(parser) + PyInstaller.building.build_main.__add_options(parser) + PyInstaller.log.__add_options(parser) + 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.")) + + args = parser.parse_args(pyi_args) + PyInstaller.log.__process_options(parser, args) + + # Print PyInstaller version, Python version and platform + # as the first line to stdout. + # This helps identify PyInstaller, Python and platform version + # when users report issues. + logger.info('PyInstaller: %s' % __version__) + logger.info('Python: %s%s', platform.python_version(), + " (conda)" if is_conda else "") + logger.info('Platform: %s' % platform.platform()) + + # Skip creating .spec when .spec file is supplied + if args.filenames[0].endswith('.spec'): + spec_file = args.filenames[0] + else: + spec_file = run_makespec(**vars(args)) + + run_build(pyi_config, spec_file, **vars(args)) + + except KeyboardInterrupt: + raise SystemExit("Aborted by user request.") + except RecursionError: + from . import _recursion_to_deep_message + _recursion_to_deep_message.raise_with_msg() + + +if __name__ == '__main__': + run() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/_recursion_to_deep_message.py b/3rdparty/pyinstaller-4.3/PyInstaller/_recursion_to_deep_message.py new file mode 100644 index 0000000000000000000000000000000000000000..37aba6491e011a64a05f7f2269c96a0fb92021ef --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/_recursion_to_deep_message.py @@ -0,0 +1,46 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 has 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) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/archive/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/archive/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e392dedb3fb954745f899ae6d74ff6efea644228 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/archive/__init__.py @@ -0,0 +1 @@ +__author__ = 'martin' diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/archive/pyz_crypto.py b/3rdparty/pyinstaller-4.3/PyInstaller/archive/pyz_crypto.py new file mode 100644 index 0000000000000000000000000000000000000000..c08904605f2082bf5675c758c56559a84b3df03c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/archive/pyz_crypto.py @@ -0,0 +1,39 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +BLOCK_SIZE = 16 + + +class PyiBlockCipher(object): + """ + This class is used only to encrypt Python modules. + """ + def __init__(self, key=None): + assert type(key) is str + if len(key) > BLOCK_SIZE: + self.key = key[0:BLOCK_SIZE] + else: + self.key = key.zfill(BLOCK_SIZE) + assert len(self.key) == BLOCK_SIZE + + import tinyaes + self._aesmod = tinyaes + + def encrypt(self, data): + iv = os.urandom(BLOCK_SIZE) + return iv + self.__create_cipher(iv).CTR_xcrypt_buffer(data) + + def __create_cipher(self, iv): + # The 'AES' class is stateful, this factory method is used to + # re-initialize the block cipher class with each call to xcrypt(). + return self._aesmod.AES(self.key.encode(), iv) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/archive/readers.py b/3rdparty/pyinstaller-4.3/PyInstaller/archive/readers.py new file mode 100644 index 0000000000000000000000000000000000000000..7a03de6f7b3965a3fc8b5379dbe78fddf638f042 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/archive/readers.py @@ -0,0 +1,257 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 CArchiveReader is used only by the archieve_viewer utility. +""" + +# TODO clean up this module + +import struct +import os + + +from PyInstaller.loader.pyimod02_archive import ArchiveReader + + +class NotAnArchiveError(Exception): + pass + + +class CTOCReader(object): + """ + A class encapsulating the table of contents of a CArchive. + + When written to disk, it is easily read from C. + """ + # (structlen, dpos, dlen, ulen, flag, typcd) followed by name + ENTRYSTRUCT = '!iIIIBB' + ENTRYLEN = struct.calcsize(ENTRYSTRUCT) + + def __init__(self): + self.data = [] + + def frombinary(self, s): + """ + Decode the binary string into an in memory list. + + S is a binary string. + """ + p = 0 + + while p < len(s): + (slen, dpos, dlen, ulen, flag, typcd) = struct.unpack(self.ENTRYSTRUCT, + s[p:p + self.ENTRYLEN]) + nmlen = slen - self.ENTRYLEN + p = p + self.ENTRYLEN + (nm,) = struct.unpack('%is' % nmlen, s[p:p + nmlen]) + p = p + nmlen + # nm may have up to 15 bytes of padding + nm = nm.rstrip(b'\0') + nm = nm.decode('utf-8') + typcd = chr(typcd) + self.data.append((dpos, dlen, ulen, flag, typcd, nm)) + + + def get(self, ndx): + """ + Return the table of contents entry (tuple) at index NDX. + """ + return self.data[ndx] + + def __getitem__(self, ndx): + return self.data[ndx] + + def find(self, name): + """ + Return the index of the toc entry with name NAME. + + Return -1 for failure. + """ + for i, nm in enumerate(self.data): + if nm[-1] == name: + return i + return -1 + + +class CArchiveReader(ArchiveReader): + """ + An Archive subclass that can hold arbitrary data. + + This class encapsulates all files that are bundled within an executable. + It can contain ZlibArchive (Python .pyc files), dlls, Python C extensions + and all other data files that are bundled in --onefile mode. + + Easily handled from C or from Python. + """ + # MAGIC is useful to verify that conversion of Python data types + # to C structure and back works properly. + MAGIC = b'MEI\014\013\012\013\016' + HDRLEN = 0 + LEVEL = 9 + + # Cookie - holds some information for the bootloader. C struct format + # definition. '!' at the beginning means network byte order. + # C struct looks like: + # + # typedef struct _cookie { + # char magic[8]; /* 'MEI\014\013\012\013\016' */ + # uint32_t len; /* len of entire package */ + # uint32_t TOC; /* pos (rel to start) of TableOfContents */ + # int TOClen; /* length of TableOfContents */ + # int pyvers; /* new in v4 */ + # char pylibname[64]; /* Filename of Python dynamic library. */ + # } COOKIE; + # + _cookie_format = '!8sIIii64s' + _cookie_size = struct.calcsize(_cookie_format) + + def __init__(self, archive_path=None, start=0, length=0, pylib_name=''): + """ + Constructor. + + archive_path path name of file (create empty CArchive if path is None). + start is the seekposition within PATH. + len is the length of the CArchive (if 0, then read till EOF). + pylib_name name of Python DLL which bootloader will use. + """ + self.length = length + self._pylib_name = pylib_name + + + # A CArchive created from scratch starts at 0, no leading bootloader. + self.pkg_start = 0 + super(CArchiveReader, self).__init__(archive_path, start) + + def checkmagic(self): + """ + Verify that self is a valid CArchive. + + Magic signature is at end of the archive. + + This fuction is used by ArchiveViewer.py utility. + """ + # Magic is at EOF; if we're embedded, we need to figure where that is. + if self.length: + self.lib.seek(self.start + self.length, 0) + else: + self.lib.seek(0, os.SEEK_END) + end_pos = self.lib.tell() + + SEARCH_CHUNK_SIZE = 8192 + magic_offset = -1 + while end_pos >= len(self.MAGIC): + 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(self.MAGIC): + break + # Read and scan the chunk + self.lib.seek(start_pos, os.SEEK_SET) + buf = self.lib.read(chunk_size) + pos = buf.rfind(self.MAGIC) + if pos != -1: + magic_offset = start_pos + pos + break + # Adjust search location for next chunk; ensure proper + # overlap + end_pos = start_pos + len(self.MAGIC) - 1 + if magic_offset == -1: + raise RuntimeError("%s is not a valid %s archive file" % + (self.path, self.__class__.__name__)) + filelen = magic_offset + self._cookie_size + # Read the whole cookie + self.lib.seek(magic_offset, os.SEEK_SET) + buf = self.lib.read(self._cookie_size) + (magic, totallen, tocpos, toclen, pyvers, pylib_name) = struct.unpack( + self._cookie_format, buf) + if magic != self.MAGIC: + raise RuntimeError("%s is not a valid %s archive file" % + (self.path, self.__class__.__name__)) + + self.pkg_start = filelen - totallen + if self.length: + if totallen != self.length or self.pkg_start != self.start: + raise RuntimeError('Problem with embedded archive in %s' % + self.path) + # Verify presence of Python library name. + if not pylib_name: + raise RuntimeError('Python library filename not defined in archive.') + self.tocpos, self.toclen = tocpos, toclen + + def loadtoc(self): + """ + Load the table of contents into memory. + """ + self.toc = CTOCReader() + self.lib.seek(self.pkg_start + self.tocpos) + tocstr = self.lib.read(self.toclen) + self.toc.frombinary(tocstr) + + def extract(self, name): + """ + Get the contents of an entry. + + NAME is an entry name OR the index to the TOC. + + Return the tuple (ispkg, contents). + For non-Python resoures, ispkg is meaningless (and 0). + Used by the import mechanism. + """ + if isinstance(name, str): + ndx = self.toc.find(name) + if ndx == -1: + return None + else: + ndx = name + (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx) + + with self.lib: + self.lib.seek(self.pkg_start + dpos) + rslt = self.lib.read(dlen) + + if flag == 1: + import zlib + rslt = zlib.decompress(rslt) + if typcd == 'M': + return (1, rslt) + + return (typcd == 'M', rslt) + + def contents(self): + """ + Return the names of the entries. + """ + rslt = [] + for (dpos, dlen, ulen, flag, typcd, nm) in self.toc: + rslt.append(nm) + return rslt + + def openEmbedded(self, name): + """ + Open a CArchive of name NAME embedded within this CArchive. + + This fuction is used by ArchiveViewer.py utility. + """ + ndx = self.toc.find(name) + + if ndx == -1: + raise KeyError("Member '%s' not found in %s" % (name, self.path)) + (dpos, dlen, ulen, flag, typcd, nm) = self.toc.get(ndx) + + if typcd not in "zZ": + raise NotAnArchiveError('%s is not an archive' % name) + + if flag: + raise ValueError('Cannot open compressed archive %s in place' % + name) + return CArchiveReader(self.path, self.pkg_start + dpos, dlen) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/archive/writers.py b/3rdparty/pyinstaller-4.3/PyInstaller/archive/writers.py new file mode 100644 index 0000000000000000000000000000000000000000..3b88530790864b3d79d7d88aff9bf8d8997c9ec0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/archive/writers.py @@ -0,0 +1,488 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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. +""" + +# While an Archive is really an abstraction for any "filesystem +# within a file", it is tuned for use with imputil.FuncImporter. +# This assumes it contains python code objects, indexed by the +# the internal name (ie, no '.py'). +# +# See pyi_carchive.py for a more general archive (contains anything) +# that can be understood by a C program. + +import os +import sys +import struct +from types import CodeType +import marshal +import zlib +import io + +from PyInstaller.building.utils import get_code_object, strip_paths_in_code,\ + fake_pyc_timestamp +from PyInstaller.loader.pyimod02_archive import PYZ_TYPE_MODULE, PYZ_TYPE_PKG, \ + PYZ_TYPE_DATA, PYZ_TYPE_NSPKG +from ..compat import BYTECODE_MAGIC, is_py37, is_win + + +class ArchiveWriter(object): + """ + A base class for a repository of python code objects. + The extract method is used by imputil.ArchiveImporter + to get code objects by name (fully qualified name), so + an enduser "import a.b" would become + extract('a.__init__') + extract('a.b') + """ + MAGIC = b'PYL\0' + HDRLEN = 12 # default is MAGIC followed by python's magic, int pos of toc + TOCPOS = 8 + + def __init__(self, archive_path, logical_toc): + """ + Create an archive file of name 'archive_path'. + logical_toc is a 'logical TOC' - a list of (name, path, ...) + where name is the internal name, eg 'a' + and path is a file to get the object from, eg './a.pyc'. + """ + self.start = 0 + + self._start_add_entries(archive_path) + self._add_from_table_of_contents(logical_toc) + self._finalize() + + def _start_add_entries(self, archive_path): + """ + Open an empty archive for addition of entries. + """ + self.lib = open(archive_path, 'wb') + # Reserve space for the header. + if self.HDRLEN: + self.lib.write(b'\0' * self.HDRLEN) + # Create an empty table of contents. + # Use a list to support reproducible builds + self.toc = [] + + def _add_from_table_of_contents(self, toc): + """ + Add entries from a logical TOC (without absolute positioning info). + An entry is an entry in a logical TOC is a tuple, + entry[0] is name (under which it will be saved). + entry[1] is fullpathname of the file. + entry[2] is a flag for it's storage format (True or 1 if compressed) + entry[3] is the entry's type code. + """ + for toc_entry in toc: + self.add(toc_entry) # The guts of the archive. + + def _finalize(self): + """ + Finalize an archive which has been opened using _start_add_entries(), + writing any needed padding and the table of contents. + """ + toc_pos = self.lib.tell() + self.save_trailer(toc_pos) + if self.HDRLEN: + self.update_headers(toc_pos) + self.lib.close() + + + ####### manages keeping the internal TOC and the guts in sync ####### + def add(self, entry): + """ + Override this to influence the mechanics of the Archive. + Assumes entry is a seq beginning with (nm, pth, ...) where + nm is the key by which we'll be asked for the object. + pth is the name of where we find the object. Overrides of + get_obj_from can make use of further elements in entry. + """ + nm = entry[0] + pth = entry[1] + pynm, ext = os.path.splitext(os.path.basename(pth)) + ispkg = pynm == '__init__' + assert ext in ('.pyc', '.pyo') + self.toc.append((nm, (ispkg, self.lib.tell()))) + with open(entry[1], 'rb') as f: + f.seek(8) # skip magic and timestamp + self.lib.write(f.read()) + + def save_trailer(self, tocpos): + """ + Default - toc is a dict + Gets marshaled to self.lib + """ + try: + self.lib.write(marshal.dumps(self.toc)) + # If the TOC to be marshalled contains an unmarshallable object, Python + # raises a cryptic exception providing no details on why such object is + # unmarshallable. Correct this by iteratively inspecting the TOC for + # unmarshallable objects. + except ValueError as exception: + if str(exception) == 'unmarshallable object': + + # List of all marshallable types. + MARSHALLABLE_TYPES = { + bool, int, float, complex, str, bytes, bytearray, tuple, + list, set, frozenset, dict, CodeType + } + + for module_name, module_tuple in self.toc.items(): + if type(module_name) not in MARSHALLABLE_TYPES: + print('Module name "%s" (%s) unmarshallable.' % (module_name, type(module_name))) + if type(module_tuple) not in MARSHALLABLE_TYPES: + print('Module "%s" tuple "%s" (%s) unmarshallable.' % (module_name, module_tuple, type(module_tuple))) + elif type(module_tuple) == tuple: + for i in range(len(module_tuple)): + if type(module_tuple[i]) not in MARSHALLABLE_TYPES: + print('Module "%s" tuple index %s item "%s" (%s) unmarshallable.' % (module_name, i, module_tuple[i], type(module_tuple[i]))) + + raise + + def update_headers(self, tocpos): + """ + Default - MAGIC + Python's magic + tocpos + """ + self.lib.seek(self.start) + self.lib.write(self.MAGIC) + self.lib.write(BYTECODE_MAGIC) + self.lib.write(struct.pack('!i', tocpos)) + + +class ZlibArchiveWriter(ArchiveWriter): + """ + ZlibArchive - an archive with compressed entries. Archive is read + from the executable created by PyInstaller. + + This archive is used for bundling python modules inside the executable. + + NOTE: The whole ZlibArchive (PYZ) is compressed so it is not necessary + to compress single modules with zlib. + """ + MAGIC = b'PYZ\0' + TOCPOS = 8 + HDRLEN = ArchiveWriter.HDRLEN + 5 + COMPRESSION_LEVEL = 6 # Default level of the 'zlib' module from Python. + + def __init__(self, archive_path, logical_toc, code_dict=None, cipher=None): + """ + code_dict dict containing module code objects from ModuleGraph. + """ + # Keep references to module code objects constructed by ModuleGraph + # to avoid writting .pyc/pyo files to hdd. + self.code_dict = code_dict or {} + self.cipher = cipher or None + + super(ZlibArchiveWriter, self).__init__(archive_path, logical_toc) + + + def add(self, entry): + name, path, typ = entry + if typ == 'PYMODULE': + typ = PYZ_TYPE_MODULE + if path in ('-', None): + # This is a NamespacePackage, modulegraph marks them + # by using the filename '-'. (But wants to use None, + # so check for None, too, to be forward-compatible.) + typ = PYZ_TYPE_NSPKG + else: + base, ext = os.path.splitext(os.path.basename(path)) + if base == '__init__': + typ = PYZ_TYPE_PKG + data = marshal.dumps(self.code_dict[name]) + else: + # Any data files, that might be required by pkg_resources. + typ = PYZ_TYPE_DATA + with open(path, 'rb') as fh: + data = fh.read() + # No need to use forward slash as path-separator here since + # pkg_resources on Windows back slash as path-separator. + + obj = zlib.compress(data, self.COMPRESSION_LEVEL) + + # First compress then encrypt. + if self.cipher: + obj = self.cipher.encrypt(obj) + + self.toc.append((name, (typ, self.lib.tell(), len(obj)))) + self.lib.write(obj) + + def update_headers(self, tocpos): + """ + add level + """ + ArchiveWriter.update_headers(self, tocpos) + self.lib.write(struct.pack('!B', self.cipher is not None)) + + + +class CTOC(object): + """ + A class encapsulating the table of contents of a CArchive. + + When written to disk, it is easily read from C. + """ + # (structlen, dpos, dlen, ulen, flag, typcd) followed by name + ENTRYSTRUCT = '!iIIIBB' + ENTRYLEN = struct.calcsize(ENTRYSTRUCT) + + def __init__(self): + self.data = [] + + def tobinary(self): + """ + Return self as a binary string. + """ + rslt = [] + for (dpos, dlen, ulen, flag, typcd, nm) in self.data: + # Encode all names using UTF-8. This should be save 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. + nm = nm.encode('utf-8') + nmlen = len(nm) + 1 # add 1 for a '\0' + # align to 16 byte boundary so xplatform C can read + toclen = nmlen + self.ENTRYLEN + if toclen % 16 == 0: + pad = b'\0' + else: + padlen = 16 - (toclen % 16) + pad = b'\0' * padlen + nmlen = nmlen + padlen + rslt.append(struct.pack(self.ENTRYSTRUCT + '%is' % nmlen, + nmlen + self.ENTRYLEN, dpos, dlen, ulen, + flag, ord(typcd), nm + pad)) + + return b''.join(rslt) + + def add(self, dpos, dlen, ulen, flag, typcd, nm): + """ + Add an entry to the table of contents. + + DPOS is data position. + DLEN is data length. + ULEN is the uncompressed data len. + FLAG says if the data is compressed. + TYPCD is the "type" of the entry (used by the C code) + NM is the entry's name. + + This function is used only while creating an executable. + """ + # Ensure forward slashes in paths are on Windows converted to back + # slashes '\\' since on Windows the bootloader works only with back + # slashes. + nm = os.path.normpath(nm) + if is_win and os.path.sep == '/': + # When building under MSYS, the above path normalization + # uses Unix-style separators, so replace them manually. + nm = nm.replace(os.path.sep, '\\') + self.data.append((dpos, dlen, ulen, flag, typcd, nm)) + + +class CArchiveWriter(ArchiveWriter): + """ + An Archive subclass that can hold arbitrary data. + + This class encapsulates all files that are bundled within an executable. + It can contain ZlibArchive (Python .pyc files), dlls, Python C extensions + and all other data files that are bundled in --onefile mode. + + Easily handled from C or from Python. + """ + # MAGIC is usefull to verify that conversion of Python data types + # to C structure and back works properly. + MAGIC = b'MEI\014\013\012\013\016' + HDRLEN = 0 + LEVEL = 9 + + # Cookie - holds some information for the bootloader. C struct format + # definition. '!' at the beginning means network byte order. + # C struct looks like: + # + # typedef struct _cookie { + # char magic[8]; /* 'MEI\014\013\012\013\016' */ + # uint32_t len; /* len of entire package */ + # uint32_t TOC; /* pos (rel to start) of TableOfContents */ + # int TOClen; /* length of TableOfContents */ + # int pyvers; /* new in v4 */ + # char pylibname[64]; /* Filename of Python dynamic library. */ + # } COOKIE; + # + _cookie_format = '!8sIIii64s' + _cookie_size = struct.calcsize(_cookie_format) + + def __init__(self, archive_path, logical_toc, pylib_name): + """ + Constructor. + + archive_path path name of file (create empty CArchive if path is None). + start is the seekposition within PATH. + len is the length of the CArchive (if 0, then read till EOF). + pylib_name name of Python DLL which bootloader will use. + """ + self._pylib_name = pylib_name + + # A CArchive created from scratch starts at 0, no leading bootloader. + super(CArchiveWriter, self).__init__(archive_path, logical_toc) + + def _start_add_entries(self, path): + """ + Open an empty archive for addition of entries. + """ + super(CArchiveWriter, self)._start_add_entries(path) + # Override parents' toc {} with a class. + self.toc = CTOC() + + def add(self, entry): + """ + Add an ENTRY to the CArchive. + + ENTRY must have: + entry[0] is name (under which it will be saved). + entry[1] is fullpathname of the file. + entry[2] is a flag for it's storage format (0==uncompressed, + 1==compressed) + entry[3] is the entry's type code. + Version 5: + If the type code is 'o': + entry[0] is the runtime option + eg: v (meaning verbose imports) + u (meaning unbuffered) + W arg (warning option arg) + s (meaning do site.py processing. + """ + (nm, pathnm, flag, typcd) = entry[:4] + # FIXME Could we make the version 5 the default one? + # Version 5 - allow type 'o' = runtime option. + code_data = None + fh = None + try: + if typcd in ('o', 'd'): + ulen = 0 + flag = 0 + elif typcd == 's': + # If it's a source code file, compile it to a code object and marshall + # the object so it can be unmarshalled by the bootloader. + + code = get_code_object(nm, pathnm) + code = strip_paths_in_code(code) + + code_data = marshal.dumps(code) + ulen = len(code_data) + elif typcd == 'm': + fh = open(pathnm, 'rb') + ulen = os.fstat(fh.fileno()).st_size + # Check if it is a PYC file + header = fh.read(4) + fh.seek(0) + if header == BYTECODE_MAGIC: + # Read whole header and load code. + # According to PEP-552, in python versions prior to + # 3.7, the PYC header consists of three 32-bit words + # (magic, timestamp, and source file size). + # From python 3.7 on, the PYC header was extended to + # four 32-bit words (magic, flags, and, depending on + # the flags, either timestamp and source file size, + # or a 64-bit hash). + if is_py37: + header = fh.read(16) + else: + header = fh.read(12) + code = marshal.load(fh) + # Strip paths from code, marshal back into module form. + # The header fields (timestamp, size, hash, etc.) are + # all referring to the source file, so our modification + # of the code object does not affect them, and we can + # re-use the original header. + code = strip_paths_in_code(code) + data = header + marshal.dumps(code) + # Create file-like object for timestamp re-write + # in the subsequent steps + fh = io.BytesIO(data) + ulen = len(data) + else: + fh = open(pathnm, 'rb') + ulen = os.fstat(fh.fileno()).st_size + except IOError: + print("Cannot find ('%s', '%s', %s, '%s')" % (nm, pathnm, flag, typcd)) + raise + + where = self.lib.tell() + assert flag in range(3) + if not fh and not code_data: + # no need to write anything + pass + elif flag == 1: + comprobj = zlib.compressobj(self.LEVEL) + if code_data is not None: + self.lib.write(comprobj.compress(code_data)) + else: + assert fh + # We only want to change it for pyc files + modify_header = typcd in ('M', 'm', 's') + while 1: + buf = fh.read(16*1024) + if not buf: + break + if modify_header: + modify_header = False + buf = fake_pyc_timestamp(buf) + self.lib.write(comprobj.compress(buf)) + self.lib.write(comprobj.flush()) + + else: + if code_data is not None: + self.lib.write(code_data) + else: + assert fh + while 1: + buf = fh.read(16*1024) + if not buf: + break + self.lib.write(buf) + + dlen = self.lib.tell() - where + if typcd == 'm': + if pathnm.find('.__init__.py') > -1: + typcd = 'M' + + if fh: + fh.close() + + # Record the entry in the CTOC + self.toc.add(where, dlen, ulen, flag, typcd, nm) + + + def save_trailer(self, tocpos): + """ + Save the table of contents and the cookie for the bootlader to + disk. + + CArchives can be opened from the end - the cookie points + back to the start. + """ + tocstr = self.toc.tobinary() + self.lib.write(tocstr) + toclen = len(tocstr) + + # now save teh cookie + total_len = tocpos + toclen + self._cookie_size + pyvers = sys.version_info[0] * 10 + sys.version_info[1] + # Before saving cookie we need to convert it to corresponding + # C representation. + cookie = struct.pack(self._cookie_format, self.MAGIC, total_len, + tocpos, toclen, pyvers, + self._pylib_name.encode('ascii')) + self.lib.write(cookie) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/run b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/run new file mode 100644 index 0000000000000000000000000000000000000000..73da5dcc662dbdcb044cb2d692eab70869781e68 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/run differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/run_d b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/run_d new file mode 100644 index 0000000000000000000000000000000000000000..983047f06aad65be3529b7804e5fdccc61d07c25 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/run_d differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/runw b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/runw new file mode 100644 index 0000000000000000000000000000000000000000..e53290c04ffa82b4b92cfedf2965a73dc721f586 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/runw differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/runw_d b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/runw_d new file mode 100644 index 0000000000000000000000000000000000000000..3ca9a93fc6b0aba05230793be08f0ea52244ce68 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Darwin-64bit/runw_d differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-32bit/run b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-32bit/run new file mode 100644 index 0000000000000000000000000000000000000000..eb408998736129fed065940a2de1a5b8fc8c8b23 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-32bit/run differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-32bit/run_d b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-32bit/run_d new file mode 100644 index 0000000000000000000000000000000000000000..01ae840c31e111ff34395518fb6f63a35f4d33d2 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-32bit/run_d differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-64bit/run b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-64bit/run new file mode 100644 index 0000000000000000000000000000000000000000..ccbe2088106cac3ac39bccf3618b7c284b14133e Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-64bit/run differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-64bit/run_d b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-64bit/run_d new file mode 100644 index 0000000000000000000000000000000000000000..ab168041401d8d1c7484fb5e28b62bfa2e4d3194 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Linux-64bit/run_d differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/run.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/run.exe new file mode 100755 index 0000000000000000000000000000000000000000..d420de123f526483f646619734432ffa80afb4e7 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/run.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/run_d.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/run_d.exe new file mode 100755 index 0000000000000000000000000000000000000000..a8357196cd3416f005a682c51df804f8e29a76a5 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/run_d.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/runw.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/runw.exe new file mode 100755 index 0000000000000000000000000000000000000000..8ca3b26791cb2b976e1d3af640bfc3fa17c8bedb Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/runw.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/runw_d.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/runw_d.exe new file mode 100755 index 0000000000000000000000000000000000000000..ce92b1037777c0ea0c29c8b258ef075e5cd6e4e4 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-32bit/runw_d.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/run.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/run.exe new file mode 100755 index 0000000000000000000000000000000000000000..1c7376fe545a77a1c63fd94073eefe26f4549f50 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/run.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/run_d.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/run_d.exe new file mode 100755 index 0000000000000000000000000000000000000000..f53b173d5cc2466081cadaddbdc9e7c64c23f924 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/run_d.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/runw.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/runw.exe new file mode 100755 index 0000000000000000000000000000000000000000..6f013a9f4b9ec35bb60cdd952c70ed300cd55dd2 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/runw.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/runw_d.exe b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/runw_d.exe new file mode 100755 index 0000000000000000000000000000000000000000..1a704022089238344e66557db0160bdf449fe6c7 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/Windows-64bit/runw_d.exe differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/github_logo.png b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/github_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..778589e03ad9d984f2f974db3c7e46317a307eb1 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/github_logo.png differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.icns b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.icns new file mode 100644 index 0000000000000000000000000000000000000000..a9ecb747019168c18a1c20d30a32047cf420afbb Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.icns differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.ico b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.ico new file mode 100644 index 0000000000000000000000000000000000000000..4d63d0f0644b8bed4bc8adb8a76b841cc07948ec Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.ico differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.svg b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.svg new file mode 100644 index 0000000000000000000000000000000000000000..290484ef02ddade74a90af8de1f7cc1dd1c4b06e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-console.svg @@ -0,0 +1,4423 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.icns b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.icns new file mode 100644 index 0000000000000000000000000000000000000000..954a9a05faa57cfeb1f96db6778c3245252f3807 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.icns differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.ico b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.ico new file mode 100644 index 0000000000000000000000000000000000000000..cc9903d21ee9478d1ee15eaa8f9dc41fab7604d7 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.ico differ diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.svg b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.svg new file mode 100644 index 0000000000000000000000000000000000000000..c16e21d389d4d5e9cb54620a4c76f7cd40704c31 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/bootloader/images/icon-windowed.svg @@ -0,0 +1,4423 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/api.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/api.py new file mode 100644 index 0000000000000000000000000000000000000000..4d648ca106d6da8f9b7a30d0ff8ad0b595005a0e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/api.py @@ -0,0 +1,889 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 contains classes that are available for the .spec files. + +Spec file is generated by PyInstaller. The generated code from .spec file +is a way how PyInstaller does the dependency analysis and creates executable. +""" +import os +import shutil +import tempfile +import pprint +from operator import itemgetter + +from PyInstaller import HOMEPATH, PLATFORM +from PyInstaller.archive.writers import ZlibArchiveWriter, CArchiveWriter +from PyInstaller.building.utils import _check_guts_toc, add_suffix_to_extensions, \ + checkCache, strip_paths_in_code, get_code_object, \ + _make_clean_directory +from PyInstaller.compat import is_win, is_darwin, is_linux, is_cygwin, \ + exec_command_all, is_64bits +from PyInstaller.depend import bindepend +from PyInstaller.depend.analysis import get_bootstrap_modules +from PyInstaller.depend.utils import is_path_to_egg +from PyInstaller.building.datastruct import TOC, Target, _check_guts_eq +from PyInstaller.utils import misc +from .. import log as logging + +logger = logging.getLogger(__name__) + +if is_win: + from PyInstaller.utils.win32 import winmanifest, icon, versioninfo, winresource + + +class PYZ(Target): + """ + Creates a ZlibArchive that contains all pure Python modules. + """ + typ = 'PYZ' + + def __init__(self, *tocs, **kwargs): + """ + tocs + One or more TOCs (Tables of Contents), normally an + Analysis.pure. + + If this TOC has an attribute `_code_cache`, this is + expected to be a dict of module code objects from + ModuleGraph. + + kwargs + Possible keywork arguments: + + name + A filename for the .pyz. Normally not needed, as the generated + name will do fine. + cipher + The block cipher that will be used to encrypt Python bytecode. + + """ + + from ..config import CONF + Target.__init__(self) + name = kwargs.get('name', None) + cipher = kwargs.get('cipher', None) + self.toc = TOC() + # If available, use code objects directly from ModuleGraph to + # speed up PyInstaller. + self.code_dict = {} + for t in tocs: + self.toc.extend(t) + self.code_dict.update(getattr(t, '_code_cache', {})) + + self.name = name + if name is None: + self.name = os.path.splitext(self.tocfilename)[0] + '.pyz' + # PyInstaller bootstrapping modules. + self.dependencies = get_bootstrap_modules() + # Bundle the crypto key. + self.cipher = cipher + if cipher: + key_file = ('pyimod00_crypto_key', + os.path.join(CONF['workpath'], 'pyimod00_crypto_key.pyc'), + 'PYMODULE') + # Insert the key as the first module in the list. The key module contains + # just variables and does not depend on other modules. + self.dependencies.insert(0, key_file) + # Compile the top-level modules so that they end up in the CArchive and can be + # imported by the bootstrap script. + self.dependencies = misc.compile_py_files(self.dependencies, CONF['workpath']) + self.__postinit__() + + _GUTS = (# input parameters + ('name', _check_guts_eq), + ('toc', _check_guts_toc), # todo: pyc=1 + # no calculated/analysed values + ) + + def _check_guts(self, data, last_build): + if Target._check_guts(self, data, last_build): + return True + return False + + def assemble(self): + logger.info("Building PYZ (ZlibArchive) %s", self.name) + # Do not bundle PyInstaller bootstrap modules into PYZ archive. + toc = self.toc - self.dependencies + for entry in toc[:]: + if not entry[0] in self.code_dict and entry[2] == 'PYMODULE': + # For some reason the code-object, modulegraph created + # is not available. Recreate it + try: + self.code_dict[entry[0]] = get_code_object(entry[0], entry[1]) + except SyntaxError: + # Exclude the module in case this is code meant for a newer Python version. + toc.remove(entry) + # sort content alphabetically to support reproducible builds + toc.sort() + + # Remove leading parts of paths in code objects + self.code_dict = { + key: strip_paths_in_code(code) + for key, code in self.code_dict.items() + } + + pyz = ZlibArchiveWriter(self.name, toc, code_dict=self.code_dict, cipher=self.cipher) + logger.info("Building PYZ (ZlibArchive) %s completed successfully.", + self.name) + + +class PKG(Target): + """ + Creates a CArchive. CArchive is the data structure that is embedded + into the executable. This data structure allows to include various + read-only data in a sigle-file deployment. + """ + typ = 'PKG' + xformdict = {'PYMODULE': 'm', + 'PYSOURCE': 's', + 'EXTENSION': 'b', + 'PYZ': 'z', + 'PKG': 'a', + 'DATA': 'x', + 'BINARY': 'b', + 'ZIPFILE': 'Z', + 'EXECUTABLE': 'b', + 'DEPENDENCY': 'd'} + + def __init__(self, toc, name=None, cdict=None, exclude_binaries=0, + strip_binaries=False, upx_binaries=False, upx_exclude=None): + """ + toc + A TOC (Table of Contents) + name + An optional filename for the PKG. + cdict + Dictionary that specifies compression by typecode. For Example, + PYZ is left uncompressed so that it can be accessed inside the + PKG. The default uses sensible values. If zlib is not available, + no compression is used. + exclude_binaries + If True, EXTENSIONs and BINARYs will be left out of the PKG, + and forwarded to its container (usually a COLLECT). + strip_binaries + If True, use 'strip' command to reduce the size of binary files. + upx_binaries + """ + Target.__init__(self) + self.toc = toc + self.cdict = cdict + self.name = name + if name is None: + self.name = os.path.splitext(self.tocfilename)[0] + '.pkg' + self.exclude_binaries = exclude_binaries + self.strip_binaries = strip_binaries + self.upx_binaries = upx_binaries + self.upx_exclude = upx_exclude or [] + # This dict tells PyInstaller what items embedded in the executable should + # be compressed. + if self.cdict is None: + self.cdict = {'EXTENSION': COMPRESSED, + 'DATA': COMPRESSED, + 'BINARY': COMPRESSED, + 'EXECUTABLE': COMPRESSED, + 'PYSOURCE': COMPRESSED, + 'PYMODULE': COMPRESSED, + # Do not compress PYZ as a whole. Single modules are + # compressed when creating PYZ archive. + 'PYZ': UNCOMPRESSED} + self.__postinit__() + + _GUTS = (# input parameters + ('name', _check_guts_eq), + ('cdict', _check_guts_eq), + ('toc', _check_guts_toc), # list unchanged and no newer files + ('exclude_binaries', _check_guts_eq), + ('strip_binaries', _check_guts_eq), + ('upx_binaries', _check_guts_eq), + ('upx_exclude', _check_guts_eq) + # no calculated/analysed values + ) + + def _check_guts(self, data, last_build): + if Target._check_guts(self, data, last_build): + return True + return False + + def assemble(self): + logger.info("Building PKG (CArchive) %s", os.path.basename(self.name)) + trash = [] + mytoc = [] + srctoc = [] + seenInms = {} + seenFnms = {} + seenFnms_typ = {} + toc = add_suffix_to_extensions(self.toc) + # 'inm' - relative filename inside a CArchive + # 'fnm' - absolute filename as it is on the file system. + for inm, fnm, typ in toc: + # Ensure filename 'fnm' is not None or empty string. Otherwise + # it will fail in case of 'typ' being type OPTION. + if fnm and not os.path.isfile(fnm) and is_path_to_egg(fnm): + # file is contained within python egg, it is added with the egg + continue + if typ in ('BINARY', 'EXTENSION', 'DEPENDENCY'): + if self.exclude_binaries and typ == 'EXTENSION': + self.dependencies.append((inm, fnm, typ)) + elif not self.exclude_binaries or typ == 'DEPENDENCY': + if typ == 'BINARY': + # Avoid importing the same binary extension twice. This might + # happen if they come from different sources (eg. once from + # binary dependence, and once from direct import). + if inm in seenInms: + logger.warning('Two binaries added with the same internal name.') + logger.warning(pprint.pformat((inm, fnm, typ))) + logger.warning('was placed previously at') + logger.warning(pprint.pformat((inm, seenInms[inm], seenFnms_typ[seenInms[inm]]))) + logger.warning('Skipping %s.' % fnm) + continue + + # Warn if the same binary extension was included + # with multiple internal names + if fnm in seenFnms: + logger.warning('One binary added with two internal names.') + logger.warning(pprint.pformat((inm, fnm, typ))) + logger.warning('was placed previously at') + logger.warning(pprint.pformat((seenFnms[fnm], fnm, seenFnms_typ[fnm]))) + seenInms[inm] = fnm + seenFnms[fnm] = inm + seenFnms_typ[fnm] = typ + + fnm = checkCache(fnm, strip=self.strip_binaries, + upx=self.upx_binaries, + upx_exclude=self.upx_exclude, + dist_nm=inm) + + mytoc.append((inm, fnm, self.cdict.get(typ, 0), + self.xformdict.get(typ, 'b'))) + elif typ == 'OPTION': + mytoc.append((inm, '', 0, 'o')) + elif typ in ('PYSOURCE', 'PYMODULE'): + # collect sourcefiles and module in a toc of it's own + # which will not be sorted. + srctoc.append((inm, fnm, self.cdict[typ], self.xformdict[typ])) + else: + mytoc.append((inm, fnm, self.cdict.get(typ, 0), self.xformdict.get(typ, 'b'))) + + # Bootloader has to know the name of Python library. Pass python libname to CArchive. + pylib_name = os.path.basename(bindepend.get_python_library_path()) + + # Sort content alphabetically by type and name to support + # reproducible builds. + mytoc.sort(key=itemgetter(3, 0)) + # Do *not* sort modules and scripts, as their order is important. + # TODO: Think about having all modules first and then all scripts. + archive = CArchiveWriter(self.name, srctoc + mytoc, + pylib_name=pylib_name) + + for item in trash: + os.remove(item) + logger.info("Building PKG (CArchive) %s completed successfully.", + os.path.basename(self.name)) + + +class EXE(Target): + """ + Creates the final executable of the frozen app. + This bundles all necessary files together. + """ + typ = 'EXECUTABLE' + + def __init__(self, *args, **kwargs): + """ + args + One or more arguments that are either TOCs Targets. + kwargs + Possible keywork arguments: + + bootloader_ignore_signals + Non-Windows only. If True, the bootloader process will ignore + all ignorable signals. If False (default), it will forward + all signals to the child process. Useful in situations where + e.g. a supervisor process signals both the bootloader and + child (e.g. via a process group) to avoid signalling the + child twice. + console + On Windows or OSX governs whether to use the console executable + or the windowed executable. Always True on Linux/Unix (always + console executable - it does not matter there). + debug + Setting to True gives you progress mesages from the executable + (for console=False there will be annoying MessageBoxes on Windows). + name + The filename for the executable. On Windows suffix '.exe' is + appended. + exclude_binaries + Forwarded to the PKG the EXE builds. + icon + Windows or OSX only. icon='myicon.ico' to use an icon file or + icon='notepad.exe,0' to grab an icon resource. + Defaults to use PyInstaller's console or windowed icon. + icon=`NONE` to not add any icon. + version + Windows only. version='myversion.txt'. Use grab_version.py to get + a version resource from an executable and then edit the output to + create your own. (The syntax of version resources is so arcane + that I wouldn't attempt to write one from scratch). + uac_admin + Windows only. Setting to True creates a Manifest with will request + elevation upon application restart + uac_uiaccess + Windows only. Setting to True allows an elevated application to + work with Remote Desktop + """ + from ..config import CONF + Target.__init__(self) + + # Available options for EXE in .spec files. + self.exclude_binaries = kwargs.get('exclude_binaries', False) + self.bootloader_ignore_signals = kwargs.get( + 'bootloader_ignore_signals', False) + self.console = kwargs.get('console', True) + self.debug = kwargs.get('debug', False) + self.name = kwargs.get('name', None) + self.icon = kwargs.get('icon', None) + self.versrsrc = kwargs.get('version', None) + self.manifest = kwargs.get('manifest', None) + self.resources = kwargs.get('resources', []) + self.strip = kwargs.get('strip', False) + self.upx_exclude = kwargs.get("upx_exclude", []) + self.runtime_tmpdir = kwargs.get('runtime_tmpdir', None) + # If ``append_pkg`` is false, the archive will not be appended + # to the exe, but copied beside it. + self.append_pkg = kwargs.get('append_pkg', True) + + # On Windows allows the exe to request admin privileges. + self.uac_admin = kwargs.get('uac_admin', False) + self.uac_uiaccess = kwargs.get('uac_uiaccess', False) + + if CONF['hasUPX']: + self.upx = kwargs.get('upx', False) + else: + self.upx = False + + # Old .spec format included in 'name' the path where to put created + # app. New format includes only exename. + # + # Ignore fullpath in the 'name' and prepend DISTPATH or WORKPATH. + # DISTPATH - onefile + # WORKPATH - onedir + if self.exclude_binaries: + # onedir mode - create executable in WORKPATH. + self.name = os.path.join(CONF['workpath'], os.path.basename(self.name)) + else: + # onefile mode - create executable in DISTPATH. + self.name = os.path.join(CONF['distpath'], os.path.basename(self.name)) + + # Old .spec format included on Windows in 'name' .exe suffix. + if is_win or is_cygwin: + # Append .exe suffix if it is not already there. + if not self.name.endswith('.exe'): + self.name += '.exe' + base_name = os.path.splitext(os.path.basename(self.name))[0] + else: + base_name = os.path.basename(self.name) + self.pkgname = base_name + '.pkg' + + self.toc = TOC() + + for arg in args: + if isinstance(arg, TOC): + self.toc.extend(arg) + elif isinstance(arg, Target): + self.toc.append((os.path.basename(arg.name), arg.name, arg.typ)) + self.toc.extend(arg.dependencies) + else: + self.toc.extend(arg) + + if self.runtime_tmpdir is not None: + self.toc.append(("pyi-runtime-tmpdir " + self.runtime_tmpdir, "", "OPTION")) + + if self.bootloader_ignore_signals: + # no value; presence means "true" + self.toc.append(("pyi-bootloader-ignore-signals", "", "OPTION")) + + if is_win: + filename = os.path.join(CONF['workpath'], CONF['specnm'] + ".exe.manifest") + self.manifest = winmanifest.create_manifest(filename, self.manifest, + self.console, self.uac_admin, self.uac_uiaccess) + + manifest_filename = os.path.basename(self.name) + ".manifest" + + self.toc.append((manifest_filename, filename, 'BINARY')) + if not self.exclude_binaries: + # Onefile mode: manifest file is explicitly loaded. + # Store name of manifest file as bootloader option. Allows + # the exe to be renamed. + self.toc.append(("pyi-windows-manifest-filename " + manifest_filename, + "", "OPTION")) + + if self.versrsrc: + if (not isinstance(self.versrsrc, versioninfo.VSVersionInfo) + and not os.path.isabs(self.versrsrc)): + # relative version-info path is relative to spec file + self.versrsrc = os.path.join( + CONF['specpath'], self.versrsrc) + + self.pkg = PKG(self.toc, cdict=kwargs.get('cdict', None), + exclude_binaries=self.exclude_binaries, + strip_binaries=self.strip, upx_binaries=self.upx, + upx_exclude=self.upx_exclude + ) + self.dependencies = self.pkg.dependencies + + # Get the path of the bootloader and store it in a TOC, so it + # can be checked for being changed. + exe = self._bootloader_file('run', '.exe' if is_win or is_cygwin else '') + self.exefiles = TOC([(os.path.basename(exe), exe, 'EXECUTABLE')]) + + self.__postinit__() + + _GUTS = (# input parameters + ('name', _check_guts_eq), + ('console', _check_guts_eq), + ('debug', _check_guts_eq), + ('exclude_binaries', _check_guts_eq), + ('icon', _check_guts_eq), + ('versrsrc', _check_guts_eq), + ('uac_admin', _check_guts_eq), + ('uac_uiaccess', _check_guts_eq), + ('manifest', _check_guts_eq), + ('append_pkg', _check_guts_eq), + # for the case the directory ius shared between platforms: + ('pkgname', _check_guts_eq), + ('toc', _check_guts_eq), + ('resources', _check_guts_eq), + ('strip', _check_guts_eq), + ('upx', _check_guts_eq), + ('mtm', None,), # checked below + # no calculated/analysed values + ('exefiles', _check_guts_toc), + ) + + def _check_guts(self, data, last_build): + if not os.path.exists(self.name): + logger.info("Rebuilding %s because %s missing", + self.tocbasename, os.path.basename(self.name)) + return 1 + if not self.append_pkg and not os.path.exists(self.pkgname): + logger.info("Rebuilding because %s missing", + os.path.basename(self.pkgname)) + return 1 + + if Target._check_guts(self, data, last_build): + return True + + if (data['versrsrc'] or data['resources']) and not is_win: + # todo: really ignore :-) + logger.warning('ignoring version, manifest and resources, platform not capable') + if data['icon'] and not (is_win or is_darwin): + logger.warning('ignoring icon, platform not capable') + + mtm = data['mtm'] + if mtm != misc.mtime(self.name): + logger.info("Rebuilding %s because mtimes don't match", self.tocbasename) + return True + if mtm < misc.mtime(self.pkg.tocfilename): + logger.info("Rebuilding %s because pkg is more recent", self.tocbasename) + return True + return False + + def _bootloader_file(self, exe, extension=None): + """ + Pick up the right bootloader file - debug, console, windowed. + """ + # Having console/windowed bootolader makes sense only on Windows and + # Mac OS X. + if is_win or is_darwin: + if not self.console: + exe = exe + 'w' + # There are two types of bootloaders: + # run - release, no verbose messages in console. + # run_d - contains verbose messages in console. + if self.debug: + exe = exe + '_d' + if extension: + exe = exe + extension + bootloader_file = os.path.join(HOMEPATH, 'PyInstaller', 'bootloader', PLATFORM, exe) + logger.info('Bootloader %s' % bootloader_file) + return bootloader_file + + def assemble(self): + from ..config import CONF + logger.info("Building EXE from %s", self.tocbasename) + trash = [] + if os.path.exists(self.name): + os.remove(self.name) + if not os.path.exists(os.path.dirname(self.name)): + os.makedirs(os.path.dirname(self.name)) + exe = self.exefiles[0][1] # pathname of bootloader + if not os.path.exists(exe): + raise SystemExit(_MISSING_BOOTLOADER_ERRORMSG) + + if is_win: + fd, tmpnm = tempfile.mkstemp(prefix=os.path.basename(exe) + ".", + dir=CONF['workpath']) + # need to close the file, otherwise copying resources will fail + # with "the file [...] is being used by another process" + os.close(fd) + self._copyfile(exe, tmpnm) + os.chmod(tmpnm, 0o755) + if not self.icon: + # --icon not specified; use default from bootloader folder + self.icon = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + 'bootloader', 'images', + 'icon-console.ico' if self.console else 'icon-windowed.ico') + if self.icon != "NONE": + icon.CopyIcons(tmpnm, self.icon) + if self.versrsrc: + versioninfo.SetVersion(tmpnm, self.versrsrc) + for res in self.resources: + res = res.split(",") + for i in range(1, len(res)): + try: + res[i] = int(res[i]) + except ValueError: + pass + resfile = res[0] + if not os.path.isabs(resfile): + resfile = os.path.join(CONF['specpath'], resfile) + restype = resname = reslang = None + if len(res) > 1: + restype = res[1] + if len(res) > 2: + resname = res[2] + if len(res) > 3: + reslang = res[3] + try: + winresource.UpdateResourcesFromResFile(tmpnm, resfile, + [restype or "*"], + [resname or "*"], + [reslang or "*"]) + except winresource.pywintypes.error as exc: + if exc.args[0] != winresource.ERROR_BAD_EXE_FORMAT: + logger.error("Error while updating resources in %s" + " from resource file %s", tmpnm, resfile, exc_info=1) + continue + + # Handle the case where the file contains no resources, and is + # intended as a single resource to be added to the exe. + if not restype or not resname: + logger.error("resource type and/or name not specified") + continue + if "*" in (restype, resname): + logger.error("no wildcards allowed for resource type " + "and name when source file does not " + "contain resources") + continue + try: + winresource.UpdateResourcesFromDataFile(tmpnm, + resfile, + restype, + [resname], + [reslang or 0]) + except winresource.pywintypes.error: + logger.error("Error while updating resource %s %s in %s" + " from data file %s", + restype, resname, tmpnm, resfile, exc_info=1) + if self.manifest and not self.exclude_binaries: + self.manifest.update_resources(tmpnm, [1]) + trash.append(tmpnm) + exe = tmpnm + + # NOTE: Do not look up for bootloader file in the cache because it might + # get corrupted by UPX when UPX is available. See #1863 for details. + + if not self.append_pkg: + logger.info("Copying bootloader exe to %s", self.name) + self._copyfile(exe, self.name) + logger.info("Copying archive to %s", self.pkgname) + self._copyfile(self.pkg.name, self.pkgname) + elif is_linux: + self._copyfile(exe, self.name) + logger.info("Appending archive to ELF section in EXE %s", self.name) + retcode, stdout, stderr = exec_command_all( + 'objcopy', '--add-section', 'pydata=%s' % self.pkg.name, + self.name) + logger.debug("objcopy returned %i", retcode) + if stdout: + logger.debug(stdout) + if stderr: + logger.debug(stderr) + if retcode != 0: + raise SystemError("objcopy Failure: %s" % stderr) + else: + # Fall back to just append on end of file + logger.info("Appending archive to EXE %s", self.name) + with open(self.name, 'wb') as outf: + # write the bootloader data + with open(exe, 'rb') as infh: + shutil.copyfileobj(infh, outf, length=64*1024) + # write the archive data + with open(self.pkg.name, 'rb') as infh: + shutil.copyfileobj(infh, outf, length=64*1024) + + if is_darwin: + # Fix Mach-O header for codesigning on OS X. + logger.info("Fixing EXE for code signing %s", self.name) + import PyInstaller.utils.osx as osxutils + osxutils.fix_exe_for_code_signing(self.name) + if is_win: + # Set checksum to appease antiviral software. + from PyInstaller.utils.win32.winutils import set_exe_checksum + set_exe_checksum(self.name) + + os.chmod(self.name, 0o755) + # get mtime for storing into the guts + self.mtm = misc.mtime(self.name) + for item in trash: + os.remove(item) + logger.info("Building EXE from %s completed successfully.", + self.tocbasename) + + + def _copyfile(self, infile, outfile): + with open(infile, 'rb') as infh: + with open(outfile, 'wb') as outfh: + shutil.copyfileobj(infh, outfh, length=64*1024) + + +class COLLECT(Target): + """ + In one-dir mode creates the output folder with all necessary files. + """ + def __init__(self, *args, **kws): + """ + args + One or more arguments that are either TOCs Targets. + kws + Possible keywork arguments: + + name + The name of the directory to be built. + """ + from ..config import CONF + Target.__init__(self) + self.strip_binaries = kws.get('strip', False) + self.upx_exclude = kws.get("upx_exclude", []) + self.console = True + + if CONF['hasUPX']: + self.upx_binaries = kws.get('upx', False) + else: + self.upx_binaries = False + + self.name = kws.get('name') + # Old .spec format included in 'name' the path where to collect files + # for the created app. + # app. New format includes only directory name. + # + # The 'name' directory is created in DISTPATH and necessary files are + # then collected to this directory. + self.name = os.path.join(CONF['distpath'], os.path.basename(self.name)) + + self.toc = TOC() + for arg in args: + if isinstance(arg, TOC): + self.toc.extend(arg) + elif isinstance(arg, Target): + self.toc.append((os.path.basename(arg.name), arg.name, arg.typ)) + if isinstance(arg, EXE): + self.console = arg.console + for tocnm, fnm, typ in arg.toc: + if tocnm == os.path.basename(arg.name) + ".manifest": + self.toc.append((tocnm, fnm, typ)) + if not arg.append_pkg: + self.toc.append((os.path.basename(arg.pkgname), arg.pkgname, 'PKG')) + self.toc.extend(arg.dependencies) + else: + self.toc.extend(arg) + self.__postinit__() + + _GUTS = ( + # COLLECT always builds, just want the toc to be written out + ('toc', None), + ) + + def _check_guts(self, data, last_build): + # COLLECT always needs to be executed, since it will clean the output + # directory anyway to make sure there is no existing cruft accumulating + return 1 + + def assemble(self): + _make_clean_directory(self.name) + logger.info("Building COLLECT %s", self.tocbasename) + toc = add_suffix_to_extensions(self.toc) + for inm, fnm, typ in toc: + if not os.path.exists(fnm) or not os.path.isfile(fnm) and is_path_to_egg(fnm): + # file is contained within python egg, it is added with the egg + continue + if os.pardir in os.path.normpath(inm).split(os.sep) \ + or os.path.isabs(inm): + raise SystemExit('Security-Alert: try to store file outside ' + 'of dist-directory. Aborting. %r' % inm) + tofnm = os.path.join(self.name, inm) + todir = os.path.dirname(tofnm) + if not os.path.exists(todir): + os.makedirs(todir) + elif not os.path.isdir(todir): + raise SystemExit( + "Pyinstaller needs to make a directory, but there " + "already is a file at that path. " + "The file at issue is {!r}".format(todir)) + if typ in ('EXTENSION', 'BINARY'): + fnm = checkCache(fnm, strip=self.strip_binaries, + upx=self.upx_binaries, + upx_exclude=self.upx_exclude, + dist_nm=inm) + if typ != 'DEPENDENCY': + if os.path.isdir(fnm): + # beacuse shutil.copy2() is the default copy function + # for shutil.copytree, this will also copy file metadata + shutil.copytree(fnm, tofnm) + else: + shutil.copy(fnm, tofnm) + try: + shutil.copystat(fnm, tofnm) + except OSError: + logger.warning("failed to copy flags of %s", fnm) + if typ in ('EXTENSION', 'BINARY'): + os.chmod(tofnm, 0o755) + logger.info("Building COLLECT %s completed successfully.", + self.tocbasename) + + +class MERGE(object): + """ + Merge repeated dependencies from other executables into the first + execuable. Data and binary files are then present only once and some + disk space is thus reduced. + """ + def __init__(self, *args): + """ + Repeated dependencies are then present only once in the first + executable in the 'args' list. Other executables depend on the + first one. Other executables have to extract necessary files + from the first executable. + + args dependencies in a list of (Analysis, id, filename) tuples. + Replace id with the correct filename. + """ + # The first Analysis object with all dependencies. + # Any item from the first executable cannot be removed. + self._main = None + + self._dependencies = {} + + self._id_to_path = {} + for _, i, p in args: + self._id_to_path[os.path.normcase(i)] = p + + # Get the longest common path + common_prefix = os.path.commonprefix([os.path.normcase(os.path.abspath(a.scripts[-1][1])) for a, _, _ in args]) + self._common_prefix = os.path.dirname(common_prefix) + if self._common_prefix[-1] != os.sep: + self._common_prefix += os.sep + logger.info("Common prefix: %s", self._common_prefix) + + self._merge_dependencies(args) + + def _merge_dependencies(self, args): + """ + Filter shared dependencies to be only in first executable. + """ + for analysis, _, _ in args: + path = os.path.normcase(os.path.abspath(analysis.scripts[-1][1])) + path = path.replace(self._common_prefix, "", 1) + path = os.path.splitext(path)[0] + if os.path.normcase(path) in self._id_to_path: + path = self._id_to_path[os.path.normcase(path)] + self._set_dependencies(analysis, path) + + def _set_dependencies(self, analysis, path): + """ + Synchronize the Analysis result with the needed dependencies. + """ + for toc in (analysis.binaries, analysis.datas): + for i, tpl in enumerate(toc): + if not tpl[1] in self._dependencies: + logger.debug("Adding dependency %s located in %s" % (tpl[1], path)) + self._dependencies[tpl[1]] = path + else: + dep_path = self._get_relative_path(path, self._dependencies[tpl[1]]) + # Ignore references that point to the origin package. + # This can happen if the same resource is listed + # multiple times in TOCs (e.g., once as binary and + # once as data). + if dep_path.endswith(path): + logger.debug("Ignoring self-reference of %s for %s, " + "located in %s - duplicated TOC entry?", + tpl[1], path, dep_path) + # Clear the entry as it is a duplicate. + toc[i] = (None, None, None) + continue + logger.debug("Referencing %s to be a dependecy for %s, located in %s" % (tpl[1], path, dep_path)) + # Determine the path relative to dep_path (i.e, within + # the target directory) from the 'name' component + # of the TOC tuple. If entry is EXTENSION, then the + # relative path needs to be reconstructed from the + # name components. + if tpl[2] == 'EXTENSION': + # Split on os.path.sep first, to handle additional + # path prefix (e.g., lib-dynload) + ext_components = tpl[0].split(os.path.sep) + ext_components = ext_components[:-1] \ + + ext_components[-1].split('.')[:-1] + if ext_components: + rel_path = os.path.join(*ext_components) + else: + rel_path = '' + else: + rel_path = os.path.dirname(tpl[0]) + # Take filename from 'path' (second component of + # TOC tuple); this way, we don't need to worry about + # suffix of extensions. + filename = os.path.basename(tpl[1]) + # Construct the full file path relative to dep_path... + filename = os.path.join(rel_path, filename) + # ...and use it in new DEPENDENCY entry + analysis.dependencies.append( + (":".join((dep_path, filename)), + tpl[1], + "DEPENDENCY")) + toc[i] = (None, None, None) + # Clean the list + toc[:] = [tpl for tpl in toc if tpl != (None, None, None)] + + # TODO move this function to PyInstaller.compat module (probably improve + # function compat.relpath() + # TODO use os.path.relpath instead + def _get_relative_path(self, startpath, topath): + start = startpath.split(os.sep)[:-1] + start = ['..'] * len(start) + if start: + start.append(topath) + return os.sep.join(start) + else: + return topath + + +UNCOMPRESSED = 0 +COMPRESSED = 1 + +_MISSING_BOOTLOADER_ERRORMSG = """ +Fatal error: PyInstaller does not include a pre-compiled bootloader for your +platform. For more details and instructions how to build the bootloader see + +""" diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/build_main.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/build_main.py new file mode 100644 index 0000000000000000000000000000000000000000..ca5ea8b0634632e05bfacb1de4bb66252a197f98 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/build_main.py @@ -0,0 +1,737 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Build packages using spec files. + +NOTE: All global variables, classes and imported modules create API + for .spec files. +""" + + +import glob +import os +import pprint +import shutil +import sys + +import pkg_resources + + +# Relative imports to PyInstaller modules. +from .. import HOMEPATH, DEFAULT_DISTPATH, DEFAULT_WORKPATH +from .. import compat +from .. import log as logging +from ..utils.misc import absnormpath, compile_py_files +from ..compat import is_win, PYDYLIB_NAMES, open_file +from ..depend import bindepend +from ..depend.analysis import initialize_modgraph +from .api import PYZ, EXE, COLLECT, MERGE +from .datastruct import TOC, Target, Tree, _check_guts_eq +from .osx import BUNDLE +from .toc_conversion import DependencyProcessor +from .utils import _check_guts_toc_mtime, format_binaries_and_datas +from ..depend.utils import create_py3_base_library, scan_code_for_ctypes +from ..archive import pyz_crypto +from ..utils.misc import get_path_to_toplevel_modules, get_unicode_modules, mtime + +if is_win: + from ..utils.win32 import winmanifest + +logger = logging.getLogger(__name__) + +STRINGTYPE = type('') +TUPLETYPE = type((None,)) + +rthooks = {} + +# place where the loader modules and initialization scripts live +_init_code_path = os.path.join(HOMEPATH, 'PyInstaller', 'loader') + +IMPORT_TYPES = ['top-level', 'conditional', 'delayed', 'delayed, conditional', + 'optional', 'conditional, optional', 'delayed, optional', + 'delayed, conditional, optional'] + +WARNFILE_HEADER = """\ + +This file lists modules PyInstaller was not able to find. This does not +necessarily mean this module is required for running you program. Python and +Python 3rd-party packages include a lot of conditional or optional modules. For +example the module 'ntpath' only exists on Windows, whereas the module +'posixpath' only exists on Posix systems. + +Types if import: +* top-level: imported at the top-level - look at these first +* conditional: imported within an if-statement +* delayed: imported from within a function +* optional: imported within a try-except-statement + +IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for + yourself tracking down the missing module. Thanks! + +""" + + +# TODO find better place for function. +def setupUPXFlags(): + f = compat.getenv("UPX", "") + if is_win: + # Binaries built with Visual Studio 7.1 require --strip-loadconf + # or they won't compress. Configure.py makes sure that UPX is new + # enough to support --strip-loadconf. + f = "--strip-loadconf " + f + # Do not compress any icon, so that additional icons in the executable + # can still be externally bound + f = "--compress-icons=0 " + f + f = "--best " + f + compat.setenv("UPX", f) + + +class Analysis(Target): + """ + Class does analysis of the user's main Python scripts. + + An Analysis has five outputs, all TOCs (Table of Contents) accessed as + attributes of the analysis. + + scripts + The scripts you gave Analysis as input, with any runtime hook scripts + prepended. + pure + The pure Python modules. + binaries + The extensionmodules and their dependencies. The secondary dependecies + are filtered. On Windows files from C:\\Windows are excluded by default. + On Linux/Unix only system libraries from /lib or /usr/lib are excluded. + datas + Data-file dependencies. These are data-file that are found to be needed + by modules. They can be anything: plugins, font files, images, translations, + etc. + zipfiles + The zipfiles dependencies (usually .egg files). + """ + _old_scripts = { + absnormpath(os.path.join(HOMEPATH, "support", "_mountzlib.py")), + absnormpath(os.path.join(HOMEPATH, "support", "useUnicode.py")), + absnormpath(os.path.join(HOMEPATH, "support", "useTK.py")), + absnormpath(os.path.join(HOMEPATH, "support", "unpackTK.py")), + absnormpath(os.path.join(HOMEPATH, "support", "removeTK.py")) + } + + def __init__(self, scripts, pathex=None, binaries=None, datas=None, + hiddenimports=None, hookspath=None, excludes=None, runtime_hooks=None, + cipher=None, win_no_prefer_redirects=False, win_private_assemblies=False, + noarchive=False): + """ + scripts + A list of scripts specified as file names. + pathex + An optional list of paths to be searched before sys.path. + binaries + An optional list of additional binaries (dlls, etc.) to include. + datas + An optional list of additional data files to include. + hiddenimport + An optional list of additional (hidden) modules to include. + hookspath + An optional list of additional paths to search for hooks. + (hook-modules). + excludes + An optional list of module or package names (their Python names, + not path names) that will be ignored (as though they were not found). + runtime_hooks + An optional list of scripts to use as users' runtime hooks. Specified + as file names. + cipher + Add optional instance of the pyz_crypto.PyiBlockCipher class + (with a provided key). + win_no_prefer_redirects + If True, prefers not to follow version redirects when searching for + Windows SxS Assemblies. + win_private_assemblies + If True, changes all bundled Windows SxS Assemblies into Private + Assemblies to enforce assembly versions. + noarchive + If True, don't place source files in a archive, but keep them as + individual files. + """ + super(Analysis, self).__init__() + from ..config import CONF + + self.inputs = [] + spec_dir = os.path.dirname(CONF['spec']) + for script in scripts: + # If path is relative, it is relative to the location of .spec file. + if not os.path.isabs(script): + script = os.path.join(spec_dir, script) + if absnormpath(script) in self._old_scripts: + logger.warning('Ignoring obsolete auto-added script %s', script) + continue + # Normalize script path. + script = os.path.normpath(script) + if not os.path.exists(script): + raise SystemExit("script '%s' not found" % script) + self.inputs.append(script) + + # Django hook requires this variable to find the script manage.py. + CONF['main_script'] = self.inputs[0] + + self.pathex = self._extend_pathex(pathex, self.inputs) + # Set global config variable 'pathex' to make it available for + # PyInstaller.utils.hooks and import hooks. Path extensions for module + # search. + CONF['pathex'] = self.pathex + # Extend sys.path so PyInstaller could find all necessary modules. + logger.info('Extending PYTHONPATH with paths\n' + pprint.pformat(self.pathex)) + sys.path.extend(self.pathex) + + # Set global variable to hold assembly binding redirects + CONF['binding_redirects'] = [] + + self.hiddenimports = hiddenimports or [] + # Include modules detected when parsing options, like 'codecs' and encodings. + self.hiddenimports.extend(CONF['hiddenimports']) + + self.hookspath = [] + # Append directories in `hookspath` (`--additional-hooks-dir`) to + # take precedence over those from the entry points. + if hookspath: + self.hookspath.extend(hookspath) + + # Add hook directories from PyInstaller entry points. + for entry_point in pkg_resources.iter_entry_points( + 'pyinstaller40', 'hook-dirs'): + self.hookspath += list(entry_point.load()()) + + # Custom runtime hook files that should be included and started before + # any existing PyInstaller runtime hooks. + self.custom_runtime_hooks = runtime_hooks or [] + + if cipher: + logger.info('Will encrypt Python bytecode with key: %s', cipher.key) + # Create a Python module which contains the decryption key which will + # be used at runtime by pyi_crypto.PyiBlockCipher. + pyi_crypto_key_path = os.path.join(CONF['workpath'], 'pyimod00_crypto_key.py') + with open_file(pyi_crypto_key_path, 'w', encoding='utf-8') as f: + f.write('# -*- coding: utf-8 -*-\n' + 'key = %r\n' % cipher.key) + self.hiddenimports.append('tinyaes') + + self.excludes = excludes or [] + self.scripts = TOC() + self.pure = TOC() + self.binaries = TOC() + self.zipfiles = TOC() + self.zipped_data = TOC() + self.datas = TOC() + self.dependencies = TOC() + self.binding_redirects = CONF['binding_redirects'] = [] + self.win_no_prefer_redirects = win_no_prefer_redirects + self.win_private_assemblies = win_private_assemblies + self._python_version = sys.version + self.noarchive = noarchive + + self.__postinit__() + + + # TODO create function to convert datas/binaries from 'hook format' to TOC. + # Initialise 'binaries' and 'datas' with lists specified in .spec file. + if binaries: + logger.info("Appending 'binaries' from .spec") + for name, pth in format_binaries_and_datas(binaries, workingdir=spec_dir): + self.binaries.append((name, pth, 'BINARY')) + if datas: + logger.info("Appending 'datas' from .spec") + for name, pth in format_binaries_and_datas(datas, workingdir=spec_dir): + self.datas.append((name, pth, 'DATA')) + + _GUTS = (# input parameters + ('inputs', _check_guts_eq), # parameter `scripts` + ('pathex', _check_guts_eq), + ('hiddenimports', _check_guts_eq), + ('hookspath', _check_guts_eq), + ('excludes', _check_guts_eq), + ('custom_runtime_hooks', _check_guts_eq), + ('win_no_prefer_redirects', _check_guts_eq), + ('win_private_assemblies', _check_guts_eq), + ('noarchive', _check_guts_eq), + + #'cipher': no need to check as it is implied by an + # additional hidden import + + #calculated/analysed values + ('_python_version', _check_guts_eq), + ('scripts', _check_guts_toc_mtime), + ('pure', lambda *args: _check_guts_toc_mtime(*args, **{'pyc': 1})), + ('binaries', _check_guts_toc_mtime), + ('zipfiles', _check_guts_toc_mtime), + ('zipped_data', None), # TODO check this, too + ('datas', _check_guts_toc_mtime), + # TODO: Need to add "dependencies"? + + # cached binding redirects - loaded into CONF for PYZ/COLLECT to find. + ('binding_redirects', None), + ) + + def _extend_pathex(self, spec_pathex, scripts): + """ + Normalize additional paths where PyInstaller will look for modules and + add paths with scripts to the list of paths. + + :param spec_pathex: Additional paths defined defined in .spec file. + :param scripts: Scripts to create executable from. + :return: list of updated paths + """ + # Based on main supplied script - add top-level modules directory to PYTHONPATH. + # Sometimes the main app script is not top-level module but submodule like 'mymodule.mainscript.py'. + # In that case PyInstaller will not be able find modules in the directory containing 'mymodule'. + # Add this directory to PYTHONPATH so PyInstaller could find it. + pathex = [] + # Add scripts paths first. + for script in scripts: + logger.debug('script: %s' % script) + script_toplevel_dir = get_path_to_toplevel_modules(script) + if script_toplevel_dir: + pathex.append(script_toplevel_dir) + # Append paths from .spec. + if spec_pathex is not None: + pathex.extend(spec_pathex) + # Normalize paths in pathex and make them absolute. + return [absnormpath(p) for p in pathex] + + def _check_guts(self, data, last_build): + if Target._check_guts(self, data, last_build): + return True + for fnm in self.inputs: + if mtime(fnm) > last_build: + logger.info("Building because %s changed", fnm) + return True + # Now we know that none of the input parameters and none of + # the input files has changed. So take the values calculated + # resp. analysed in the last run and store them in `self`. + self.scripts = TOC(data['scripts']) + self.pure = TOC(data['pure']) + self.binaries = TOC(data['binaries']) + self.zipfiles = TOC(data['zipfiles']) + self.zipped_data = TOC(data['zipped_data']) + self.datas = TOC(data['datas']) + + # Store previously found binding redirects in CONF for later use by PKG/COLLECT + from ..config import CONF + self.binding_redirects = CONF['binding_redirects'] = data['binding_redirects'] + + return False + + def assemble(self): + """ + This method is the MAIN method for finding all necessary files to be bundled. + """ + from ..config import CONF + + for m in self.excludes: + logger.debug("Excluding module '%s'" % m) + self.graph = initialize_modgraph( + excludes=self.excludes, user_hook_dirs=self.hookspath) + + # TODO Find a better place where to put 'base_library.zip' and when to created it. + # For Python 3 it is necessary to create file 'base_library.zip' + # containing core Python modules. In Python 3 some built-in modules + # are written in pure Python. base_library.zip is a way how to have + # those modules as "built-in". + libzip_filename = os.path.join(CONF['workpath'], 'base_library.zip') + create_py3_base_library(libzip_filename, graph=self.graph) + # Bundle base_library.zip as data file. + # Data format of TOC item: ('relative_path_in_dist_dir', 'absolute_path_on_disk', 'DATA') + self.datas.append((os.path.basename(libzip_filename), libzip_filename, 'DATA')) + + # Expand sys.path of module graph. + # The attribute is the set of paths to use for imports: sys.path, + # plus our loader, plus other paths from e.g. --path option). + self.graph.path = self.pathex + self.graph.path + self.graph.set_setuptools_nspackages() + + logger.info("running Analysis %s", self.tocbasename) + # Get paths to Python and, in Windows, the manifest. + python = sys.executable + if not is_win: + # Linux/MacOS: get a real, non-link path to the running Python executable. + while os.path.islink(python): + python = os.path.join(os.path.dirname(python), os.readlink(python)) + depmanifest = None + else: + # Windows: Create a manifest to embed into built .exe, containing the same + # dependencies as python.exe. + depmanifest = winmanifest.Manifest(type_="win32", name=CONF['specnm'], + processorArchitecture=winmanifest.processor_architecture(), + version=(1, 0, 0, 0)) + depmanifest.filename = os.path.join(CONF['workpath'], + CONF['specnm'] + ".exe.manifest") + + # We record "binaries" separately from the modulegraph, as there + # is no way to record those dependencies in the graph. These include + # the python executable and any binaries added by hooks later. + # "binaries" are not the same as "extensions" which are .so or .dylib + # that are found and recorded as extension nodes in the graph. + # Reset seen variable before running bindepend. We use bindepend only for + # the python executable. + bindepend.seen.clear() + + # Add binary and assembly dependencies of Python.exe. + # This also ensures that its assembly depencies under Windows get added to the + # built .exe's manifest. Python 2.7 extension modules have no assembly + # dependencies, and rely on the app-global dependencies set by the .exe. + self.binaries.extend(bindepend.Dependencies([('', python, '')], + manifest=depmanifest, + redirects=self.binding_redirects)[1:]) + if is_win: + depmanifest.writeprettyxml() + + ### Module graph. + # + # Construct the module graph of import relationships between modules + # required by this user's application. For each entry point (top-level + # user-defined Python script), all imports originating from this entry + # point are recursively parsed into a subgraph of the module graph. This + # subgraph is then connected to this graph's root node, ensuring + # imported module nodes will be reachable from the root node -- which is + # is (arbitrarily) chosen to be the first entry point's node. + + # List to hold graph nodes of scripts and runtime hooks in use order. + priority_scripts = [] + + # Assume that if the script does not exist, Modulegraph will raise error. + # Save the graph nodes of each in sequence. + for script in self.inputs: + logger.info("Analyzing %s", script) + priority_scripts.append(self.graph.run_script(script)) + + # Analyze the script's hidden imports (named on the command line) + self.graph.add_hiddenimports(self.hiddenimports) + + ### Post-graph hooks. + self.graph.process_post_graph_hooks() + + # Update 'binaries' TOC and 'datas' TOC. + deps_proc = DependencyProcessor(self.graph, + self.graph._additional_files_cache) + self.binaries.extend(deps_proc.make_binaries_toc()) + self.datas.extend(deps_proc.make_datas_toc()) + self.zipped_data.extend(deps_proc.make_zipped_data_toc()) + # Note: zipped eggs are collected below + + + ### Look for dlls that are imported by Python 'ctypes' module. + # First get code objects of all modules that import 'ctypes'. + logger.info('Looking for ctypes DLLs') + ctypes_code_objs = self.graph.get_co_using_ctypes() # dict like: {'module1': code_obj, 'module2': code_obj} + for name, co in ctypes_code_objs.items(): + # Get dlls that might be needed by ctypes. + logger.debug('Scanning %s for shared libraries or dlls', name) + ctypes_binaries = scan_code_for_ctypes(co) + self.binaries.extend(set(ctypes_binaries)) + + # Analyze run-time hooks. + # Run-time hooks has to be executed before user scripts. Add them + # to the beginning of 'priority_scripts'. + priority_scripts = self.graph.analyze_runtime_hooks(self.custom_runtime_hooks) + priority_scripts + + # 'priority_scripts' is now a list of the graph nodes of custom runtime + # hooks, then regular runtime hooks, then the PyI loader scripts. + # Further on, we will make sure they end up at the front of self.scripts + + ### Extract the nodes of the graph as TOCs for further processing. + + # Initialize the scripts list with priority scripts in the proper order. + self.scripts = self.graph.nodes_to_toc(priority_scripts) + + # Extend the binaries list with all the Extensions modulegraph has found. + self.binaries = self.graph.make_binaries_toc(self.binaries) + # Fill the "pure" list with pure Python modules. + assert len(self.pure) == 0 + self.pure = self.graph.make_pure_toc() + # And get references to module code objects constructed by ModuleGraph + # to avoid writing .pyc/pyo files to hdd. + self.pure._code_cache = self.graph.get_code_objects() + + # Add remaining binary dependencies - analyze Python C-extensions and what + # DLLs they depend on. + logger.info('Looking for dynamic libraries') + self.binaries.extend(bindepend.Dependencies(self.binaries, + redirects=self.binding_redirects)) + + ### Include zipped Python eggs. + logger.info('Looking for eggs') + self.zipfiles.extend(deps_proc.make_zipfiles_toc()) + + # Verify that Python dynamic library can be found. + # Without dynamic Python library PyInstaller cannot continue. + self._check_python_library(self.binaries) + + if is_win: + # Remove duplicate redirects + self.binding_redirects[:] = list(set(self.binding_redirects)) + logger.info("Found binding redirects: \n%s", self.binding_redirects) + + # Filter binaries to adjust path of extensions that come from + # python's lib-dynload directory. Prefix them with lib-dynload + # so that we'll collect them into subdirectory instead of + # directly into _MEIPASS + for idx, tpl in enumerate(self.binaries): + name, path, typecode = tpl + if typecode == 'EXTENSION' \ + and not os.path.dirname(os.path.normpath(name)) \ + and os.path.basename(os.path.dirname(path)) == 'lib-dynload': + name = os.path.join('lib-dynload', name) + self.binaries[idx] = (name, path, typecode) + + # Place Python source in data files for the noarchive case. + if self.noarchive: + # Create a new TOC of ``(dest path for .pyc, source for .py, type)``. + new_toc = TOC() + for name, path, typecode in self.pure: + assert typecode == 'PYMODULE' + # Transform a python module name into a file name. + name = name.replace('.', os.sep) + # Special case: modules have an implied filename to add. + if os.path.splitext(os.path.basename(path))[0] == '__init__': + name += os.sep + '__init__' + # Append the extension for the compiled result. + # In python 3.5 (PEP-488) .pyo files were replaced by + # .opt-1.pyc and .opt-2.pyc. However, it seems that for + # bytecode-only module distribution, we always need to + # use the .pyc extension. + name += '.pyc' + new_toc.append((name, path, typecode)) + # Put the result of byte-compiling this TOC in datas. Mark all entries as data. + for name, path, typecode in compile_py_files(new_toc, CONF['workpath']): + self.datas.append((name, path, 'DATA')) + # Store no source in the archive. + self.pure = TOC() + + # Write warnings about missing modules. + self._write_warnings() + # Write debug information about hte graph + self._write_graph_debug() + + def _write_warnings(self): + """ + Write warnings about missing modules. Get them from the graph + and use the graph to figure out who tried to import them. + """ + def dependency_description(name, depInfo): + if not depInfo or depInfo == 'direct': + imptype = 0 + else: + imptype = (depInfo.conditional + + 2 * depInfo.function + + 4 * depInfo.tryexcept) + return '%s (%s)' % (name, IMPORT_TYPES[imptype]) + + from ..config import CONF + miss_toc = self.graph.make_missing_toc() + with open_file(CONF['warnfile'], 'w', encoding='utf-8') as wf: + wf.write(WARNFILE_HEADER) + for (n, p, status) in miss_toc: + importers = self.graph.get_importers(n) + print(status, 'module named', n, '- imported by', + ', '.join(dependency_description(name, data) + for name, data in importers), + file=wf) + logger.info("Warnings written to %s", CONF['warnfile']) + + def _write_graph_debug(self): + """Write a xref (in html) and with `--log-level DEBUG` a dot-drawing + of the graph. + """ + from ..config import CONF + with open_file(CONF['xref-file'], 'w', encoding='utf-8') as fh: + self.graph.create_xref(fh) + logger.info("Graph cross-reference written to %s", CONF['xref-file']) + if logger.getEffectiveLevel() > logging.DEBUG: + return + # The `DOT language's `_ + # default character encoding (see the end of the linked page) is UTF-8. + with open_file(CONF['dot-file'], 'w', encoding='utf-8') as fh: + self.graph.graphreport(fh) + logger.info("Graph drawing written to %s", CONF['dot-file']) + + def _check_python_library(self, binaries): + """ + Verify presence of the Python dynamic library in the binary dependencies. + Python library is an essential piece that has to be always included. + """ + # First check that libpython is in resolved binary dependencies. + for (nm, filename, typ) in binaries: + if typ == 'BINARY' and nm in PYDYLIB_NAMES: + # Just print its filename and return. + logger.info('Using Python library %s', filename) + # Checking was successful - end of function. + return + + # Python lib not in dependencies - try to find it. + logger.info('Python library not in binary dependencies. Doing additional searching...') + python_lib = bindepend.get_python_library_path() + logger.debug('Adding Python library to binary dependencies') + binaries.append((os.path.basename(python_lib), python_lib, 'BINARY')) + logger.info('Using Python library %s', python_lib) + + +class ExecutableBuilder(object): + """ + Class that constructs the executable. + """ + # TODO wrap the 'main' and 'build' function into this class. + + +def build(spec, distpath, workpath, clean_build): + """ + Build the executable according to the created SPEC file. + """ + from ..config import CONF + + # Ensure starting tilde and environment variables get expanded in distpath / workpath. + # '~/path/abc', '${env_var_name}/path/abc/def' + distpath = compat.expand_path(distpath) + workpath = compat.expand_path(workpath) + CONF['spec'] = compat.expand_path(spec) + + CONF['specpath'], CONF['specnm'] = os.path.split(spec) + CONF['specnm'] = os.path.splitext(CONF['specnm'])[0] + + # Add 'specname' to workpath and distpath if they point to PyInstaller homepath. + if os.path.dirname(distpath) == HOMEPATH: + distpath = os.path.join(HOMEPATH, CONF['specnm'], os.path.basename(distpath)) + CONF['distpath'] = distpath + if os.path.dirname(workpath) == HOMEPATH: + workpath = os.path.join(HOMEPATH, CONF['specnm'], os.path.basename(workpath), CONF['specnm']) + else: + workpath = os.path.join(workpath, CONF['specnm']) + + CONF['warnfile'] = os.path.join(workpath, 'warn-%s.txt' % CONF['specnm']) + CONF['dot-file'] = os.path.join(workpath, 'graph-%s.dot' % CONF['specnm']) + CONF['xref-file'] = os.path.join(workpath, 'xref-%s.html' % CONF['specnm']) + + # Clean PyInstaller cache (CONF['cachedir']) and temporary files (workpath) + # to be able start a clean build. + if clean_build: + logger.info('Removing temporary files and cleaning cache in %s', CONF['cachedir']) + for pth in (CONF['cachedir'], workpath): + if os.path.exists(pth): + # Remove all files in 'pth'. + for f in glob.glob(pth + '/*'): + # Remove dirs recursively. + if os.path.isdir(f): + shutil.rmtree(f) + else: + os.remove(f) + + # Create DISTPATH and workpath if they does not exist. + for pth in (CONF['distpath'], workpath): + if not os.path.exists(pth): + os.makedirs(pth) + + # Construct NAMESPACE for running the Python code from .SPEC file. + # NOTE: Passing NAMESPACE allows to avoid having global variables in this + # module and makes isolated environment for running tests. + # NOTE: Defining NAMESPACE allows to map any class to a apecific name for .SPEC. + # FIXME: Some symbols might be missing. Add them if there are some failures. + # TODO: What from this .spec API is deprecated and could be removed? + spec_namespace = { + # Set of global variables that can be used while processing .spec file. + # Some of them act as configuration options. + 'DISTPATH': CONF['distpath'], + 'HOMEPATH': HOMEPATH, + 'SPEC': CONF['spec'], + 'specnm': CONF['specnm'], + 'SPECPATH': CONF['specpath'], + 'WARNFILE': CONF['warnfile'], + 'workpath': workpath, + # PyInstaller classes for .spec. + 'TOC': TOC, + 'Analysis': Analysis, + 'BUNDLE': BUNDLE, + 'COLLECT': COLLECT, + 'EXE': EXE, + 'MERGE': MERGE, + 'PYZ': PYZ, + 'Tree': Tree, + # Python modules available for .spec. + 'os': os, + 'pyi_crypto': pyz_crypto, + } + + # Set up module PyInstaller.config for passing some arguments to 'exec' + # function. + from ..config import CONF + CONF['workpath'] = workpath + + # Execute the specfile. Read it as a binary file... + try: + with open(spec, 'rb') as f: + # ... then let Python determine the encoding, since ``compile`` accepts + # byte strings. + code = compile(f.read(), spec, 'exec') + except FileNotFoundError as e: + raise SystemExit('spec "{}" not found'.format(spec)) + exec(code, spec_namespace) + +def __add_options(parser): + parser.add_argument("--distpath", metavar="DIR", + default=DEFAULT_DISTPATH, + help=('Where to put the bundled app (default: %s)' % + os.path.join(os.curdir, 'dist'))) + parser.add_argument('--workpath', default=DEFAULT_WORKPATH, + help=('Where to put all the temporary work files, ' + '.log, .pyz and etc. (default: %s)' % + os.path.join(os.curdir, 'build'))) + parser.add_argument('-y', '--noconfirm', + action="store_true", default=False, + help='Replace output directory (default: %s) without ' + 'asking for confirmation' % os.path.join('SPECPATH', 'dist', 'SPECNAME')) + parser.add_argument('--upx-dir', default=None, + help='Path to UPX utility (default: search the execution path)') + parser.add_argument("-a", "--ascii", action="store_true", + help="Do not include unicode encoding support " + "(default: included if available)") + parser.add_argument('--clean', dest='clean_build', action='store_true', + default=False, + help='Clean PyInstaller cache and remove temporary ' + 'files before building.') + + +def main(pyi_config, specfile, noconfirm, ascii=False, **kw): + + from ..config import CONF + CONF['noconfirm'] = noconfirm + + # Some modules are included if they are detected at build-time or + # if a command-line argument is specified. (e.g. --ascii) + if CONF.get('hiddenimports') is None: + CONF['hiddenimports'] = [] + # Test unicode support. + if not ascii: + CONF['hiddenimports'].extend(get_unicode_modules()) + + # FIXME: this should be a global import, but can't due to recursive imports + # If configuration dict is supplied - skip configuration step. + if pyi_config is None: + import PyInstaller.configure as configure + CONF.update(configure.get_config(kw.get('upx_dir'))) + else: + CONF.update(pyi_config) + + if CONF['hasUPX']: + setupUPXFlags() + + CONF['ui_admin'] = kw.get('ui_admin', False) + CONF['ui_access'] = kw.get('ui_uiaccess', False) + + build(specfile, kw.get('distpath'), kw.get('workpath'), kw.get('clean_build')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/datastruct.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/datastruct.py new file mode 100644 index 0000000000000000000000000000000000000000..bd11c0806b8cc24038d2a995f3274fe4e3fdd31a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/datastruct.py @@ -0,0 +1,287 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +from PyInstaller.utils import misc +from PyInstaller.utils.misc import load_py_data_struct, save_py_data_struct +from .. import log as logging +from .utils import _check_guts_eq + +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'): + name = os.path.normcase(name) + + return name + + +class TOC(list): + # TODO Simplify the representation and use directly Modulegraph objects. + """ + TOC (Table of Contents) class is a list of tuples of the form (name, path, tytecode). + + 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(TOC, self).__init__(self) + 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(TOC, self).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(TOC, self).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 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): + other = TOC(other) + filenames = self.filenames - other.filenames + result = TOC() + for entry in self: + unique = unique_name(entry) + + if unique in filenames: + super(TOC, result).append(entry) + return result + + def __rsub__(self, other): + result = TOC(other) + return result.__sub__(self) + + +class Target(object): + invcnum = 0 + + def __init__(self): + from ..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 = TOC() + + 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 = load_py_data_struct(self.tocfilename) + except: + 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) + save_py_data_struct(self.tocfilename, data) + + +class Tree(Target, TOC): + """ + This class is a way of creating a TOC (Table of Contents) 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) + TOC.__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(Tree, self)._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 diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/makespec.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/makespec.py new file mode 100644 index 0000000000000000000000000000000000000000..63a09921ea34e60ba395cef72540e1627332ec0f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/makespec.py @@ -0,0 +1,603 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 os +import sys +import argparse + +from .. import HOMEPATH, DEFAULT_SPECPATH +from .. import log as logging +from ..compat import expand_path, is_darwin, is_win, open_file +from .templates import onefiletmplt, onedirtmplt, cipher_absent_template, \ + cipher_init_template, bundleexetmplt, bundletmplt + +logger = logging.getLogger(__name__) +add_command_sep = os.pathsep + +# 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 quote_win_filepath(path): + # quote 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"), + ) + + +def add_data_or_binary(string): + try: + src, dest = string.split(add_command_sep) + except ValueError as e: + # Split into SRC and DEST failed, wrong syntax + raise argparse.ArgumentError( + "Wrong syntax, should be SRC{}DEST".format(add_command_sep) + ) from e + if not src or not dest: + # Syntax was correct, but one or both of SRC and DEST was not given + raise argparse.ArgumentError("You have to specify both SRC and DEST") + # Return tuple containing SRC and SRC + return (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 absolut, 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 + + +# 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 accomodate 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): + # 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: + 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: + 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 - 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_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]' # noqa: E501 + ] + + +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=False, + help="Create a one-folder bundle containing an executable (default)") + g.add_argument("-F", "--onefile", dest="onefile", + action="store_true", default=False, + 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 = parser.add_argument_group('What to bundle, where to search') + g.add_argument('--add-data', + action='append', default=[], type=add_data_or_binary, + metavar='', dest='datas', + help='Additional non-binary files or folders to be added ' + 'to the executable. The path separator is platform ' + 'specific, ``os.pathsep`` (which is ``;`` on Windows ' + 'and ``:`` on most unix systems) is used. This option ' + 'can be used multiple times.') + g.add_argument('--add-binary', + action='append', default=[], type=add_data_or_binary, + metavar='', dest="binaries", + help='Additional binary files to be added to the executable. ' + 'See the ``--add-data`` option for more details. ' + '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" + % 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("--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='The key used to encrypt Python bytecode.') + + 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 list containing one + # item, per `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("-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 Mac OS X specific options') + g.add_argument("-c", "--console", "--nowindowed", dest="console", + action="store_true", default=True, + help="Open a console window for standard i/o (default). " + "On Windows this option will have no effect if the " + "first script is a '.pyw' file.") + g.add_argument("-w", "--windowed", "--noconsole", dest="console", + action="store_false", + help="Windows and Mac OS X: do not provide a console window " + "for standard i/o. " + "On Mac OS X this also triggers building an OS X .app bundle. " + "On Windows this option will be set if the first " + "script is a '.pyw' file. " + "This option is ignored in *NIX systems.") + g.add_argument("-i", "--icon", dest="icon_file", + metavar='', + help="FILE.ico: apply that 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 Mac OS X. " + 'Use "NONE" to not apply any icon, ' + "thereby making the OS to show some default " + "(default: apply PyInstaller's icon)") + + 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("-m", "--manifest", metavar="", + help="add manifest FILE or XML to the exe") + 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 ' + 'which will request elevation upon application restart.') + 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", dest="win_private_assemblies", + action="store_true", + help="Any Shared Assemblies bundled into the application " + "will be changed into Private Assemblies. This means " + "the exact versions of these assemblies will always " + "be used, and any newer versions installed on user " + "machines at the system level will be ignored.") + g.add_argument("--win-no-prefer-redirects", dest="win_no_prefer_redirects", + action="store_true", + help="While searching for Shared or Private Assemblies to " + "bundle into the application, PyInstaller will prefer " + "not to follow policies that redirect to newer versions, " + "and will try to bundle the exact versions of the assembly.") + + + g = parser.add_argument_group('Mac OS X specific options') + g.add_argument('--osx-bundle-identifier', dest='bundle_identifier', + help='Mac OS X .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 = 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.") + 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 e.g. a supervisor " + "process signals both the bootloader and child " + "(e.g. via a process group) to avoid signalling " + "the child twice.")) + + +def main(scripts, name=None, onefile=None, + console=True, debug=None, strip=False, noupx=False, upx_exclude=None, + runtime_tmpdir=None, pathex=None, version_file=None, specpath=None, + bootloader_ignore_signals=False, + datas=None, binaries=None, icon_file=None, manifest=None, resources=None, bundle_identifier=None, + hiddenimports=None, hookspath=None, key=None, runtime_hooks=None, + excludes=None, uac_admin=False, uac_uiaccess=False, + win_no_prefer_redirects=False, win_private_assemblies=False, + collect_submodules=None, collect_binaries=None, collect_data=None, + collect_all=None, copy_metadata=None, **kwargs): + # 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 tilde to user's home directory. + specpath = expand_path(specpath) + # If cwd is the root directory of PyInstaller then generate .spec file + # subdirectory ./appname/. + if specpath == HOMEPATH: + specpath = os.path.join(HOMEPATH, name) + # Create directory tree if missing. + if not os.path.exists(specpath): + os.makedirs(specpath) + + # Append specpath to PYTHONPATH - where to look for additional Python modules. + pathex = pathex or [] + pathex = pathex[:] + pathex.append(specpath) + + # Handle additional EXE options. + exe_options = '' + if version_file: + exe_options = "%s, version='%s'" % (exe_options, quote_win_filepath(version_file)) + if uac_admin: + exe_options = "%s, uac_admin=%s" % (exe_options, 'True') + if uac_uiaccess: + exe_options = "%s, uac_uiaccess=%s" % (exe_options, 'True') + if icon_file: + # Icon file for Windows. + # On Windows default icon is embedded in the bootloader executable. + exe_options = "%s, icon='%s'" % (exe_options, quote_win_filepath(icon_file)) + # Icon file for OSX. + # We need to encapsulate it into apostrofes. + icon_file = "'%s'" % icon_file + else: + # On OSX default icon has to be copied into the .app bundle. + # The the text value 'None' means - use default icon. + icon_file = 'None' + + if bundle_identifier: + # We need to encapsulate it into apostrofes. + bundle_identifier = "'%s'" % bundle_identifier + + if manifest: + if "<" in manifest: + # Assume XML string + exe_options = "%s, manifest='%s'" % (exe_options, manifest.replace("'", "\\'")) + else: + # Assume filename + exe_options = "%s, manifest='%s'" % (exe_options, quote_win_filepath(manifest)) + if resources: + resources = list(map(quote_win_filepath, resources)) + exe_options = "%s, resources=%s" % (exe_options, repr(resources)) + + hiddenimports = hiddenimports or [] + upx_exclude = upx_exclude or [] + + # 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)) + + if key: + # Tries to import tinyaes since we need it for bytecode obfuscation. + try: + import tinyaes # noqa: F401 (test import) + except ImportError: + logger.error('We need tinyaes to use byte-code obfuscation but we ' + 'could not') + logger.error('find it. You can install it with pip by running:') + logger.error(' pip install tinyaes') + sys.exit(1) + cipher_init = cipher_init_template % {'key': key} + else: + cipher_init = cipher_absent_template + + # 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 + ) + + d = { + 'scripts': scripts, + 'pathex': pathex, + 'binaries': preamble.binaries, + 'datas': preamble.datas, + 'hiddenimports': preamble.hiddenimports, + 'preamble': preamble.content, + 'name': name, + 'noarchive': 'noarchive' in debug, + 'options': [('v', None, 'OPTION')] if 'imports' in debug else [], + '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, + 'cipher_init': cipher_init, + # Directory with additional custom import hooks. + 'hookspath': hookspath, + # List with custom runtime hook files. + 'runtime_hooks': runtime_hooks or [], + # List of modules/pakages to ignore. + 'excludes': excludes or [], + # only Windows and Mac OS X distinguish windowed and console apps + 'console': console, + # Icon filename. Only OSX uses this item. + 'icon': icon_file, + # .app bundle identifier. Only OSX uses this item. + 'bundle_identifier': bundle_identifier, + # Windows assembly searching options + 'win_no_prefer_redirects': win_no_prefer_redirects, + 'win_private_assemblies': win_private_assemblies, + } + + # Write down .spec file to filesystem. + specfnm = os.path.join(specpath, name + '.spec') + with open_file(specfnm, 'w', encoding='utf-8') as specfile: + if onefile: + specfile.write(onefiletmplt % d) + # For OSX create .app bundle. + if is_darwin and not console: + specfile.write(bundleexetmplt % d) + else: + specfile.write(onedirtmplt % d) + # For OSX create .app bundle. + if is_darwin and not console: + specfile.write(bundletmplt % d) + + return specfnm diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/osx.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/osx.py new file mode 100644 index 0000000000000000000000000000000000000000..3020097bb1c5d63a5d5a4ead40627b3b888976ee --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/osx.py @@ -0,0 +1,232 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 plistlib +import shutil +from ..compat import is_darwin +from .api import EXE, COLLECT +from .datastruct import Target, TOC, logger +from .utils import _check_path_overlap, _rmtree, add_suffix_to_extensions, checkCache + + + +class BUNDLE(Target): + def __init__(self, *args, **kws): + from ..config import CONF + + # BUNDLE only has a sense under Mac OS X, it's a noop on other platforms + if not is_darwin: + return + + # get a path to a .icns icon for the app bundle. + self.icon = kws.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) + # ensure icon path is absolute + self.icon = os.path.abspath(self.icon) + + Target.__init__(self) + + # .app bundle is created in DISTPATH. + self.name = kws.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] + self.version = kws.get("version", "0.0.0") + self.toc = TOC() + self.strip = False + self.upx = False + self.console = True + + # .app bundle identifier for Code Signing + self.bundle_identifier = kws.get('bundle_identifier') + if not self.bundle_identifier: + # Fallback to appname. + self.bundle_identifier = self.appname + + self.info_plist = kws.get('info_plist', None) + + for arg in args: + if isinstance(arg, EXE): + self.toc.append((os.path.basename(arg.name), arg.name, arg.typ)) + self.toc.extend(arg.dependencies) + self.strip = arg.strip + self.upx = arg.upx + self.upx_exclude = arg.upx_exclude + self.console = arg.console + elif isinstance(arg, TOC): + self.toc.extend(arg) + # TOC doesn't have a strip or upx attribute, so there is no way for us to + # tell which cache we should draw from. + elif isinstance(arg, COLLECT): + self.toc.extend(arg.toc) + self.strip = arg.strip_binaries + self.upx = arg.upx_binaries + self.upx_exclude = arg.upx_exclude + self.console = arg.console + else: + logger.info("unsupported entry %s", arg.__class__.__name__) + # Now, find values for app filepath (name), app name (appname), and name + # of the actual executable (exename) from the first EXECUTABLE item in + # toc, which might have come from a COLLECT too (not from an EXE). + for inm, name, typ in self.toc: + if typ == "EXECUTABLE": + self.exename = name + break + 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, since it will clean the output + # directory anyway to make sure there is no existing cruft accumulating + return 1 + + def assemble(self): + 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")) + + # Copy icns icon to Resources directory. + if os.path.exists(self.icon): + shutil.copy(self.icon, os.path.join(self.name, 'Contents', 'Resources')) + else: + logger.warning("icon not found %s", 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 OS X areas + # like Keychain. + # + # The identifier used for signing must be globally unique. The usal + # 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, + + # Fix for #156 - 'MacOS' must be in the name - not sure why + "CFBundleExecutable": 'MacOS/%s' % 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) + + links = [] + toc = add_suffix_to_extensions(self.toc) + for inm, fnm, typ in toc: + # Copy files from cache. This ensures that are used files with relative + # paths to dynamic library dependencies (@executable_path) + base_path = inm.split('/', 1)[0] + if typ in ('EXTENSION', 'BINARY'): + fnm = checkCache(fnm, strip=self.strip, upx=self.upx, + upx_exclude=self.upx_exclude, dist_nm=inm) + # Add most data files to a list for symlinking later. + if typ == 'DATA' and base_path not in ('PySide2', 'PyQt5'): + links.append((inm, fnm)) + else: + tofnm = os.path.join(self.name, "Contents", "MacOS", inm) + todir = os.path.dirname(tofnm) + if not os.path.exists(todir): + os.makedirs(todir) + if os.path.isdir(fnm): + # beacuse shutil.copy2() is the default copy function + # for shutil.copytree, this will also copy file metadata + shutil.copytree(fnm, tofnm) + else: + shutil.copy(fnm, tofnm) + + logger.info('moving BUNDLE data files to Resource directory') + + # Mac OS X Code Signing does not work when .app bundle contains + # data files in dir ./Contents/MacOS. + # + # Put all data files in ./Resources and create symlinks in ./MacOS. + bin_dir = os.path.join(self.name, 'Contents', 'MacOS') + res_dir = os.path.join(self.name, 'Contents', 'Resources') + for inm, fnm in links: + tofnm = os.path.join(res_dir, inm) + todir = os.path.dirname(tofnm) + if not os.path.exists(todir): + os.makedirs(todir) + if os.path.isdir(fnm): + # beacuse shutil.copy2() is the default copy function + # for shutil.copytree, this will also copy file metadata + shutil.copytree(fnm, tofnm) + else: + shutil.copy(fnm, tofnm) + base_path = os.path.split(inm)[0] + if base_path: + if not os.path.exists(os.path.join(bin_dir, inm)): + path = '' + for part in iter(base_path.split(os.path.sep)): + # Build path from previous path and the next part of the base path + path = os.path.join(path, part) + try: + relative_source_path = os.path.relpath(os.path.join(res_dir, path), + os.path.split(os.path.join(bin_dir, path))[0]) + dest_path = os.path.join(bin_dir, path) + os.symlink(relative_source_path, dest_path) + break + except FileExistsError: + pass + if not os.path.exists(os.path.join(bin_dir, inm)): + relative_source_path = os.path.relpath(os.path.join(res_dir, inm), + os.path.split(os.path.join(bin_dir, inm))[0]) + dest_path = os.path.join(bin_dir, inm) + os.symlink(relative_source_path, dest_path) + else: # If path is empty, e.g., a top level file, try to just symlink the file + os.symlink(os.path.relpath(os.path.join(res_dir, inm), + os.path.split(os.path.join(bin_dir, inm))[0]), + os.path.join(bin_dir, inm)) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/templates.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/templates.py new file mode 100644 index 0000000000000000000000000000000000000000..35af6c13ddd553124eefeeb57eda0dfa50a9e54b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/templates.py @@ -0,0 +1,107 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 +%(cipher_init)s + +a = Analysis(%(scripts)s, + pathex=%(pathex)s, + binaries=%(binaries)s, + datas=%(datas)s, + hiddenimports=%(hiddenimports)s, + hookspath=%(hookspath)r, + runtime_hooks=%(runtime_hooks)r, + excludes=%(excludes)s, + win_no_prefer_redirects=%(win_no_prefer_redirects)s, + win_private_assemblies=%(win_private_assemblies)s, + cipher=block_cipher, + noarchive=%(noarchive)s) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + %(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 %(exe_options)s) +""" + +onedirtmplt = """# -*- mode: python ; coding: utf-8 -*- +%(preamble)s +%(cipher_init)s + +a = Analysis(%(scripts)s, + pathex=%(pathex)s, + binaries=%(binaries)s, + datas=%(datas)s, + hiddenimports=%(hiddenimports)s, + hookspath=%(hookspath)r, + runtime_hooks=%(runtime_hooks)r, + excludes=%(excludes)s, + win_no_prefer_redirects=%(win_no_prefer_redirects)s, + win_private_assemblies=%(win_private_assemblies)s, + cipher=block_cipher, + noarchive=%(noarchive)s) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + %(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 %(exe_options)s) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=%(strip)s, + upx=%(upx)s, + upx_exclude=%(upx_exclude)s, + name='%(name)s') +""" + +cipher_absent_template = """ +block_cipher = None +""" + +cipher_init_template = """ +block_cipher = pyi_crypto.PyiBlockCipher(key=%(key)r) +""" + +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) +""" diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/toc_conversion.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/toc_conversion.py new file mode 100644 index 0000000000000000000000000000000000000000..92096a1f4aac1b471b9d97b668495f0ddab45d1d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/toc_conversion.py @@ -0,0 +1,172 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 zipfile +import pkg_resources +from ..depend.utils import get_path_to_egg +from .datastruct import TOC, Tree +from .. import log as logging +from ..compat import ALL_SUFFIXES + +logger = logging.getLogger(__name__) + +# create a list of excludes suitable for Tree. +PY_IGNORE_EXTENSIONS = set( + ['*' + x for x in ALL_SUFFIXES] + + # Exclude EGG-INFO, too, as long as we do not have a way to hold several + # in one archive + ['EGG-INFO'] +) + + +class DependencyProcessor(object): + """ + Class to convert final module dependency graph into TOC data structures. + TOC data structures are suitable for creating the final executable. + """ + def __init__(self, graph, additional_files): + self._binaries = set() + self._datas = set() + self._distributions = set() + self.__seen_distribution_paths = set() + # Include files that were found by hooks. + # graph.flatten() should include only those modules that are reachable + # from top-level script. + for node in graph.flatten(start=graph._top_script_node): + # Update 'binaries', 'datas' + name = node.identifier + if name in additional_files: + self._binaries.update(additional_files.binaries(name)) + self._datas.update(additional_files.datas(name)) + # Any module can belong to a single distribution + self._distributions.update(self._get_distribution_for_node(node)) + + + def _get_distribution_for_node(self, node): + """Get the distribution a module belongs to. + + Bug: This currently only handles packages in eggs. + """ + # TODO: Modulegraph could flag a module as residing in a zip file + # TODO add support for single modules in eggs (e.g. mock-1.0.1) + # TODO add support for egg-info: + # TODO add support for wheels (dist-info) + # + # TODO add support for unpacked eggs and for new .whl packages. + # Wheels: + # .../site-packages/pip/ # It seams this has to be a package + # .../site-packages/pip-6.1.1.dist-info + # Unzipped Eggs: + # .../site-packages/mock.py # this may be a single module, too! + # .../site-packages/mock-1.0.1-py2.7.egg-info + # Unzipped Eggs (I asume: multiple-versions externaly managed): + # .../site-packages/pyPdf-1.13-py2.6.egg/pyPdf/ + # .../site-packages/pyPdf-1.13-py2.6.egg/EGG_INFO + # Zipped Egg: + # .../site-packages/zipped.egg/zipped_egg/ + # .../site-packages/zipped.egg/EGG_INFO + modpath = node.filename + if not modpath: + # e.g. namespace-package + return [] + # TODO: add other ways to get a distribution path + distpath = get_path_to_egg(modpath) + if not distpath or distpath in self.__seen_distribution_paths: + # no egg or already handled + return [] + self.__seen_distribution_paths.add(distpath) + dists = list(pkg_resources.find_distributions(distpath)) + assert len(dists) == 1 + dist = dists[0] + dist._pyinstaller_info = info = { + 'zipped': zipfile.is_zipfile(dist.location), + 'egg': True, # TODO when supporting other types + 'zip-safe': dist.has_metadata('zip-safe'), + } + return dists + + + # Public methods. + + def make_binaries_toc(self): + # TODO create a real TOC when handling of more files is added. + return [(x, y, 'BINARY') for x, y in self._binaries] + + def make_datas_toc(self): + toc = TOC((x, y, 'DATA') for x, y in self._datas) + for dist in self._distributions: + if (dist._pyinstaller_info['egg'] and + not dist._pyinstaller_info['zipped'] and + not dist._pyinstaller_info['zip-safe']): + # this is a un-zipped, not-zip-safe egg + toplevel = dist.get_metadata('top_level.txt').strip() + basedir = dist.location + if toplevel: + os.path.join(basedir, toplevel) + tree = Tree(dist.location, excludes=PY_IGNORE_EXTENSIONS) + toc.extend(tree) + return toc + + + def make_zipfiles_toc(self): + # TODO create a real TOC when handling of more files is added. + toc = [] + for dist in self._distributions: + if (dist._pyinstaller_info['zipped'] and + not dist._pyinstaller_info['egg']): + # Hmm, this should never happen as normal zip-files + # are not associated with an distribution, are they? + toc.append(("eggs/" + os.path.basename(dist.location), + dist.location, 'ZIPFILE')) + return toc + + + @staticmethod + def __collect_data_files_from_zip(zipfilename): + # 'PyInstaller.config' cannot be imported as other top-level modules. + from ..config import CONF + workpath = os.path.join(CONF['workpath'], os.path.basename(zipfilename)) + try: + os.makedirs(workpath) + except OSError as e: + import errno + if e.errno != errno.EEXIST: + raise + # TODO extract only those file which whould then be included + with zipfile.ZipFile(zipfilename) as zfh: + zfh.extractall(workpath) + return Tree(workpath, excludes=PY_IGNORE_EXTENSIONS) + + + def make_zipped_data_toc(self): + toc = TOC() + logger.debug('Looking for egg data files...') + for dist in self._distributions: + if dist._pyinstaller_info['egg']: + # TODO: check in docu if top_level.txt always exists + toplevel = dist.get_metadata('top_level.txt').strip() + if dist._pyinstaller_info['zipped']: + # this is a zipped egg + tree = self.__collect_data_files_from_zip(dist.location) + toc.extend(tree) + elif dist._pyinstaller_info['zip-safe']: + # this is a un-zipped, zip-safe egg + basedir = dist.location + if toplevel: + os.path.join(basedir, toplevel) + tree = Tree(dist.location, excludes=PY_IGNORE_EXTENSIONS) + toc.extend(tree) + else: + # this is a un-zipped, not-zip-safe egg, handled in + # make_datas_toc() + pass + return toc diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/building/utils.py b/3rdparty/pyinstaller-4.3/PyInstaller/building/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..d73a4b28f9e581cde30987082ad65f023dfbd48a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/building/utils.py @@ -0,0 +1,707 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + + +#--- functions for checking guts --- +# NOTE: By GUTS it is meant intermediate files and data structures that +# PyInstaller creates for bundling files and creating final executable. +import glob +import hashlib +import os +import os.path +import pkgutil +import platform +import shutil +import sys + +import struct + +from PyInstaller.config import CONF +from .. import compat +from ..compat import is_darwin, is_win, EXTENSION_SUFFIXES, \ + open_file, is_py37, is_cygwin +from ..depend import dylib +from ..depend.bindepend import match_binding_redirect +from ..utils import misc +from ..utils.misc import load_py_data_struct, save_py_data_struct +from .. import log as logging + +if is_win: + from ..utils.win32 import winmanifest, winresource + from ..utils.win32.versioninfo import pefile_check_control_flow_guard + +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, old, new, last_build): + """ + rebuild is required if values differ + """ + if old != new: + logger.info("Building because %s changed", attr) + return True + return False + + +def _check_guts_toc_mtime(attr, old, toc, last_build, pyc=0): + """ + rebuild is required if mtimes of files listed in old toc are newer + than last_build + + if pyc=1, check for .py files, too + + Use this for calculated/analysed values read from cache. + """ + for (nm, fnm, typ) in old: + if misc.mtime(fnm) > last_build: + logger.info("Building because %s changed", fnm) + return True + elif pyc and misc.mtime(fnm[:-1]) > last_build: + logger.info("Building because %s changed", fnm[:-1]) + return True + return False + + +def _check_guts_toc(attr, old, toc, last_build, pyc=0): + """ + rebuild is required if either toc content changed or mtimes of + files listed in old toc are newer than last_build + + if pyc=1, check for .py files, too + + Use this for input parameters. + """ + return (_check_guts_eq(attr, old, toc, last_build) + or _check_guts_toc_mtime(attr, old, toc, last_build, pyc=pyc)) + + +#--- + +def add_suffix_to_extensions(toc): + """ + Returns a new TOC with proper library suffix for EXTENSION items. + """ + # TODO: Fix this recursive import + from .datastruct import TOC + new_toc = TOC() + for inm, fnm, typ in toc: + if typ == 'EXTENSION': + # Change the dotted name into a relative path. This places C + # extensions in the Python-standard location. + inm = inm.replace('.', os.sep) + # In some rare cases extension might already contain a suffix. + # Skip it in this case. + if os.path.splitext(inm)[1] not in EXTENSION_SUFFIXES: + # Determine the base name of the file. + base_name = os.path.basename(inm) + assert '.' not in base_name + # Use this file's existing extension. For extensions such as + # ``libzmq.cp36-win_amd64.pyd``, we can't use + # ``os.path.splitext``, which would give only the ```.pyd`` part + # of the extension. + inm = inm + os.path.basename(fnm)[len(base_name):] + + elif typ == 'DEPENDENCY': + # Use the suffix from the filename. + # TODO Verify what extensions are by DEPENDENCIES. + binext = os.path.splitext(fnm)[1] + if not os.path.splitext(inm)[1] == binext: + inm = inm + binext + new_toc.append((inm, fnm, typ)) + return new_toc + +def applyRedirects(manifest, redirects): + """ + Apply the binding redirects specified by 'redirects' to the dependent assemblies + of 'manifest'. + + :param manifest: + :type manifest: + :param redirects: + :type redirects: + :return: + :rtype: + """ + redirecting = False + for binding in redirects: + for dep in manifest.dependentAssemblies: + if match_binding_redirect(dep, binding): + logger.info("Redirecting %s version %s -> %s", + binding.name, dep.version, binding.newVersion) + dep.version = binding.newVersion + redirecting = True + return redirecting + + +def checkCache(fnm, strip=False, upx=False, upx_exclude=None, dist_nm=None): + """ + Cache prevents preprocessing binary files again and again. + + 'dist_nm' Filename relative to dist directory. We need it on Mac + to determine level of paths for @loader_path like + '@loader_path/../../' for qt4 plugins. + """ + from ..config import CONF + # On darwin a cache is required anyway to keep the libaries + # with relative install names. Caching on darwin does not work + # since we need to modify binary headers to use relative paths + # to dll depencies and starting with '@loader_path'. + if not strip and not upx and not is_darwin and not is_win: + return fnm + + if dist_nm is not None and ":" in dist_nm: + # A file embedded in another pyinstaller build via multipackage + # No actual file exists to process + return fnm + + if strip: + strip = True + else: + strip = False + upx_exclude = upx_exclude or [] + upx = (upx and (is_win or is_cygwin) and + os.path.normcase(os.path.basename(fnm)) not in upx_exclude) + + # Load cache index + # Make cachedir per Python major/minor version. + # This allows parallel building of executables with different + # Python versions as one user. + pyver = ('py%d%s') % (sys.version_info[0], sys.version_info[1]) + arch = platform.architecture()[0] + cachedir = os.path.join(CONF['cachedir'], 'bincache%d%d_%s_%s' % (strip, upx, pyver, arch)) + if not os.path.exists(cachedir): + os.makedirs(cachedir) + cacheindexfn = os.path.join(cachedir, "index.dat") + if os.path.exists(cacheindexfn): + try: + cache_index = load_py_data_struct(cacheindexfn) + except Exception as e: + # tell the user they may want to fix their cache + # .. however, don't delete it for them; if it keeps getting + # corrupted, we'll never find out + logger.warn("pyinstaller bincache may be corrupted; " + "use pyinstaller --clean to fix") + raise + else: + cache_index = {} + + # Verify if the file we're looking for is present in the cache. + # Use the dist_mn if given to avoid different extension modules + # sharing the same basename get corrupted. + if dist_nm: + basenm = os.path.normcase(dist_nm) + else: + basenm = os.path.normcase(os.path.basename(fnm)) + + # Binding redirects should be taken into account to see if the file + # needs to be reprocessed. The redirects may change if the versions of dependent + # manifests change due to system updates. + redirects = CONF.get('binding_redirects', []) + digest = cacheDigest(fnm, redirects) + cachedfile = os.path.join(cachedir, basenm) + cmd = None + if basenm in cache_index: + if digest != cache_index[basenm]: + os.remove(cachedfile) + else: + # On Mac OS X we need relative paths to dll dependencies + # starting with @executable_path. We may also need to strip + # (invalidated) signature from collected shared libraries. + if is_darwin: + dylib.mac_set_relative_dylib_deps(cachedfile, dist_nm) + dylib.mac_strip_signature(cachedfile, dist_nm) + return cachedfile + + + # Optionally change manifest and its deps to private assemblies + if fnm.lower().endswith(".manifest"): + manifest = winmanifest.Manifest() + manifest.filename = fnm + with open(fnm, "rb") as f: + manifest.parse_string(f.read()) + if CONF.get('win_private_assemblies', False): + if manifest.publicKeyToken: + logger.info("Changing %s into private assembly", os.path.basename(fnm)) + manifest.publicKeyToken = None + for dep in manifest.dependentAssemblies: + # Exclude common-controls which is not bundled + if dep.name != "Microsoft.Windows.Common-Controls": + dep.publicKeyToken = None + + applyRedirects(manifest, redirects) + + manifest.writeprettyxml(cachedfile) + return cachedfile + + if upx: + if strip: + fnm = checkCache(fnm, strip=True, upx=False) + # We meed to avoid using UPX with Windows DLLs that have Control + # Flow Guard enabled, as it breaks them. + if is_win and pefile_check_control_flow_guard(fnm): + logger.info('Disabling UPX for %s due to CFG!', fnm) + else: + bestopt = "--best" + # FIXME: Linux builds of UPX do not seem to contain LZMA + # (they assert out). A better configure-time check is due. + if CONF["hasUPX"] >= (3,) and os.name == "nt": + bestopt = "--lzma" + + upx_executable = "upx" + if CONF.get('upx_dir'): + upx_executable = os.path.join(CONF['upx_dir'], upx_executable) + cmd = [upx_executable, bestopt, "-q", cachedfile] + else: + if strip: + strip_options = [] + if is_darwin: + # The default strip behaviour breaks some shared libraries + # under Mac OSX. + # -S = strip only debug symbols. + strip_options = ["-S"] + cmd = ["strip"] + strip_options + [cachedfile] + + if not os.path.exists(os.path.dirname(cachedfile)): + os.makedirs(os.path.dirname(cachedfile)) + # There are known some issues with 'shutil.copy2' on Mac OS X 10.11 + # with copying st_flags. Issue #1650. + # 'shutil.copy' copies also permission bits and it should be sufficient for + # PyInstalle purposes. + shutil.copy(fnm, cachedfile) + # TODO find out if this is still necessary when no longer using shutil.copy2() + if hasattr(os, 'chflags'): + # Some libraries on FreeBSD have immunable flag (libthr.so.3, for example) + # If flags still remains, os.chmod will failed with: + # OSError: [Errno 1] Operation not permitted. + try: + os.chflags(cachedfile, 0) + except OSError: + pass + os.chmod(cachedfile, 0o755) + + if os.path.splitext(fnm.lower())[1] in (".pyd", ".dll"): + # When shared assemblies are bundled into the app, they may optionally be + # changed into private assemblies. + try: + res = winmanifest.GetManifestResources(os.path.abspath(cachedfile)) + except winresource.pywintypes.error as e: + if e.args[0] == winresource.ERROR_BAD_EXE_FORMAT: + # Not a win32 PE file + pass + else: + logger.error(os.path.abspath(cachedfile)) + raise + else: + if winmanifest.RT_MANIFEST in res and len(res[winmanifest.RT_MANIFEST]): + for name in res[winmanifest.RT_MANIFEST]: + for language in res[winmanifest.RT_MANIFEST][name]: + try: + manifest = winmanifest.Manifest() + manifest.filename = ":".join([cachedfile, + str(winmanifest.RT_MANIFEST), + str(name), + str(language)]) + manifest.parse_string(res[winmanifest.RT_MANIFEST][name][language], + False) + except Exception as exc: + logger.error("Cannot parse manifest resource %s, " + "%s", name, language) + logger.error("From file %s", cachedfile, exc_info=1) + else: + # optionally change manifest to private assembly + private = CONF.get('win_private_assemblies', False) + if private: + if manifest.publicKeyToken: + logger.info("Changing %s into a private assembly", + os.path.basename(fnm)) + manifest.publicKeyToken = None + + # Change dep to private assembly + for dep in manifest.dependentAssemblies: + # Exclude common-controls which is not bundled + if dep.name != "Microsoft.Windows.Common-Controls": + dep.publicKeyToken = None + redirecting = applyRedirects(manifest, redirects) + if redirecting or private: + try: + manifest.update_resources(os.path.abspath(cachedfile), + [name], + [language]) + except Exception as e: + logger.error(os.path.abspath(cachedfile)) + raise + + if cmd: + logger.info("Executing - " + ' '.join(cmd)) + # terminates if execution fails + compat.exec_command(*cmd) + + # update cache index + cache_index[basenm] = digest + save_py_data_struct(cacheindexfn, cache_index) + + # On Mac OS X we need relative paths to dll dependencies + # starting with @executable_path. We may also need to strip + # (invalidated) signature from collected shared libraries. + if is_darwin: + dylib.mac_set_relative_dylib_deps(cachedfile, dist_nm) + dylib.mac_strip_signature(cachedfile, dist_nm) + return cachedfile + + +def cacheDigest(fnm, redirects): + hasher = hashlib.md5() + with open(fnm, "rb") as f: + for chunk in iter(lambda: f.read(16 * 1024), b""): + hasher.update(chunk) + if redirects: + redirects = str(redirects).encode('utf-8') + hasher.update(redirects) + digest = bytearray(hasher.digest()) + return 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 ..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 ..config import CONF + if CONF['noconfirm']: + choice = 'y' + elif sys.stdout.isatty(): + choice = compat.stdin_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 ist he 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: + if not trg_root_dir: + raise SystemExit("Empty DEST not allowed when adding binary " + "and data files. " + "Maybe you want to used %r.\nCaused by %r." % + (os.curdir, src_root_path_or_glob)) + # 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 os.path.isfile(src_root_path_or_glob): + src_root_paths = [src_root_path_or_glob] + else: + # List of the absolute paths of all source paths matching the + # current glob. + src_root_paths = glob.glob(src_root_path_or_glob) + + if not src_root_paths: + msg = 'Unable to find "%s" when adding binary and data files.' % ( + src_root_path_or_glob) + # on Debian/Ubuntu, missing pyconfig.h files can be fixed with + # installing python-dev + if src_root_path_or_glob.endswith("pyconfig.h"): + msg += """This would mean your Python installation doesn't +come with proper library files. This usually happens by missing development +package, or unsuitable build parameters of Python installation. +* On Debian/Ubuntu, you would need to install Python development packages + * apt-get install python3-dev + * apt-get install python-dev +* If you're building Python by yourself, please rebuild your Python with +`--enable-shared` (or, `--enable-framework` on Darwin) +""" + raise SystemExit(msg) + + 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"). + trg_dir = os.path.normpath(os.path.join( + trg_root_dir, + os.path.relpath(src_dir, src_root_path))) + + 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 _load_code(modname, filename): + path_item = os.path.dirname(filename) + if os.path.basename(filename).startswith('__init__.py'): + # this is a package + path_item = os.path.dirname(path_item) + if os.path.basename(path_item) == '__pycache__': + path_item = os.path.dirname(path_item) + importer = pkgutil.get_importer(path_item) + package, _, modname = modname.rpartition('.') + + if hasattr(importer, 'find_loader'): + loader, portions = importer.find_loader(modname) + else: + loader = importer.find_module(modname) + + logger.debug('Compiling %s', filename) + if loader and hasattr(loader, 'get_code'): + return loader.get_code(modname) + else: + # Just as ``python foo.bar`` will read and execute statements in + # ``foo.bar``, even though it lacks the ``.py`` extension, so + # ``pyinstaller foo.bar`` should also work. However, Python's import + # machinery doesn't load files without a ``.py`` extension. So, use + # ``compile`` instead. + # + # On a side note, neither the Python 2 nor Python 3 calls to + # ``pkgutil`` and ``find_module`` above handle modules ending in + # ``.pyw``, even though ``imp.find_module`` and ``import `` both + # work. This code supports ``.pyw`` files. + + # Open the source file in binary mode and allow the `compile()` call to + # detect the source encoding. + with open_file(filename, 'rb') as f: + source = f.read() + return compile(source, filename, 'exec') + +def get_code_object(modname, filename): + """ + Get the code-object for a module. + + This is a extra-simple version for compiling a module. It's + not worth spending more effort here, as it is only used in the + rare case if outXX-Analysis.toc exists, but outXX-PYZ.toc does + not. + """ + + try: + if filename in ('-', None): + # This is a NamespacePackage, modulegraph marks them + # by using the filename '-'. (But wants to use None, + # so check for None, too, to be forward-compatible.) + logger.debug('Compiling namespace package %s', modname) + txt = '#\n' + return compile(txt, filename, 'exec') + else: + logger.debug('Compiling %s', filename) + co = _load_code(modname, filename) + if not co: + raise ValueError("Module file %s is missing" % filename) + return co + except SyntaxError as e: + print("Syntax error in ", filename) + print(e.args) + raise + + +def strip_paths_in_code(co, new_filename=None): + + # Paths to remove from filenames embedded in code objects + replace_paths = sys.path + CONF['pathex'] + # Make sure paths end with os.sep and the longest paths are first + replace_paths = sorted((os.path.join(f, '') for f in replace_paths), + key=len, reverse=True) + + if new_filename is None: + original_filename = os.path.normpath(co.co_filename) + for f in replace_paths: + if original_filename.startswith(f): + new_filename = original_filename[len(f):] + break + + else: + return co + + code_func = type(co) + + consts = tuple( + strip_paths_in_code(const_co, new_filename) + if isinstance(const_co, code_func) else const_co + for const_co in co.co_consts + ) + + if hasattr(co, 'replace'): # is_py38 + return co.replace(co_consts=consts, co_filename=new_filename) + elif hasattr(co, 'co_kwonlyargcount'): + # co_kwonlyargcount was added in some version of Python 3 + return code_func(co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, co.co_stacksize, + co.co_flags, co.co_code, consts, co.co_names, + co.co_varnames, new_filename, co.co_name, + co.co_firstlineno, co.co_lnotab, + co.co_freevars, co.co_cellvars) + else: + return code_func(co.co_argcount, co.co_nlocals, co.co_stacksize, + co.co_flags, co.co_code, consts, co.co_names, + co.co_varnames, new_filename, co.co_name, + co.co_firstlineno, co.co_lnotab, + co.co_freevars, co.co_cellvars) + + +def fake_pyc_timestamp(buf): + """ + Reset the timestamp from a .pyc-file header to a fixed value. + + This enables deterministic builds without having to set pyinstaller + source metadata (mtime) since that changes the pyc-file contents. + + _buf_ must at least contain the full pyc-file header. + """ + assert buf[:4] == compat.BYTECODE_MAGIC, \ + "Expected pyc magic {}, got {}".format(compat.BYTECODE_MAGIC, buf[:4]) + start, end = 4, 8 + if is_py37: + # see https://www.python.org/dev/peps/pep-0552/ + (flags,) = struct.unpack_from(">I", buf, 4) + if flags & 1: + # We are in the future and hash-based pyc-files are used, so + # clear "check_source" flag, since there is no source + buf[4:8] = struct.pack(">I", flags ^ 2) + return buf + else: + # no hash-based pyc-file, timestamp is the next field + start, end = 8, 12 + + ts = b'pyi0' # So people know where this comes from + return buf[:start] + ts + buf[end:] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/compat.py b/3rdparty/pyinstaller-4.3/PyInstaller/compat.py new file mode 100644 index 0000000000000000000000000000000000000000..7cfc6ed5f19ee3e2bc7781a33f6870753254a813 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/compat.py @@ -0,0 +1,729 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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. +""" + +import os +import platform +import site +import subprocess +import sys +import errno +import importlib.machinery +from .exceptions import ExecCommandFailed + +# Copied from https://docs.python.org/3/library/platform.html#cross-platform. +is_64bits = 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_win = sys.platform.startswith('win') +is_win_10 = is_win and (platform.win32_ver()[0] == '10') +is_cygwin = sys.platform == 'cygwin' +is_darwin = sys.platform == 'darwin' # Mac OS X + +# Unix platforms +is_linux = sys.platform.startswith('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) +# Mac OS X 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 + +# On different platforms is different file for dynamic python library. +# TODO: When removing support for is_py37, the "m" variants can be +# removed, see +# +_pyver = sys.version_info[:2] +if is_win or is_cygwin: + PYDYLIB_NAMES = {'python%d%d.dll' % _pyver, + 'libpython%d%d.dll' % _pyver, + 'libpython%d%dm.dll' % _pyver, + 'libpython%d.%d.dll' % _pyver, + 'libpython%d.%dm.dll' % _pyver} # For MSYS2 environment +elif is_darwin: + # libpython%d.%dm.dylib for Conda virtual environment installations + PYDYLIB_NAMES = {'Python', '.Python', + 'Python%d' % _pyver[0], + 'libpython%d.%d.dylib' % _pyver, + 'libpython%d.%dm.dylib' % _pyver} +elif is_aix: + # Shared libs on AIX may be archives with shared object members, + # hence the ".a" suffix. However, starting with python 2.7.11 + # libpython?.?.so and Python3 libpython?.?m.so files are produced. + PYDYLIB_NAMES = {'libpython%d.%d.a' % _pyver, + 'libpython%d.%dm.a' % _pyver, + 'libpython%d.%d.so' % _pyver, + 'libpython%d.%dm.so' % _pyver} +elif is_freebsd: + PYDYLIB_NAMES = {'libpython%d.%d.so.1' % _pyver, + 'libpython%d.%dm.so.1' % _pyver, + 'libpython%d.%d.so.1.0' % _pyver, + 'libpython%d.%dm.so.1.0' % _pyver} +elif is_openbsd: + PYDYLIB_NAMES = {'libpython%d.%d.so.0.0' % _pyver, + 'libpython%d.%dm.so.0.0' % _pyver} +elif is_hpux: + PYDYLIB_NAMES = {'libpython%d.%d.so' % _pyver} +elif is_unix: + # Other *nix platforms. + # Python 2 .so library on Linux is: libpython2.7.so.1.0 + # Python 3 .so library on Linux is: libpython3.2mu.so.1.0, libpython3.3m.so.1.0 + PYDYLIB_NAMES = {'libpython%d.%d.so.1.0' % _pyver, + 'libpython%d.%dm.so.1.0' % _pyver, + 'libpython%d.%dmu.so.1.0' % _pyver, + 'libpython%d.%dm.so' % _pyver} +else: + raise SystemExit('Your platform is not yet supported. ' + 'Please define constant PYDYLIB_NAMES for your platform.') + + +# Function with which to open files. +open_file = open +text_read_mode = 'r' + +# In Python 3 built-in function raw_input() was renamed to just 'input()'. +stdin_input = input + +# Safe repr that always outputs ascii +safe_repr = ascii + +# String types to replace `isinstance(foo, str)` +# Use `isinstance(foo, string_types)` instead. +string_types = str + +# Correct extension ending: 'c' or 'o' +if __debug__: + PYCO = 'c' +else: + PYCO = 'o' + +# Options for python interpreter when invoked in a subprocess. +if __debug__: + # Python started *without* -O + _PYOPTS = '' +else: + _PYOPTS = '-O' + + +# 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 = 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')) + +# In Python 3.4 module 'imp' is deprecated and there is another way how +# to obtain magic value. +import importlib.util +BYTECODE_MAGIC = importlib.util.MAGIC_NUMBER + + +# List of suffixes for Python C extension modules. +from importlib.machinery import EXTENSION_SUFFIXES, all_suffixes +ALL_SUFFIXES = all_suffixes() + + +# In Python 3 'Tkinter' has been made lowercase - 'tkinter'. +modname_tkinter = 'tkinter' + + +# 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: + try: + from win32ctypes.pywin32 import pywintypes # noqa: F401 + from win32ctypes.pywin32 import win32api + except ImportError: + # This environment variable is set by setup.py + # - It's not an error for pywin32 to not be installed at that point + if not os.environ.get('PYINSTALLER_NO_PYWIN32_FAILURE'): + raise SystemExit('PyInstaller cannot check for assembly dependencies.\n' + 'Please install pywin32-ctypes.\n\n' + 'pip install pywin32-ctypes\n') + + +# 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 +architecture = '64bit' if sys.maxsize > 2**32 and is_darwin else \ + '32bit' if is_darwin else platform.architecture()[0] + +system = platform.system() + +# Machine suffix for bootloader. +# PyInstaller is reported to work on ARM architecture, so for that +# case we need an extra identifying specifier on the bootloader +# name string, like: Linux-32bit-arm, over normal Linux-32bit +machine = 'arm' if platform.machine().startswith('arm') else \ + 'aarch' if platform.machine().startswith('aarch') else \ + 'sw_64' if platform.machine().startswith('sw_64') else None + + +# 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 : +# "Calling putenv() directly does not change os.environ, so it's +# better to modify os.environ." (Same for unsetenv.) + +def getenv(name, default=None): + """ + Returns unicode string containing value of environment variable 'name'. + """ + return os.environ.get(name, default) + + +def setenv(name, value): + """ + Accepts unicode string and set it as environment variable 'name' containing + value 'value'. + """ + os.environ[name] = value + + +def unsetenv(name): + """ + Delete the environment variable 'name'. + """ + # Some platforms (e.g. AIX) do not support `os.unsetenv()` and + # thus `del os.environ[name]` has no effect onto 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, **kwargs): + """ + 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. Consider + calling the general-purpose `exec_command_stdout()` function instead. + + For backward compatibility, this function's return value non-portably + depends on the current Python version and passed keyword arguments: + + * Under Python 2.7, this value is an **encoded `str` string** rather than + a decoded `unicode` string. 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. + * 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. + 1. 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. + """ + + encoding = kwargs.pop('encoding', None) + raise_ENOENT = kwargs.pop('__raise_ENOENT__', None) + try: + out = subprocess.Popen( + cmdargs, stdout=subprocess.PIPE, **kwargs).communicate()[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 + + # 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) + else: + # If no encoding is given, assume we're reading filenames from + # stdout only because it's 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, **kwargs): + """ + 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. + 1. 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 failure. + """ + + # 'encoding' keyword is not supported for 'subprocess.call'. + # Remove it thus from kwargs. + if 'encoding' in kwargs: + kwargs.pop('encoding') + return subprocess.call(cmdargs, **kwargs) + + +def exec_command_stdout(*command_args, **kwargs): + """ + Capture and return the standard output of the command specified by the + passed positional arguments, optionally configured by the passed keyword + arguments. + + Unlike the legacy `exec_command()` and `exec_command_all()` functions, this + modern function is explicitly designed for cross-platform portability. The + return value may be safely used for any purpose, including string + manipulation and parsing. + + .. NOTE:: + If this command's standard output contains _only_ pathnames, this + function does _not_ return the correct filesystem-encoded string expected + by PyInstaller. If this is the case, consider calling the + filesystem-specific `exec_command()` function instead. + + 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. + 1. Optional remaining elements are arguments to pass to this command. + encoding : str, optional + Optional name of the encoding with which to decode this command's + standard output (e.g., `utf8`), passed as a keyword argument. If + unpassed , this output will be decoded in a portable manner specific to + to the current platform, shell environment, and system settings with + Python's built-in `universal_newlines` functionality. + + All remaining keyword arguments are passed as is to the + `subprocess.check_output()` function. + + Returns + ---------- + str + Unicode string of this command's standard output decoded according to + the "encoding" keyword argument. + """ + + # Value of the passed "encoding" parameter, defaulting to None. + encoding = kwargs.pop('encoding', None) + + # If no encoding was specified, the current locale is defaulted to. Else, an + # encoding was specified. To ensure this encoding is respected, the + # "universal_newlines" option is disabled if also passed. Nice, eh? + kwargs['universal_newlines'] = encoding is None + + # Standard output captured from this command as a decoded Unicode string if + # "universal_newlines" is enabled or an encoded byte array otherwise. + stdout = subprocess.check_output(command_args, **kwargs) + + # Return a Unicode string, decoded from this encoded byte array if needed. + return stdout if encoding is None else stdout.decode(encoding) + + +def exec_command_all(*cmdargs, **kwargs): + """ + 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()`; + else, consider calling `exec_command_stdout()`. + + 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. + 1. 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. + """ + encoding = kwargs.pop('encoding', None) + proc = subprocess.Popen(cmdargs, bufsize=-1, # Default OS buffer size. + stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) + # Waits for subprocess to complete. + out, err = proc.communicate() + # 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] + + # Mac OS X 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 machine == 'arm': + py_prefix = ['arch', '-arm64'] # Apple M1 + else: + py_prefix = ['arch', '-x86_64'] # Intel + elif architecture == '32bit': + py_prefix = ['arch', '-i386'] + else: + py_prefix = [] + # Since OS X 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 _PYOPTS: + cmdargs.append(_PYOPTS) + + 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, **kwargs): + """ + 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, **kwargs): + """ + 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. + +def expand_path(path): + """ + Replace initial tilde '~' in path with user's home directory and also + expand environment variables (${VARNAME} - Unix, %VARNAME% - Windows). + """ + return os.path.expandvars(os.path.expanduser(path)) + + +# Site-packages functions - use native function if available. +def getsitepackages(prefixes=None): + """Returns a list containing all global site-packages directories. + + For each directory present in ``prefixes`` (or the global ``PREFIXES``), + this function will find its `site-packages` subdirectory depending on the + system environment, and will return 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, pathname): + # Import module from a file. + mod_loader = importlib.machinery.SourceFileLoader(name, pathname) + return mod_loader.load_module() + + +# Patterns of module names that should be bundled into the base_library.zip. + +PY3_BASE_MODULES = { + # Python 3.x + # These modules are direct or indirect dependencies of encodings.* modules. + # encodings modules must be recursively included to set the I/O encoding during + # python startup. + '_bootlocale', + '_collections_abc', + '_weakrefset', + 'abc', + 'codecs', + 'collections', + 'copyreg', + 'encodings', + 'enum', + 'functools', + 'io', + 'heapq', + 'keyword', + 'linecache', + 'locale', + 'operator', + 're', + 'reprlib', + 'sre_compile', + 'sre_constants', + 'sre_parse', + 'traceback', # for startup errors + 'types', + 'weakref', + 'warnings', +} + +# 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 = { + '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. +# Dict 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, 6): + raise EnvironmentError('PyInstaller requires at Python 3.6 or newer.') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/config.py b/3rdparty/pyinstaller-4.3/PyInstaller/config.py new file mode 100644 index 0000000000000000000000000000000000000000..26302086b869ae0ff2d179f9dd5f9e21a6322cb6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/config.py @@ -0,0 +1,59 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 +hasUPX +hiddenimports +noconfirm +pathex +ui_admin +ui_access +upx_dir +workpath + +tests_modgraph - cached PyiModuleGraph object to speed up tests +""" + +# NOTE: Do not import other PyInstaller modules here. Just define constants here. + +CONF = { + # Unit tests require this key to exist. + 'pathex': [], +} diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/configure.py b/3rdparty/pyinstaller-4.3/PyInstaller/configure.py new file mode 100644 index 0000000000000000000000000000000000000000..224137db9598e26be4c82fb3b6900004da6c3010 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/configure.py @@ -0,0 +1,99 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +from . import compat +from . import log as logging +from .compat import is_win, is_darwin + +logger = logging.getLogger(__name__) + + +def test_UPX(config, upx_dir): + logger.debug('Testing for UPX ...') + cmd = "upx" + if upx_dir: + cmd = os.path.normpath(os.path.join(upx_dir, cmd)) + + hasUPX = 0 + try: + vers = compat.exec_command( + cmd, '-V', __raise_ENOENT__=True).strip().splitlines() + if vers: + v = vers[0].split()[1] + try: + # v = "3.96-git-d7ba31cab8ce" + v = v.split("-")[0] + except Exception: + pass + hasUPX = tuple(map(int, v.split("."))) + if is_win and hasUPX < (1, 92): + logger.error('UPX is too old! Python 2.4 under Windows requires UPX 1.92+') + hasUPX = 0 + except Exception as e: + if isinstance(e, OSError) and e.errno == 2: + # No such file or directory + pass + else: + logger.info('An exception occured when testing for UPX:') + logger.info(' %r', e) + if hasUPX: + is_available = 'available' + else: + is_available = 'not available' + logger.info('UPX is %s.', is_available) + config['hasUPX'] = hasUPX + config['upx_dir'] = upx_dir + + +def _get_pyinst_cache_dir(): + old_cache_dir = None + if compat.getenv('PYINSTALLER_CONFIG_DIR'): + cache_dir = compat.getenv('PYINSTALLER_CONFIG_DIR') + elif is_win: + cache_dir = compat.getenv('LOCALAPPDATA') + if not cache_dir: + cache_dir = os.path.expanduser('~\\Application Data') + elif 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 now 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, **kw): + config = {} + test_UPX(config, upx_dir) + config['cachedir'] = _get_pyinst_cache_dir() + + return config diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/depend/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/depend/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/depend/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/depend/analysis.py b/3rdparty/pyinstaller-4.3/PyInstaller/depend/analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..c93cf63bd5fbd70de40bbf4b61458bff89918ce3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/depend/analysis.py @@ -0,0 +1,852 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Define a modified ModuleGraph that can return its contents as +a TOC and in other ways act like the old ImpTracker. +TODO: This class, along with TOC and Tree should be in a separate module. + +For reference, the ModuleGraph node types and their contents: + + nodetype identifier filename + + Script full path to .py full path to .py + SourceModule basename full path to .py + BuiltinModule basename None + CompiledModule basename full path to .pyc + Extension basename full path to .so + MissingModule basename None + Package basename full path to __init__.py + packagepath is ['path to package'] + globalnames is set of global names __init__.py defines + ExtensionPackage basename full path to __init__.{so,dll} + packagepath is ['path to package'] + +The main extension here over ModuleGraph is a method to extract nodes +from the flattened graph and return them as a TOC, or added to a TOC. +Other added methods look up nodes by identifier and return facts +about them, replacing what the old ImpTracker list could do. +""" + +import os +import re +import sys +import traceback +import ast + +from copy import deepcopy +from collections import defaultdict + +from .. import compat +from .. import HOMEPATH, PACKAGEPATH +from .. import log as logging +from ..log import INFO, DEBUG, TRACE +from ..building.datastruct import TOC +from .imphook import AdditionalFilesCache, ModuleHookCache +from .imphookapi import PreSafeImportModuleAPI, PreFindModulePathAPI +from ..compat import importlib_load_source, PY3_BASE_MODULES,\ + PURE_PYTHON_MODULE_TYPES, BINARY_MODULE_TYPES, VALID_MODULE_TYPES, \ + BAD_MODULE_TYPES, MODULE_TYPES_TO_TOC_DICT +from ..lib.modulegraph.find_modules import get_implies +from ..lib.modulegraph.modulegraph import ModuleGraph +from ..utils.hooks import collect_submodules, is_package + + +logger = logging.getLogger(__name__) + + +class PyiModuleGraph(ModuleGraph): + """ + Directed graph whose nodes represent modules and edges represent + dependencies between these modules. + + This high-level subclass wraps the lower-level `ModuleGraph` class with + support for graph and runtime hooks. While each instance of `ModuleGraph` + represents a set of disconnected trees, each instance of this class *only* + represents a single connected tree whose root node is the Python script + originally passed by the user on the command line. For that reason, while + there may (and typically do) exist more than one `ModuleGraph` instance, + there typically exists only a singleton instance of this class. + + Attributes + ---------- + _hooks : ModuleHookCache + Dictionary mapping the fully-qualified names of all modules with + normal (post-graph) hooks to the absolute paths of such hooks. See the + the `_find_module_path()` method for details. + _hooks_pre_find_module_path : ModuleHookCache + Dictionary mapping the fully-qualified names of all modules with + pre-find module path hooks to the absolute paths of such hooks. See the + the `_find_module_path()` method for details. + _hooks_pre_safe_import_module : ModuleHookCache + Dictionary mapping the fully-qualified names of all modules with + pre-safe import module hooks to the absolute paths of such hooks. See + the `_safe_import_module()` method for details. + _user_hook_dirs : list + List of the absolute paths of all directories containing user-defined + hooks for the current application. + _excludes : list + List of module names to be excluded when searching for dependencies. + _additional_files_cache : AdditionalFilesCache + Cache of all external dependencies (e.g., binaries, datas) listed in + hook scripts for imported modules. + _base_modules: list + Dependencies for `base_library.zip` (which remain the same for every + executable). + """ + + # Note: these levels are completely arbitrary and may be adjusted if needed. + LOG_LEVEL_MAPPING = {0: INFO, 1: DEBUG, 2: TRACE, 3: TRACE, 4: TRACE} + + def __init__(self, pyi_homepath, user_hook_dirs=(), excludes=(), **kwargs): + super(PyiModuleGraph, self).__init__(excludes=excludes, **kwargs) + # Homepath to the place where is PyInstaller located. + self._homepath = pyi_homepath + # modulegraph Node for the main python script that is analyzed + # by PyInstaller. + self._top_script_node = None + + # Absolute paths of all user-defined hook directories. + self._excludes = excludes + self._reset(user_hook_dirs) + self._analyze_base_modules() + + def _reset(self, user_hook_dirs): + """ + Reset for another set of scripts. + This is primary required for running the test-suite. + """ + self._top_script_node = None + self._additional_files_cache = AdditionalFilesCache() + # Command line, Entry Point, and then builtin hook dirs. + self._user_hook_dirs = ( + list(user_hook_dirs) + [os.path.join(PACKAGEPATH, 'hooks')] + ) + # Hook-specific lookup tables. + # These need to reset when reusing cached PyiModuleGraph to avoid + # hooks to refer to files or data from another test-case. + logger.info('Caching module graph hooks...') + self._hooks = self._cache_hooks("") + self._hooks_pre_safe_import_module = self._cache_hooks('pre_safe_import_module') + self._hooks_pre_find_module_path = self._cache_hooks('pre_find_module_path') + + # Search for run-time hooks in all hook directories. + self._available_rthooks = defaultdict(list) + for uhd in self._user_hook_dirs: + uhd_path = os.path.abspath(os.path.join(uhd, 'rthooks.dat')) + try: + with compat.open_file(uhd_path, compat.text_read_mode, + encoding='utf-8') as f: + rthooks = ast.literal_eval(f.read()) + except FileNotFoundError: + # Ignore if this hook path doesn't have run-time hooks. + continue + except Exception as e: + logger.error('Unable to read run-time hooks from %r: %s' % + (uhd_path, e)) + continue + + self._merge_rthooks(rthooks, uhd, uhd_path) + + # Convert back to a standard dict. + self._available_rthooks = dict(self._available_rthooks) + + def _merge_rthooks(self, rthooks, uhd, uhd_path): + """The expected data structure for a run-time hook file is a Python + dictionary of type ``Dict[str, List[str]]`` where the dictionary + keys are module names the the sequence strings are Python file names. + + Check then merge this data structure, updating the file names to be + absolute. + """ + # Check that the root element is a dict. + assert isinstance(rthooks, dict), ( + 'The root element in %s must be a dict.' % uhd_path) + for module_name, python_file_name_list in rthooks.items(): + # Ensure the key is a string. + assert isinstance(module_name, compat.string_types), ( + '%s must be a dict whose keys are strings; %s ' + 'is not a string.' % (uhd_path, module_name)) + # Ensure the value is a list. + assert isinstance(python_file_name_list, list), ( + 'The value of %s key %s must be a list.' % + (uhd_path, module_name)) + if module_name in self._available_rthooks: + logger.warning( + 'Runtime hooks for %s have already been defined. Skipping ' + 'the runtime hooks for %s that are defined in %s.', + module_name, module_name, os.path.join(uhd, 'rthooks') + ) + # Skip this module + continue + # Merge this with existing run-time hooks. + for python_file_name in python_file_name_list: + # Ensure each item in the list is a string. + assert isinstance(python_file_name, compat.string_types), ( + '%s key %s, item %r must be a string.' % + (uhd_path, module_name, python_file_name)) + # Transform it into an absolute path. + abs_path = os.path.join(uhd, 'rthooks', python_file_name) + # Make sure this file exists. + assert os.path.exists(abs_path), ( + 'In %s, key %s, the file %r expected to be located at ' + '%r does not exist.' % + (uhd_path, module_name, python_file_name, abs_path)) + # Merge it. + self._available_rthooks[module_name].append(abs_path) + + + @staticmethod + def _findCaller(*args, **kwargs): + # Used to add an additional stack-frame above logger.findCaller. + # findCaller expects the caller to be three stack-frames above itself. + return logger.findCaller(*args, **kwargs) + + def msg(self, level, s, *args): + """ + Print a debug message with the given level. + + 1. Map the msg log level to a logger log level. + 2. Generate the message format (the same format as ModuleGraph) + 3. Find the caller, which findCaller expects three stack-frames above + itself: + [3] caller -> [2] msg (here) -> [1] _findCaller -> [0] logger.findCaller + 4. Create a logRecord with the caller's information. + 5. Handle the logRecord. + """ + try: + level = self.LOG_LEVEL_MAPPING[level] + except KeyError: + return + if not logger.isEnabledFor(level): + return + + msg = "%s %s" % (s, ' '.join(map(repr, args))) + + try: + fn, lno, func, sinfo = self._findCaller() + except ValueError: # pragma: no cover + fn, lno, func, sinfo = "(unknown file)", 0, "(unknown function)", None + record = logger.makeRecord( + logger.name, level, fn, lno, msg, [], None, func, None, sinfo) + + logger.handle(record) + + # Set logging methods so that the stack is correctly detected. + msgin = msg + msgout = msg + + def _cache_hooks(self, hook_type): + """ + Get a cache of all hooks of the passed type. + + The cache will include all official hooks defined by the PyInstaller + codebase _and_ all unofficial hooks defined for the current application. + + Parameters + ---------- + hook_type : str + Type of hooks to be cached, equivalent to the basename of the + subpackage of the `PyInstaller.hooks` package containing such hooks + (e.g., `post_create_package` for post-create package hooks). + """ + # Cache of this type of hooks. + # logger.debug("Caching system %s hook dir %r" % (hook_type, system_hook_dir)) + hook_dirs = [] + for user_hook_dir in self._user_hook_dirs: + # Absolute path of the user-defined subdirectory of this hook type. + # If this directory exists, add it to the list to be cached. + user_hook_type_dir = os.path.join(user_hook_dir, hook_type) + if os.path.isdir(user_hook_type_dir): + # logger.debug("Caching user %s hook dir %r" % (hook_type, hooks_user_dir)) + hook_dirs.append(user_hook_type_dir) + + return ModuleHookCache(self, hook_dirs) + + + def _analyze_base_modules(self): + """ + Analyze dependencies of the the modules in base_library.zip. + """ + logger.info('Analyzing base_library.zip ...') + required_mods = [] + # Collect submodules from required modules in base_library.zip. + for m in PY3_BASE_MODULES: + if is_package(m): + required_mods += collect_submodules(m) + else: + required_mods.append(m) + # Initialize ModuleGraph. + self._base_modules = [mod + for req in required_mods + for mod in self.import_hook(req)] + + + def run_script(self, pathname, caller=None): + """ + Wrap the parent's 'run_script' method and create graph from the first + script in the analysis, and save its node to use as the "caller" node + for all others. This gives a connected graph rather than a collection + of unrelated trees, + """ + if self._top_script_node is None: + # Remember the node for the first script. + try: + self._top_script_node = super(PyiModuleGraph, self).run_script( + pathname) + except SyntaxError: + print("\nSyntax error in", pathname, file=sys.stderr) + formatted_lines = traceback.format_exc().splitlines(True) + print(*formatted_lines[-4:], file=sys.stderr) + sys.exit(1) + # Create references from the top script to the base_modules in graph. + for node in self._base_modules: + self.createReference(self._top_script_node, node) + # Return top-level script node. + return self._top_script_node + else: + if not caller: + # Defaults to as any additional script is called from the + # top-level script. + caller = self._top_script_node + return super(PyiModuleGraph, self).run_script( + pathname, caller=caller) + + + def process_post_graph_hooks(self): + """ + For each imported module, run this module's post-graph hooks if any. + """ + # For each iteration of the infinite "while" loop below: + # + # 1. All hook() functions defined in cached hooks for imported modules + # are called. This may result in new modules being imported (e.g., as + # hidden imports) that were ignored earlier in the current iteration: + # if this is the case, all hook() functions defined in cached hooks + # for these modules will be called by the next iteration. + # 2. All cached hooks whose hook() functions were called are removed + # from this cache. If this cache is empty, no hook() functions will + # be called by the next iteration and this loop will be terminated. + # 3. If no hook() functions were called, this loop is terminated. + logger.info('Processing module hooks...') + while True: + # Set of the names of all imported modules whose post-graph hooks + # are run by this iteration, preventing the next iteration from re- + # running these hooks. If still empty at the end of this iteration, + # no post-graph hooks were run; thus, this loop will be terminated. + hooked_module_names = set() + + # For each remaining hookable module and corresponding hooks... + for module_name, module_hooks in self._hooks.items(): + # Graph node for this module if imported or "None" otherwise. + module_node = self.findNode( + module_name, create_nspkg=False) + + # If this module has not been imported, temporarily ignore it. + # This module is retained in the cache, as a subsequently run + # post-graph hook could import this module as a hidden import. + if module_node is None: + continue + + # If this module is unimportable, permanently ignore it. + if type(module_node).__name__ not in VALID_MODULE_TYPES: + hooked_module_names.add(module_name) + continue + + # For each hook script for this module... + for module_hook in module_hooks: + # Run this script's post-graph hook. + module_hook.post_graph() + + # Cache all external dependencies listed by this script + # after running this hook, which could add dependencies. + self._additional_files_cache.add( + module_name, + module_hook.binaries, + module_hook.datas) + + # Prevent this module's hooks from being run again. + hooked_module_names.add(module_name) + + # Prevent all post-graph hooks run above from being run again by the + # next iteration. + self._hooks.remove_modules(*hooked_module_names) + + # If no post-graph hooks were run, terminate iteration. + if not hooked_module_names: + break + + def _safe_import_module(self, module_basename, module_name, parent_package): + """ + Create a new graph node for the module with the passed name under the + parent package signified by the passed graph node. + + This method wraps the superclass method with support for pre-import + module hooks. If such a hook exists for this module (e.g., a script + `PyInstaller.hooks.hook-{module_name}` containing a function + `pre_safe_import_module()`), that hook will be run _before_ the + superclass method is called. + + Pre-Safe-Import-Hooks are performed just *prior* to importing + the module. When running the hook, the modules parent package + has already been imported and ti's `__path__` is set up. But + the module is just about to be imported. + + See the superclass method for description of parameters and + return value. + """ + # If this module has pre-safe import module hooks, run these first. + if module_name in self._hooks_pre_safe_import_module: + # For the absolute path of each such hook... + for hook in self._hooks_pre_safe_import_module[module_name]: + # Dynamically import this hook as a fabricated module. + logger.info('Processing pre-safe import module hook %s ' + 'from %r.', module_name, hook.hook_filename) + hook_module_name = 'PyInstaller_hooks_pre_safe_import_module_' + module_name.replace('.', '_') + hook_module = importlib_load_source(hook_module_name, + hook.hook_filename) + + # Object communicating changes made by this hook back to us. + hook_api = PreSafeImportModuleAPI( + module_graph=self, + module_basename=module_basename, + module_name=module_name, + parent_package=parent_package, + ) + + # Run this hook, passed this object. + if not hasattr(hook_module, 'pre_safe_import_module'): + raise NameError( + 'pre_safe_import_module() function not defined by ' + 'hook %r.' % hook_module + ) + hook_module.pre_safe_import_module(hook_api) + + # Respect method call changes requested by this hook. + module_basename = hook_api.module_basename + module_name = hook_api.module_name + + # Prevent subsequent calls from rerunning these hooks. + del self._hooks_pre_safe_import_module[module_name] + + # Call the superclass method. + return super(PyiModuleGraph, self)._safe_import_module( + module_basename, module_name, parent_package) + + def _find_module_path(self, fullname, module_name, search_dirs): + """ + Get a 3-tuple detailing the physical location of the module with the + passed name if that module exists _or_ raise `ImportError` otherwise. + + This method wraps the superclass method with support for pre-find module + path hooks. If such a hook exists for this module (e.g., a script + `PyInstaller.hooks.hook-{module_name}` containing a function + `pre_find_module_path()`), that hook will be run _before_ the + superclass method is called. + + See superclass method for parameter and return value descriptions. + """ + # If this module has pre-find module path hooks, run these first. + if fullname in self._hooks_pre_find_module_path: + # For the absolute path of each such hook... + for hook in self._hooks_pre_find_module_path[fullname]: + # Dynamically import this hook as a fabricated module. + logger.info('Processing pre-find module path hook %s from %r.', + fullname, hook.hook_filename) + hook_fullname = 'PyInstaller_hooks_pre_find_module_path_' + fullname.replace('.', '_') + hook_module = importlib_load_source(hook_fullname, + hook.hook_filename) + + # Object communicating changes made by this hook back to us. + hook_api = PreFindModulePathAPI( + module_graph=self, + module_name=fullname, + search_dirs=search_dirs, + ) + + # Run this hook, passed this object. + if not hasattr(hook_module, 'pre_find_module_path'): + raise NameError( + 'pre_find_module_path() function not defined by ' + 'hook %r.' % hook_module + ) + hook_module.pre_find_module_path(hook_api) + + # Respect method call changes requested by this hook. + search_dirs = hook_api.search_dirs + + # Prevent subsequent calls from rerunning these hooks. + del self._hooks_pre_find_module_path[fullname] + + # Call the superclass method. + return super(PyiModuleGraph, self)._find_module_path( + fullname, module_name, search_dirs) + + def get_code_objects(self): + """ + Get code objects from ModuleGraph for pure Pyhton modules. This allows + to avoid writing .pyc/pyo files to hdd at later stage. + + :return: Dict with module name and code object. + """ + code_dict = {} + mod_types = PURE_PYTHON_MODULE_TYPES + for node in self.flatten(start=self._top_script_node): + # TODO This is terrible. To allow subclassing, types should never be + # directly compared. Use isinstance() instead, which is safer, + # simpler, and accepts sets. Most other calls to type() in the + # codebase should also be refactored to call isinstance() instead. + + # get node type e.g. Script + mg_type = type(node).__name__ + if mg_type in mod_types: + if node.code: + code_dict[node.identifier] = node.code + return code_dict + + def _make_toc(self, typecode=None, existing_TOC=None): + """ + Return the name, path and type of selected nodes as a TOC, or appended + to a TOC. The selection is via a list of PyInstaller TOC typecodes. + If that list is empty we return the complete flattened graph as a TOC + with the ModuleGraph note types in place of typecodes -- meant for + debugging only. Normally we return ModuleGraph nodes whose types map + to the requested PyInstaller typecode(s) as indicated in the MODULE_TYPES_TO_TOC_DICT. + + We use the ModuleGraph (really, ObjectGraph) flatten() method to + scan all the nodes. This is patterned after ModuleGraph.report(). + """ + # Construct regular expression for matching modules that should be + # excluded because they are bundled in base_library.zip. + # + # This expression matches the base module name, optionally followed by + # a period and then any number of characters. This matches the module name and + # the fully qualified names of any of its submodules. + regex_str = '(' + '|'.join(PY3_BASE_MODULES) + r')(\.|$)' + module_filter = re.compile(regex_str) + + result = existing_TOC or TOC() + for node in self.flatten(start=self._top_script_node): + # Skip modules that are in base_library.zip. + if module_filter.match(node.identifier): + continue + entry = self._node_to_toc(node, typecode) + if entry is not None: + # TOC.append the data. This checks for a pre-existing name + # and skips it if it exists. + result.append(entry) + return result + + def make_pure_toc(self): + """ + Return all pure Python modules formatted as TOC. + """ + # PyInstaller should handle special module types without code object. + return self._make_toc(PURE_PYTHON_MODULE_TYPES) + + def make_binaries_toc(self, existing_toc): + """ + Return all binary Python modules formatted as TOC. + """ + return self._make_toc(BINARY_MODULE_TYPES, existing_toc) + + def make_missing_toc(self): + """ + Return all MISSING Python modules formatted as TOC. + """ + return self._make_toc(BAD_MODULE_TYPES) + + @staticmethod + def _node_to_toc(node, typecode=None): + # TODO This is terrible. Everything in Python has a type. It's + # nonsensical to even speak of "nodes [that] are not typed." How + # would that even occur? After all, even "None" has a type! (It's + # "NoneType", for the curious.) Remove this, please. + + # get node type e.g. Script + mg_type = type(node).__name__ + assert mg_type is not None + + if typecode and not (mg_type in typecode): + # Type is not a to be selected one, skip this one + return None + # Extract the identifier and a path if any. + if mg_type == 'Script': + # for Script nodes only, identifier is a whole path + (name, ext) = os.path.splitext(node.filename) + name = os.path.basename(name) + elif mg_type == 'ExtensionPackage': + # package with __init__ module being an extension module + # This needs to end up as e.g. 'mypkg/__init__.so'. + # Convert the packages name ('mypkg') into the module name + # ('mypkg.__init__') *here* to keep special cases away elsewhere + # (where the module name is converted to a filename). + name = node.identifier + ".__init__" + else: + name = node.identifier + path = node.filename if node.filename is not None else '' + # Ensure name is really 'str'. Module graph might return + # object type 'modulegraph.Alias' which inherits fromm 'str'. + # But 'marshal.dumps()' function is able to marshal only 'str'. + # Otherwise on Windows PyInstaller might fail with message like: + # + # ValueError: unmarshallable object + name = str(name) + # Translate to the corresponding TOC typecode. + toc_type = MODULE_TYPES_TO_TOC_DICT[mg_type] + return (name, path, toc_type) + + def nodes_to_toc(self, node_list, existing_TOC=None): + """ + Given a list of nodes, create a TOC representing those nodes. + This is mainly used to initialize a TOC of scripts with the + ones that are runtime hooks. The process is almost the same as + _make_toc(), but the caller guarantees the nodes are + valid, so minimal checking. + """ + result = existing_TOC or TOC() + for node in node_list: + result.append(self._node_to_toc(node)) + return result + + # Return true if the named item is in the graph as a BuiltinModule node. + # The passed name is a basename. + def is_a_builtin(self, name) : + node = self.findNode(name) + if node is None: + return False + return type(node).__name__ == 'BuiltinModule' + + def get_importers(self, name): + """List all modules importing the module with the passed name. + + Returns a list of (identifier, DependencyIinfo)-tuples. If the names + module has not yet been imported, this method returns an empty list. + + Parameters + ---------- + name : str + Fully-qualified name of the module to be examined. + + Returns + ---------- + list + List of (fully-qualified names, DependencyIinfo)-tuples of all + modules importing the module with the passed fully-qualified name. + + """ + def get_importer_edge_data(importer): + edge = self.graph.edge_by_node(importer, name) + # edge might be None in case an AliasModule was added. + if edge is not None: + return self.graph.edge_data(edge) + + node = self.findNode(name) + if node is None : return [] + _, importers = self.get_edges(node) + importers = (importer.identifier + for importer in importers + if importer is not None) + return [(importer, get_importer_edge_data(importer)) + for importer in importers] + + # TODO create class from this function. + def analyze_runtime_hooks(self, custom_runhooks): + """ + Analyze custom run-time hooks and run-time hooks implied by found modules. + + :return : list of Graph nodes. + """ + rthooks_nodes = [] + logger.info('Analyzing run-time hooks ...') + # Process custom runtime hooks (from --runtime-hook options). + # The runtime hooks are order dependent. First hooks in the list + # are executed first. Put their graph nodes at the head of the + # priority_scripts list Pyinstaller-defined rthooks and + # thus they are executed first. + if custom_runhooks: + for hook_file in custom_runhooks: + logger.info("Including custom run-time hook %r", hook_file) + hook_file = os.path.abspath(hook_file) + # Not using "try" here because the path is supposed to + # exist, if it does not, the raised error will explain. + rthooks_nodes.append(self.run_script(hook_file)) + + # Find runtime hooks that are implied by packages already imported. + # Get a temporary TOC listing all the scripts and packages graphed + # so far. Assuming that runtime hooks apply only to modules and packages. + temp_toc = self._make_toc(VALID_MODULE_TYPES) + for (mod_name, path, typecode) in temp_toc: + # Look if there is any run-time hook for given module. + if mod_name in self._available_rthooks: + # There could be several run-time hooks for a module. + for abs_path in self._available_rthooks[mod_name]: + logger.info("Including run-time hook %r", abs_path) + rthooks_nodes.append(self.run_script(abs_path)) + + return rthooks_nodes + + def add_hiddenimports(self, module_list): + """ + Add hidden imports that are either supplied as CLI option --hidden-import=MODULENAME + or as dependencies from some PyInstaller features when enabled (e.g. crypto feature). + """ + assert self._top_script_node is not None + # Analyze the script's hidden imports (named on the command line) + for modnm in module_list: + node = self.findNode(modnm) + if node is not None: + logger.debug('Hidden import %r already found', modnm) + else: + logger.info("Analyzing hidden import %r", modnm) + # ModuleGraph throws ImportError if import not found + try: + nodes = self.import_hook(modnm) + assert len(nodes) == 1 + node = nodes[0] + except ImportError: + logger.error("Hidden import %r not found", modnm) + continue + # Create references from the top script to the hidden import, + # even if found otherwise. Don't waste time checking whether it + # as actually added by this (test-) script. + self.createReference(self._top_script_node, node) + + + def get_co_using_ctypes(self): + """ + Find modules that import Python module 'ctypes'. + + Modules that import 'ctypes' probably load a dll that might be + required for bundling with the executable. Thus these modules' code + needs then to be searched for patterns like these: + + ctypes.CDLL('libname') + ctypes.cdll.LoadLibrary('libname') + + :return: Code objects importing `ctypes` and thus need to be scanned + for module dependencies. + """ + co_dict = {} + pure_python_module_types = PURE_PYTHON_MODULE_TYPES | {'Script',} + node = self.findNode('ctypes') + if node: + referers = self.getReferers(node) + for r in referers: + # Under python 3.7 and earlier, if ctypes is added to + # hidden imports, one of referers ends up being None, + # causing #3825. Work around it. + if r is None: + continue + r_ident = r.identifier + # Ensure that modulegraph objects has attribute 'code'. + if type(r).__name__ in pure_python_module_types: + if r_ident == 'ctypes' or r_ident.startswith('ctypes.'): + # Skip modules of 'ctypes' package. + continue + co_dict[r.identifier] = r.code + return co_dict + + +_cached_module_graph_ = None + +def initialize_modgraph(excludes=(), user_hook_dirs=()): + """ + Create the cached module graph. + + This function might appear weird but is necessary for speeding up + test runtime because it allows caching basic ModuleGraph object that + gets created for 'base_library.zip'. + + Parameters + ---------- + excludes : list + List of the fully-qualified names of all modules to be "excluded" and + hence _not_ frozen into the executable. + user_hook_dirs : list + List of the absolute paths of all directories containing user-defined + hooks for the current application or `None` if no such directories were + specified. + + Returns + ---------- + PyiModuleGraph + Module graph with core dependencies. + """ + # normalize parameters to ensure tuples and make camparism work + user_hook_dirs = user_hook_dirs or () + excludes = excludes or () + + # If there is a graph cached with the same same excludes, reuse it. + # See ``PyiModulegraph._reset()`` for why what is reset. + # This cache is uses primary to speed up the test-suite. Fixture + # `pyi_modgraph` calls this function with empty excludes, creating + # a graph suitable for the huge majority of tests. + global _cached_module_graph_ + if (_cached_module_graph_ and + _cached_module_graph_._excludes == excludes): + logger.info('Reusing cached module dependency graph...') + graph = deepcopy(_cached_module_graph_) + graph._reset(user_hook_dirs) + return graph + + logger.info('Initializing module dependency graph...') + + # Construct the initial module graph by analyzing all import statements. + graph = PyiModuleGraph( + HOMEPATH, + excludes=excludes, + # get_implies() are hidden imports known by modulgraph. + implies=get_implies(), + user_hook_dirs=user_hook_dirs, + ) + + if not _cached_module_graph_: + # Only cache the first graph, see above for explanation. + logger.info('Caching module dependency graph...') + # cache a deep copy of the graph + _cached_module_graph_ = deepcopy(graph) + # Clear data which does not need to be copied from teh cached graph + # since it will be reset by ``PyiModulegraph._reset()`` anyway. + _cached_module_graph_._hooks = None + _cached_module_graph_._hooks_pre_safe_import_module = None + _cached_module_graph_._hooks_pre_find_module_path = None + + return graph + + +def get_bootstrap_modules(): + """ + Get TOC with the bootstrapping modules and their dependencies. + :return: TOC with modules + """ + # Import 'struct' modules to get real paths to module file names. + mod_struct = __import__('struct') + # Basic modules necessary for the bootstrap process. + loader_mods = TOC() + loaderpath = os.path.join(HOMEPATH, 'PyInstaller', 'loader') + # On some platforms (Windows, Debian/Ubuntu) '_struct' and zlib modules are + # built-in modules (linked statically) and thus does not have attribute __file__. + # 'struct' module is required for reading Python bytecode from executable. + # 'zlib' is required to decompress this bytecode. + for mod_name in ['_struct', 'zlib']: + mod = __import__(mod_name) # C extension. + if hasattr(mod, '__file__'): + loader_mods.append((mod_name, os.path.abspath(mod.__file__), 'EXTENSION')) + # NOTE:These modules should be kept simple without any complicated dependencies. + loader_mods +=[ + ('struct', os.path.abspath(mod_struct.__file__), 'PYMODULE'), + ('pyimod01_os_path', os.path.join(loaderpath, 'pyimod01_os_path.pyc'), 'PYMODULE'), + ('pyimod02_archive', os.path.join(loaderpath, 'pyimod02_archive.pyc'), 'PYMODULE'), + ('pyimod03_importers', os.path.join(loaderpath, 'pyimod03_importers.pyc'), 'PYMODULE'), + ('pyiboot01_bootstrap', os.path.join(loaderpath, 'pyiboot01_bootstrap.py'), 'PYSOURCE'), + ] + return loader_mods diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/depend/bindepend.py b/3rdparty/pyinstaller-4.3/PyInstaller/depend/bindepend.py new file mode 100644 index 0000000000000000000000000000000000000000..2a4eae5d5ab7aee0d4f5a7f80fd067fe0e35ac34 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/depend/bindepend.py @@ -0,0 +1,974 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +""" +Find external dependencies of binary libraries. +""" + +import ctypes.util +import os +import re +import sys +from glob import glob +# Required for extracting eggs. +import zipfile +import collections + +from .. import compat +from ..compat import (is_win, is_win_10, is_unix, + is_aix, is_solar, is_cygwin, is_hpux, + is_darwin, is_freebsd, is_openbsd, is_venv, is_conda, + base_prefix, PYDYLIB_NAMES) +from . import dylib, utils + +from .. import log as logging +from ..utils.win32 import winutils + +logger = logging.getLogger(__name__) + +seen = set() + +# Import windows specific stuff. +if is_win: + from distutils.sysconfig import get_python_lib + from ..utils.win32.winmanifest import RT_MANIFEST + from ..utils.win32.winmanifest import GetManifestResources + from ..utils.win32.winmanifest import Manifest + from ..utils.win32 import winresource + import pefile + # Do not load all the directories information from the PE file + pefile.fast_load = True + + +def getfullnameof(mod, xtrapath=None): + """ + Return the full path name of MOD. + + MOD is the basename of a dll or pyd. + XTRAPATH is a path or list of paths to search first. + Return the full path name of MOD. + Will search the full Windows search path, as well as sys.path + """ + pywin32_paths = [] + if is_win: + pywin32_paths = [os.path.join(get_python_lib(), 'pywin32_system32')] + if is_venv: + pywin32_paths.append( + os.path.join(base_prefix, 'Lib', 'site-packages', + 'pywin32_system32') + ) + + epath = (sys.path + # Search sys.path first! + pywin32_paths + + winutils.get_system_path() + + compat.getenv('PATH', '').split(os.pathsep)) + if xtrapath is not None: + if type(xtrapath) == type(''): + epath.insert(0, xtrapath) + else: + epath = xtrapath + epath + for p in epath: + npth = os.path.join(p, mod) + if os.path.exists(npth) and matchDLLArch(npth): + return npth + return '' + + +def _getImports_pe(pth): + """ + Find the binary dependencies of PTH. + + This implementation walks through the PE header + and uses library pefile for that and supports + 32/64bit Windows + """ + dlls = set() + # By default library pefile parses all PE information. + # We are only interested in the list of dependent dlls. + # Performance is improved by reading only needed information. + # https://code.google.com/p/pefile/wiki/UsageExamples + + pe = pefile.PE(pth, fast_load=True) + pe.parse_data_directories(directories=[ + pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_IMPORT'], + pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_EXPORT'], + ], + forwarded_exports_only=True, + import_dllnames_only=True, + ) + + # Some libraries have no other binary dependencies. Use empty list + # in that case. Otherwise pefile would return None. + # e.g. C:\windows\system32\kernel32.dll on Wine + for entry in getattr(pe, 'DIRECTORY_ENTRY_IMPORT', []): + dll_str = winutils.convert_dll_name_to_str(entry.dll) + dlls.add(dll_str) + + # We must also read the exports table to find forwarded symbols: + # http://blogs.msdn.com/b/oldnewthing/archive/2006/07/19/671238.aspx + exportSymbols = getattr(pe, 'DIRECTORY_ENTRY_EXPORT', None) + if exportSymbols: + for sym in exportSymbols.symbols: + if sym.forwarder is not None: + # sym.forwarder is a bytes object. Convert it to a string. + forwarder = winutils.convert_dll_name_to_str(sym.forwarder) + # sym.forwarder is for example 'KERNEL32.EnterCriticalSection' + dll = forwarder.split('.')[0] + dlls.add(dll + ".dll") + + pe.close() + return dlls + + +def _extract_from_egg(toc): + """ + Ensure all binary modules in zipped eggs get extracted and + included with the frozen executable. + + return modified table of content + """ + new_toc = [] + for item in toc: + # Item is a tupple + # (mod_name, path, type) + modname, pth, typ = item + if not os.path.isfile(pth): + pth = check_extract_from_egg(pth)[0][0] + + # Add value to new data structure. + new_toc.append((modname, pth, typ)) + return new_toc + + +BindingRedirect = collections.namedtuple('BindingRedirect', + 'name language arch oldVersion newVersion publicKeyToken') + +def match_binding_redirect(manifest, redirect): + return all([ + manifest.name == redirect.name, + manifest.version == redirect.oldVersion, + manifest.language == redirect.language, + manifest.processorArchitecture == redirect.arch, + manifest.publicKeyToken == redirect.publicKeyToken, + ]) + +_exe_machine_type = None + +def matchDLLArch(filename): + """ + Return True if the DLL given by filename matches the CPU type/architecture of the + Python process running PyInstaller. + + Always returns True on non-Windows platforms + + :param filename: + :type filename: + :return: + :rtype: + """ + # TODO: check machine type on other platforms? + if not is_win: + return True + + global _exe_machine_type + try: + if _exe_machine_type is None: + pefilename = sys.executable # for exception handling + exe_pe = pefile.PE(sys.executable, fast_load=True) + _exe_machine_type = exe_pe.FILE_HEADER.Machine + exe_pe.close() + + pefilename = filename # for exception handling + pe = pefile.PE(filename, fast_load=True) + match_arch = pe.FILE_HEADER.Machine == _exe_machine_type + pe.close() + except pefile.PEFormatError as exc: + raise SystemExit('Can not get architecture from file: %s\n' + ' Reason: %s' % (pefilename, exc)) + return match_arch + + +def Dependencies(lTOC, xtrapath=None, manifest=None, redirects=None): + """ + Expand LTOC to include all the closure of binary dependencies. + + `LTOC` is a logical table of contents, ie, a seq of tuples (name, path). + Return LTOC expanded by all the binary dependencies of the entries + in LTOC, except those listed in the module global EXCLUDES + + `manifest` may be a winmanifest.Manifest instance for a program manifest, so + that all dependent assemblies of python.exe can be added to the built exe. + + `redirects` may be a list. Any assembly redirects found via policy files will + be added to the list as BindingRedirect objects so they can later be used + to modify any manifests that reference the redirected assembly. + """ + # Extract all necessary binary modules from Python eggs to be included + # directly with PyInstaller. + lTOC = _extract_from_egg(lTOC) + + for nm, pth, typ in lTOC: + if nm.upper() in seen: + continue + logger.debug("Analyzing %s", pth) + seen.add(nm.upper()) + if is_win: + for ftocnm, fn in getAssemblyFiles(pth, manifest, redirects): + lTOC.append((ftocnm, fn, 'BINARY')) + for lib, npth in selectImports(pth, xtrapath): + if lib.upper() in seen or npth.upper() in seen: + continue + seen.add(npth.upper()) + lTOC.append((lib, npth, 'BINARY')) + + return lTOC + + +def pkg_resources_get_default_cache(): + """ + Determine the default cache location + + This returns the ``PYTHON_EGG_CACHE`` environment variable, if set. + Otherwise, on Windows, it returns a 'Python-Eggs' subdirectory of the + 'Application Data' directory. On all other systems, it's '~/.python-eggs'. + """ + # This function borrowed from setuptools/pkg_resources + egg_cache = compat.getenv('PYTHON_EGG_CACHE') + if egg_cache is not None: + return egg_cache + + if os.name != 'nt': + return os.path.expanduser('~/.python-eggs') + + app_data = 'Application Data' # XXX this may be locale-specific! + app_homes = [ + (('APPDATA',), None), # best option, should be locale-safe + (('USERPROFILE',), app_data), + (('HOMEDRIVE', 'HOMEPATH'), app_data), + (('HOMEPATH',), app_data), + (('HOME',), None), + (('WINDIR',), app_data), # 95/98/ME + ] + + for keys, subdir in app_homes: + dirname = '' + for key in keys: + if key in os.environ: + dirname = os.path.join(dirname, compat.getenv(key)) + else: + break + else: + if subdir: + dirname = os.path.join(dirname, subdir) + return os.path.join(dirname, 'Python-Eggs') + else: + raise RuntimeError( + "Please set the PYTHON_EGG_CACHE environment variable" + ) + + +def check_extract_from_egg(pth, todir=None): + r""" + Check if path points to a file inside a python egg file, extract the + file from the egg to a cache directory (following pkg_resources + convention) and return [(extracted path, egg file path, relative path + inside egg file)]. + Otherwise, just return [(original path, None, None)]. + If path points to an egg file directly, return a list with all files + from the egg formatted like above. + + Example: + >>> check_extract_from_egg(r'C:\Python26\Lib\site-packages\my.egg\mymodule\my.pyd') + [(r'C:\Users\UserName\AppData\Roaming\Python-Eggs\my.egg-tmp\mymodule\my.pyd', + r'C:\Python26\Lib\site-packages\my.egg', r'mymodule/my.pyd')] + """ + rv = [] + if os.path.altsep: + pth = pth.replace(os.path.altsep, os.path.sep) + components = pth.split(os.path.sep) + for i, name in enumerate(components): + if name.lower().endswith(".egg"): + eggpth = os.path.sep.join(components[:i + 1]) + if os.path.isfile(eggpth): + # eggs can also be directories! + try: + egg = zipfile.ZipFile(eggpth) + except zipfile.BadZipfile as e: + raise SystemExit("Error: %s %s" % (eggpth, e)) + if todir is None: + # Use the same directory as setuptools/pkg_resources. So, + # if the specific egg was accessed before (not necessarily + # by pyinstaller), the extracted contents already exist + # (pkg_resources puts them there) and can be used. + todir = os.path.join(pkg_resources_get_default_cache(), + name + "-tmp") + if components[i + 1:]: + members = ["/".join(components[i + 1:])] + else: + members = egg.namelist() + for member in members: + pth = os.path.join(todir, member) + if not os.path.isfile(pth): + dirname = os.path.dirname(pth) + if not os.path.isdir(dirname): + os.makedirs(dirname) + with open(pth, "wb") as f: + f.write(egg.read(member)) + rv.append((pth, eggpth, member)) + return rv + return [(pth, None, None)] + + +def getAssemblies(pth): + """ + On Windows return the dependent Side-by-Side (SxS) assemblies of a binary as a + list of Manifest objects. + + Dependent assemblies are required only by binaries compiled with MSVC 9.0. + Python 2.7 and 3.2 is compiled with MSVC 9.0 and thus depends on Microsoft + Redistributable runtime libraries 9.0. + + Python 3.3+ is compiled with version 10.0 and does not use SxS assemblies. + + FIXME: Can this be removed since we now only support Python 3.5+? + FIXME: IS there some test-case covering this? + """ + if pth.lower().endswith(".manifest"): + return [] + # check for manifest file + manifestnm = pth + ".manifest" + if os.path.isfile(manifestnm): + with open(manifestnm, "rb") as fd: + res = {RT_MANIFEST: {1: {0: fd.read()}}} + else: + # check the binary for embedded manifest + try: + res = GetManifestResources(pth) + except winresource.pywintypes.error as exc: + if exc.args[0] == winresource.ERROR_BAD_EXE_FORMAT: + logger.info('Cannot get manifest resource from non-PE ' + 'file %s', pth) + return [] + raise + rv = [] + if RT_MANIFEST in res and len(res[RT_MANIFEST]): + for name in res[RT_MANIFEST]: + for language in res[RT_MANIFEST][name]: + # check the manifest for dependent assemblies + try: + manifest = Manifest() + manifest.filename = ":".join([pth, str(RT_MANIFEST), + str(name), str(language)]) + manifest.parse_string(res[RT_MANIFEST][name][language], + False) + except Exception as exc: + logger.error("Can not parse manifest resource %s, %s" + " from %s", name, language, pth, exc_info=1) + else: + if manifest.dependentAssemblies: + logger.debug("Dependent assemblies of %s:", pth) + logger.debug(", ".join([assembly.getid() + for assembly in + manifest.dependentAssemblies])) + rv.extend(manifest.dependentAssemblies) + return rv + + +def getAssemblyFiles(pth, manifest=None, redirects=None): + """ + Find all assemblies that are dependencies of the given binary and return the files + that make up the assemblies as (name, fullpath) tuples. + + If a WinManifest object is passed as `manifest`, also updates that manifest to + reference the returned assemblies. This is done only to update the built app's .exe + with the dependencies of python.exe + + If a list is passed as `redirects`, and binding redirects in policy files are + applied when searching for assemblies, BindingRedirect objects are appended to this + list. + + Return a list of pairs (name, fullpath) + """ + rv = [] + if manifest: + _depNames = set(dep.name for dep in manifest.dependentAssemblies) + for assembly in getAssemblies(pth): + if assembly.getid().upper() in seen: + continue + if manifest and assembly.name not in _depNames: + # Add assembly as dependency to our final output exe's manifest + logger.info("Adding %s to dependent assemblies " + "of final executable\n required by %s", + assembly.name, pth) + manifest.dependentAssemblies.append(assembly) + _depNames.add(assembly.name) + if not dylib.include_library(assembly.name): + logger.debug("Skipping assembly %s", assembly.getid()) + continue + if assembly.optional: + logger.debug("Skipping optional assembly %s", assembly.getid()) + continue + + from ..config import CONF + if CONF.get("win_no_prefer_redirects"): + files = assembly.find_files() + else: + files = [] + if not len(files): + # If no files were found, it may be the case that the required version + # of the assembly is not installed, and the policy file is redirecting it + # to a newer version. So, we collect the newer version instead. + files = assembly.find_files(ignore_policies=False) + if len(files) and redirects is not None: + # New version was found, old version was not. Add a redirect in the + # app configuration + old_version = assembly.version + new_version = assembly.get_policy_redirect() + logger.info("Adding redirect %s version %s -> %s", + assembly.name, old_version, new_version) + redirects.append(BindingRedirect( + name=assembly.name, + language=assembly.language, + arch=assembly.processorArchitecture, + publicKeyToken=assembly.publicKeyToken, + oldVersion=old_version, + newVersion=new_version, + )) + + if files: + seen.add(assembly.getid().upper()) + for fn in files: + fname, fext = os.path.splitext(fn) + if fext.lower() == ".manifest": + nm = assembly.name + fext + else: + nm = os.path.basename(fn) + ftocnm = nm + if assembly.language not in (None, "", "*", "neutral"): + ftocnm = os.path.join(assembly.getlanguage(), + ftocnm) + nm, ftocnm, fn = [item.encode(sys.getfilesystemencoding()) + for item in + (nm, + ftocnm, + fn)] + if fn.upper() not in seen: + logger.debug("Adding %s", ftocnm) + seen.add(nm.upper()) + seen.add(fn.upper()) + rv.append((ftocnm, fn)) + else: + #logger.info("skipping %s part of assembly %s dependency of %s", + # ftocnm, assembly.name, pth) + pass + else: + logger.error("Assembly %s not found", assembly.getid()) + + # Convert items in list from 'bytes' type to 'str' type. + # NOTE: With Python 3 we somehow get type 'bytes' and it + # then causes other issues and failures with PyInstaller. + new_rv = [] + for item in rv: + a = item[0].decode('ascii') + b = item[1].decode('ascii') + new_rv.append((a, b)) + rv = new_rv + + return rv + + +def selectImports(pth, xtrapath=None): + """ + Return the dependencies of a binary that should be included. + + Return a list of pairs (name, fullpath) + """ + rv = [] + if xtrapath is None: + xtrapath = [os.path.dirname(pth)] + else: + assert isinstance(xtrapath, list) + xtrapath = [os.path.dirname(pth)] + xtrapath # make a copy + dlls = getImports(pth) + for lib in dlls: + if lib.upper() in seen: + continue + if not is_win: + # all other platforms + npth = lib + lib = os.path.basename(lib) + else: + # plain win case + npth = getfullnameof(lib, xtrapath) + + # now npth is a candidate lib if found + # check again for excludes but with regex FIXME: split the list + if npth: + candidatelib = npth + else: + candidatelib = lib + + if not dylib.include_library(candidatelib): + if (candidatelib.find('libpython') < 0 and + candidatelib.find('Python.framework') < 0): + # skip libs not containing (libpython or Python.framework) + if npth.upper() not in seen: + logger.debug("Skipping %s dependency of %s", + lib, os.path.basename(pth)) + continue + else: + pass + + if npth: + if npth.upper() not in seen: + logger.debug("Adding %s dependency of %s from %s", + lib, os.path.basename(pth), npth) + rv.append((lib, npth)) + else: + # Don't spew out false warnings on win 10 and UCRT (see issue + # #1566). + if not (is_win_10 and lib.startswith("api-ms-win-crt")): + logger.warning("lib not found: %s dependency of %s", lib, pth) + + return rv + + +def _getImports_ldd(pth): + """ + Find the binary dependencies of PTH. + + This implementation is for ldd platforms (mostly unix). + """ + rslt = set() + if is_aix: + # Match libs of the form + # 'archivelib.a(objectmember.so/.o)' + # or + # 'sharedlib.so' + # Will not match the fake lib '/unix' + lddPattern = re.compile(r"^\s*(((?P(.*\.a))(?P\(.*\)))|((?P(.*\.so))))$") + elif is_hpux: + # Match libs of the form + # 'sharedlib.so => full-path-to-lib + # e.g. + # 'libpython2.7.so => /usr/local/lib/hpux32/libpython2.7.so' + lddPattern = re.compile(r"^\s+(.*)\s+=>\s+(.*)$") + elif is_solar: + # Match libs of the form + # 'sharedlib.so => full-path-to-lib + # e.g. + # 'libpython2.7.so.1.0 => /usr/local/lib/libpython2.7.so.1.0' + # Will not match the platform specific libs starting with '/platform' + lddPattern = re.compile(r"^\s+(.*)\s+=>\s+(.*)$") + else: + lddPattern = re.compile(r"\s*(.*?)\s+=>\s+(.*?)\s+\(.*\)") + + for line in compat.exec_command('ldd', pth).splitlines(): + m = lddPattern.search(line) + if m: + if is_aix: + libarchive = m.group('libarchive') + if libarchive: + # We matched an archive lib with a request for a particular + # embedded shared object. + # 'archivelib.a(objectmember.so/.o)' + lib = libarchive + name = os.path.basename(lib) + m.group('objectmember') + else: + # We matched a stand-alone shared library. + # 'sharedlib.so' + lib = m.group('libshared') + name = os.path.basename(lib) + elif is_hpux: + name, lib = m.group(1), m.group(2) + else: + name, lib = m.group(1), m.group(2) + if name[:10] in ('linux-gate', 'linux-vdso'): + # linux-gate is a fake library which does not exist and + # should be ignored. See also: + # http://www.trilithium.com/johan/2005/08/linux-gate/ + continue + + if is_cygwin: + # exclude Windows system library + if lib.lower().startswith('/cygdrive/c/windows/system'): + continue + + if os.path.exists(lib): + # Add lib if it is not already found. + if lib not in rslt: + rslt.add(lib) + else: + logger.error('Can not find %s in path %s (needed by %s)', + name, lib, pth) + return rslt + + +def _getImports_macholib(pth): + """ + Find the binary dependencies of PTH. + + This implementation is for Mac OS X and uses library macholib. + """ + from macholib.MachO import MachO + from macholib.mach_o import LC_RPATH + from macholib.dyld import dyld_find + from macholib.util import in_system_path + rslt = set() + seen = set() # Libraries read from binary headers. + + ## Walk through mach binary headers. + + m = MachO(pth) + for header in m.headers: + for idx, name, lib in header.walkRelocatables(): + # Sometimes some libraries are present multiple times. + if lib not in seen: + seen.add(lib) + + # Walk through mach binary headers and look for LC_RPATH. + # macholib can't handle @rpath. LC_RPATH has to be read + # from the MachO header. + # TODO Do we need to remove LC_RPATH from MachO load commands? + # Will it cause any harm to leave them untouched? + # Removing LC_RPATH should be implemented when getting + # files from the bincache if it is necessary. + run_paths = set() + for header in m.headers: + for command in header.commands: + # A command is a tupple like: + # (, + # , + # '../lib\x00\x00') + cmd_type = command[0].cmd + if cmd_type == LC_RPATH: + rpath = command[2].decode('utf-8') + # Remove trailing '\x00' characters. + # e.g. '../lib\x00\x00' + rpath = rpath.rstrip('\x00') + # Replace the @executable_path and @loader_path keywords + # with the actual path to the binary. + executable_path = os.path.dirname(pth) + rpath = re.sub('^@(executable_path|loader_path|rpath)(/|$)', + executable_path + r'\2', rpath) + # Make rpath absolute. According to Apple doc LC_RPATH + # is always relative to the binary location. + rpath = os.path.normpath(os.path.join(executable_path, rpath)) + run_paths.update([rpath]) + else: + # Frameworks that have this structure Name.framework/Versions/N/Name + # need to to search at the same level as the framework dir. + # This is specifically needed so that the QtWebEngine dependencies + # can be found. + if '.framework' in pth: + run_paths.update(['../../../']) + + # for distributions like Anaconda, all of the dylibs are stored in the lib directory + # of the Python distribution, not alongside of the .so's in each module's subdirectory. + run_paths.add(os.path.join(base_prefix, 'lib')) + + ## Try to find files in file system. + + # In cases with @loader_path or @executable_path + # try to look in the same directory as the checked binary is. + # This seems to work in most cases. + exec_path = os.path.abspath(os.path.dirname(pth)) + + + for lib in seen: + + # Suppose that @rpath is not used for system libraries and + # using macholib can be avoided. + # macholib can't handle @rpath. + if lib.startswith('@rpath'): + lib = lib.replace('@rpath', '.') # Make path relative. + final_lib = None # Absolute path to existing lib on disk. + # Try multiple locations. + for run_path in run_paths: + # @rpath may contain relative value. Use exec_path as + # base path. + if not os.path.isabs(run_path): + run_path = os.path.join(exec_path, run_path) + # Stop looking for lib when found in first location. + if os.path.exists(os.path.join(run_path, lib)): + final_lib = os.path.abspath(os.path.join(run_path, lib)) + rslt.add(final_lib) + break + # Log error if no existing file found. + if not final_lib: + logger.error('Can not find path %s (needed by %s)', lib, pth) + + # Macholib has to be used to get absolute path to libraries. + else: + # macholib can't handle @loader_path. It has to be + # handled the same way as @executable_path. + # It is also replaced by 'exec_path'. + if lib.startswith('@loader_path'): + lib = lib.replace('@loader_path', '@executable_path') + try: + lib = dyld_find(lib, executable_path=exec_path) + rslt.add(lib) + except ValueError: + # Starting with Big Sur, system libraries are hidden. And + # we do not collect system libraries on any macOS version + # anyway, so suppress the corresponding error messages. + if not in_system_path(lib): + logger.error('Can not find path %s (needed by %s)', + lib, pth) + + return rslt + + +def getImports(pth): + """ + Forwards to the correct getImports implementation for the platform. + """ + if is_win: + if pth.lower().endswith(".manifest"): + return [] + try: + return _getImports_pe(pth) + except Exception as exception: + # Assemblies can pull in files which aren't necessarily PE, + # but are still needed by the assembly. Any additional binary + # dependencies should already have been handled by + # selectAssemblies in that case, so just warn, return an empty + # list and continue. + # For less specific errors also log the traceback. + logger.warning('Can not get binary dependencies for file: %s', pth) + logger.warning( + ' Reason: %s', exception, + exc_info=not isinstance(exception, pefile.PEFormatError)) + return [] + elif is_darwin: + return _getImports_macholib(pth) + else: + return _getImports_ldd(pth) + + +def findLibrary(name): + """ + Look for a library in the system. + + Emulate the algorithm used by dlopen. + `name`must include the prefix, e.g. ``libpython2.4.so`` + """ + assert is_unix, ("Current implementation for Unix only (Linux, Solaris, " + "AIX, FreeBSD)") + + lib = None + + # Look in the LD_LIBRARY_PATH according to platform. + if is_aix: + lp = compat.getenv('LIBPATH', '') + elif is_darwin: + lp = compat.getenv('DYLD_LIBRARY_PATH', '') + else: + lp = compat.getenv('LD_LIBRARY_PATH', '') + for path in lp.split(os.pathsep): + libs = glob(os.path.join(path, name + '*')) + if libs: + lib = libs[0] + break + + # Look in /etc/ld.so.cache + # Solaris does not have /sbin/ldconfig. Just check if this file exists. + if lib is None: + utils.load_ldconfig_cache() + lib = utils.LDCONFIG_CACHE.get(name) + if lib: + assert os.path.isfile(lib) + + # Look in the known safe paths. + if lib is None: + # Architecture independent locations. + paths = ['/lib', '/usr/lib'] + # Architecture dependent locations. + if compat.architecture == '32bit': + paths.extend(['/lib32', '/usr/lib32', '/usr/lib/i386-linux-gnu']) + else: + paths.extend(['/lib64', '/usr/lib64', '/usr/lib/x86_64-linux-gnu']) + + + # On Debian/Ubuntu /usr/bin/python is linked statically with libpython. + # Newer Debian/Ubuntu with multiarch support putsh the libpythonX.Y.so + # To paths like /usr/lib/i386-linux-gnu/. + try: + # Module available only in Python 2.7+ + import sysconfig + # 'multiarchsubdir' works on Debian/Ubuntu only in Python 2.7 and 3.3+. + arch_subdir = sysconfig.get_config_var('multiarchsubdir') + # Ignore if None is returned. + if arch_subdir: + arch_subdir = os.path.basename(arch_subdir) + paths.append(os.path.join('/usr/lib', arch_subdir)) + else: + logger.debug('Multiarch directory not detected.') + except ImportError: + logger.debug('Multiarch directory not detected.') + + if is_aix: + paths.append('/opt/freeware/lib') + elif is_hpux: + if compat.architecture == '32bit': + paths.append('/usr/local/lib/hpux32') + else: + paths.append('/usr/local/lib/hpux64') + elif is_freebsd or is_openbsd: + paths.append('/usr/local/lib') + for path in paths: + libs = glob(os.path.join(path, name + '*')) + if libs: + lib = libs[0] + break + + # give up :( + if lib is None: + return None + + # Resolve the file name into the soname + if is_freebsd or is_aix or is_openbsd: + # On FreeBSD objdump doesn't show SONAME, + # and on AIX objdump does not exist, + # so we just return the lib we've found + return lib + else: + dir = os.path.dirname(lib) + return os.path.join(dir, _get_so_name(lib)) + + +def _get_so_name(filename): + """ + Return the soname of a library. + + Soname is usefull whene there are multiple symplinks to one library. + """ + # TODO verify that objdump works on other unixes and not Linux only. + cmd = ["objdump", "-p", filename] + pattern = r'\s+SONAME\s+([^\s]+)' + if is_solar: + cmd = ["elfdump", "-d", filename] + pattern = r'\s+SONAME\s+[^\s]+\s+([^\s]+)' + m = re.search(pattern, compat.exec_command(*cmd)) + return m.group(1) + + +def get_python_library_path(): + """ + Find dynamic Python library that will be bundled with frozen executable. + + NOTOE: This is a fallback option when Python library is probably linked + statically with the Python executable and we need to search more for it. + On Debian/Ubuntu this is the case. + + Return full path to Python dynamic library or None when not found. + + + We need to know name of the Python dynamic library for the bootloader. + Bootloader has to know what library to load and not trying to guess. + + Some linux distributions (e.g. debian-based) statically build the + Python executable to the libpython, so bindepend doesn't include + it in its output. In this situation let's try to find it. + + Darwin custom builds could possibly also have non-framework style libraries, + so this method also checks for that variant as well. + """ + def _find_lib_in_libdirs(*libdirs): + for libdir in libdirs: + for name in PYDYLIB_NAMES: + full_path = os.path.join(libdir, name) + if os.path.exists(full_path): + return full_path + return None + + # Try to get Python library name from the Python executable. It assumes that Python + # library is not statically linked. + executable = getattr(sys, '_base_executable', sys.executable) + dlls = getImports(executable) + for filename in dlls: + for name in PYDYLIB_NAMES: + if os.path.basename(filename) == name: + # On Windows filename is just like 'python27.dll'. Convert it + # to absolute path. + if is_win and not os.path.isabs(filename): + filename = getfullnameof(filename) + # Python library found. Return absolute path to it. + return filename + + # Python library NOT found. Resume searching using alternative methods. + + # Work around for python venv having VERSION.dll rather than pythonXY.dll + if is_win and 'VERSION.dll' in dlls: + pydll = 'python%d%d.dll' % sys.version_info[:2] + return getfullnameof(pydll) + + # Applies only to non Windows platforms and conda. + + if is_conda: + # Conda needs to be the first here since it overrules the operating + # system specific paths. + python_libname = _find_lib_in_libdirs( + os.path.join(compat.base_prefix, 'lib')) + if python_libname: + return python_libname + + elif is_unix: + for name in PYDYLIB_NAMES: + python_libname = findLibrary(name) + if python_libname: + return python_libname + + elif is_darwin: + # On MacPython, Analysis.assemble is able to find the libpython with + # no additional help, asking for sys.executable dependencies. + # However, this fails on system python, because the shared library + # is not listed as a dependency of the binary (most probably it's + # opened at runtime using some dlopen trickery). + # This happens on Mac OS X when Python is compiled as Framework. + + # Python compiled as Framework contains same values in sys.prefix + # and exec_prefix. That's why we can use just sys.prefix. + # In virtualenv PyInstaller is not able to find Python library. + # We need special care for this case. + python_libname = _find_lib_in_libdirs(compat.base_prefix) + if python_libname: + return python_libname + + # Python library NOT found. Provide helpful feedback. + msg = """Python library not found: %s + This would mean your Python installation doesn't come with proper library files. + This usually happens by missing development package, or unsuitable build parameters of Python installation. + + * On Debian/Ubuntu, you would need to install Python development packages + * apt-get install python3-dev + * apt-get install python-dev + * If you're building Python by yourself, please rebuild your Python with `--enable-shared` (or, `--enable-framework` on Darwin) + """ % (", ".join(PYDYLIB_NAMES),) + raise IOError(msg) + + +def findSystemLibrary(name): + ''' + Given a library name, try to resolve the path to that library. If the + path is already an absolute path, return that without searching. + ''' + + if os.path.isabs(name): + return name + + if is_unix: + return findLibrary(name) + elif is_win: + return getfullnameof(name) + else: + # This seems to work, and is similar to what we have above.. + return ctypes.util.find_library(name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/depend/dylib.py b/3rdparty/pyinstaller-4.3/PyInstaller/depend/dylib.py new file mode 100644 index 0000000000000000000000000000000000000000..28b76704c8fc17e6c8ec1d5e1212773868da0378 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/depend/dylib.py @@ -0,0 +1,396 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Manipulating with dynamic libraries. +""" + +import os.path + +from PyInstaller.utils.win32 import winutils + + +__all__ = ['exclude_list', 'include_list', 'include_library'] + + +import os +import re + + +from PyInstaller.compat import is_win, is_unix, is_aix, is_darwin + + +import PyInstaller.log as logging +logger = logging.getLogger(__name__) + + +_BOOTLOADER_FNAMES = {'run', 'run_d', 'runw', 'runw_d'} + + +# Ignoring some system libraries speeds up packaging process +_excludes = { + # Ignore annoying warnings with Windows system DLLs. + # + # 'W: library kernel32.dll required via ctypes not found' + # 'W: library coredll.dll required via ctypes not found' + # + # These these dlls has to be ignored for all operating systems + # because they might be resolved when scanning code for ctypes + # dependencies. + r'advapi32\.dll', + r'ws2_32\.dll', + r'gdi32\.dll', + r'oleaut32\.dll', + r'shell32\.dll', + r'ole32\.dll', + + r'coredll\.dll', + r'crypt32\.dll', + r'kernel32', + r'kernel32\.dll', + r'msvcrt\.dll', + r'rpcrt4\.dll', + r'user32\.dll', + # Some modules tries to import the Python library. + # e.g. pyreadline.console.console + r'python\%s\%s', +} + +# Regex includes - overrides excludes. +# Include list is used only to override specific libraries +# from exclude list. +_includes = set() + + +_win_includes = { + # DLLs are from 'Microsoft Visual C++ 2010 Redistributable Package'. + # http://msdn.microsoft.com/en-us/library/8kche8ah(v=vs.100).aspx + # + # Python 3.3 and 3.4 depends use Visual Studio C++ 2010 for Windows builds. + # python33.dll depends on msvcr100.dll. + # + # Visual Studio C++ 2010 does not need Assembly manifests anymore and + # uses C++ runtime libraries the old way - pointing to C:\Windows\System32. + # It is necessary to allow inclusion of these libraries from + # C:\Windows\System32. + r'atl100\.dll', + r'msvcr100\.dll', + r'msvcp100\.dll', + r'mfc100\.dll', + r'mfc100u\.dll', + r'mfcmifc80\.dll', + r'mfcm100\.dll', + r'mfcm100u\.dll', + + # Python 3.5 uses the Univeral C Runtime which consists of these DLLs: + r'api-ms-win-core.*', + r'api-ms-win-crt.*', + r'ucrtbase\.dll', + r'vcruntime140\.dll', + + # Allow pythonNN.dll, pythoncomNN.dll, pywintypesNN.dll + r'py(?:thon(?:com(?:loader)?)?|wintypes)\d+\.dll', +} + +_win_excludes = { + # On Windows, only .dll files can be loaded. + r'.*\.so', + r'.*\.dylib', + + # MS assembly excludes + r'Microsoft\.Windows\.Common-Controls', +} + + +_unix_excludes = { + r'libc\.so(\..*)?', + r'libdl\.so(\..*)?', + r'libm\.so(\..*)?', + r'libpthread\.so(\..*)?', + r'librt\.so(\..*)?', + r'libthread_db\.so(\..*)?', + # glibc regex excludes. + r'ld-linux\.so(\..*)?', + r'libBrokenLocale\.so(\..*)?', + r'libanl\.so(\..*)?', + r'libcidn\.so(\..*)?', + r'libcrypt\.so(\..*)?', + r'libnsl\.so(\..*)?', + r'libnss_compat.*\.so(\..*)?', + r'libnss_dns.*\.so(\..*)?', + r'libnss_files.*\.so(\..*)?', + r'libnss_hesiod.*\.so(\..*)?', + r'libnss_nis.*\.so(\..*)?', + r'libnss_nisplus.*\.so(\..*)?', + r'libresolv\.so(\..*)?', + r'libutil\.so(\..*)?', + # graphical interface libraries come with graphical stack (see libglvnd) + r'libE?(Open)?GLX?(ESv1_CM|ESv2)?(dispatch)?\.so(\..*)?', + r'libdrm\.so(\..*)?', + # libxcb-dri changes ABI frequently (e.g.: between Ubuntu LTS releases) and + # is usually installed as dependency of the graphics stack anyway. No need + # to bundle it. + r'libxcb\.so(\..*)?', + r'libxcb-dri.*\.so(\..*)?', +} + +_aix_excludes = { + r'libbz2\.a', + r'libc\.a', + r'libC\.a', + r'libcrypt\.a', + r'libdl\.a', + r'libintl\.a', + r'libpthreads\.a', + r'librt\\.a', + r'librtl\.a', + r'libz\.a', +} + + +if is_win: + _includes |= _win_includes + _excludes |= _win_excludes +elif is_aix: + # The exclude list for AIX differs from other *nix platforms. + _excludes |= _aix_excludes +elif is_unix: + # Common excludes for *nix platforms -- except AIX. + _excludes |= _unix_excludes + + +class ExcludeList(object): + def __init__(self): + self.regex = re.compile('|'.join(_excludes), re.I) + + def search(self, libname): + # Running re.search() on '' regex never returns None. + if _excludes: + return self.regex.match(os.path.basename(libname)) + else: + return False + + +class IncludeList(object): + def __init__(self): + self.regex = re.compile('|'.join(_includes), re.I) + + def search(self, libname): + # Running re.search() on '' regex never returns None. + if _includes: + return self.regex.match(os.path.basename(libname)) + else: + return False + + +exclude_list = ExcludeList() +include_list = IncludeList() + + +if is_darwin: + # On Mac use macholib to decide if a binary is a system one. + from macholib import util + + class MacExcludeList(object): + def __init__(self, global_exclude_list): + # Wraps the global 'exclude_list' before it is overriden + # by this class. + self._exclude_list = global_exclude_list + + def search(self, libname): + # First try global exclude list. If it matches then + # return it's result otherwise continue with other check. + result = self._exclude_list.search(libname) + if result: + return result + else: + return util.in_system_path(libname) + + exclude_list = MacExcludeList(exclude_list) + +elif is_win: + class WinExcludeList(object): + def __init__(self, global_exclude_list): + self._exclude_list = global_exclude_list + # use normpath because msys2 uses / instead of \ + self._windows_dir = os.path.normpath( + winutils.get_windows_dir().lower() + ) + + def search(self, libname): + libname = libname.lower() + result = self._exclude_list.search(libname) + if result: + return result + else: + # Exclude everything from the Windows directory by default. + # .. sometimes realpath changes the case of libname, lower it + # .. use normpath because msys2 uses / instead of \ + fn = os.path.normpath(os.path.realpath(libname).lower()) + return fn.startswith(self._windows_dir) + + exclude_list = WinExcludeList(exclude_list) + + +def include_library(libname): + """ + Check if a dynamic library should be included with application or not. + """ + # For configuration phase we need to have exclude / include lists None + # so these checking is skipped and library gets included. + if exclude_list: + if exclude_list.search(libname) and not include_list.search(libname): + # Library is excluded and is not overriden by include list. + # It should be then excluded. + return False + else: + # Include library + return True + else: + # By default include library. + return True + + +def mac_set_relative_dylib_deps(libname, distname): + """ + On Mac OS X set relative paths to dynamic library dependencies + of `libname`. + + Relative paths allow to avoid using environment variable DYLD_LIBRARY_PATH. + There are known some issues with DYLD_LIBRARY_PATH. Relative paths is + more flexible mechanism. + + Current location of dependend libraries is derived from the location + of the library path (paths start with '@loader_path'). + + 'distname' path of the library relative to dist directory of frozen + executable. We need this to determine the level of directory + level for @loader_path of binaries not found in dist directory. + + E.g. qt4 plugins are not in the same directory as Qt*.dylib + files. Without using '@loader_path/../..' for qt plugins + Mac OS X would not be able to resolve shared library + dependencies and qt plugins will not be loaded. + """ + + from macholib import util + from macholib.MachO import MachO + + # Ignore bootloader otherwise PyInstaller fails with exception like + # 'ValueError: total_size > low_offset (288 > 0)' + if os.path.basename(libname) in _BOOTLOADER_FNAMES: + return + + # Determine how many directories up is the directory with shared + # dynamic libraries. '../' + # E.g. ./qt4_plugins/images/ -> ./../../ + parent_dir = '' + # Check if distname is not only base filename. + if os.path.dirname(distname): + parent_level = len(os.path.dirname(distname).split(os.sep)) + parent_dir = parent_level * (os.pardir + os.sep) + + def match_func(pth): + """ + For system libraries is still used absolute path. It is unchanged. + """ + # Leave system dynamic libraries unchanged + if util.in_system_path(pth): + return None + + # The older python.org builds that use system Tcl/Tk framework + # have their _tkinter.cpython-*-darwin.so library linked against + # /Library/Frameworks/Tcl.framework/Versions/8.5/Tcl and + # /Library/Frameworks/Tk.framework/Versions/8.5/Tk, although the + # actual frameworks are located in /System/Library/Frameworks. + # Therefore, they slip through the above in_system_path() check, + # and we need to exempt them manually. + _exemptions = [ + '/Library/Frameworks/Tcl.framework/', + '/Library/Frameworks/Tk.framework/' + ] + if any([x in pth for x in _exemptions]): + return None + + # Use relative path to dependent dynamic libraries based on the + # location of the executable. + return os.path.join('@loader_path', parent_dir, os.path.basename(pth)) + + # Rewrite mach headers with @loader_path. + dll = MachO(libname) + dll.rewriteLoadCommands(match_func) + + # Write changes into file. + # Write code is based on macholib example. + try: + with open(dll.filename, 'rb+') as f: + for header in dll.headers: + f.seek(0) + dll.write(f) + f.seek(0, 2) + f.flush() + except Exception: + pass + + +def mac_is_binary_signed(filename): + """ + Check if the given macOS binary file is signed. + """ + from macholib.MachO import MachO + from macholib import mach_o # constants + + # Open the file + try: + m = MachO(filename) + except Exception: + return False + + # Walk over all headers and check if any contains LC_CODE_SIGNATURE + # load command + for header in m.headers: + for cmd in header.commands: + if cmd[0].cmd == mach_o.LC_CODE_SIGNATURE: + return True + return False + + +def mac_strip_signature(libname, distname): + """ + On macOS, strip away the signature from the binary file. As we may + not be collecting all components from a signed framework bundle, the + collection may invalidate the existing signature on a collected + shared library, which will prevent the latter from being loaded. + """ + from ..compat import exec_command_rc + + # For now, limit this only to Python shared library. Other shared + # library files from Python.framework bundle also seem to be signed, + # but their signature is not invalidated by partial collection like + # it is for Python library... + if os.path.basename(libname) != 'Python': + return + if not mac_is_binary_signed(libname): + return + # Run codesign --remove-signature libname + try: + logger.debug("Removing signature from %s", libname) + result = exec_command_rc('codesign', '--remove-signature', libname) + except Exception as e: + logger.warning( + "Failed to run 'codesign' to remove signature from %s: %r", + libname, e) + return + if result != 0: + logger.warning( + "'codesign --remove-signature %s' returned non-zero status %d", + libname, result) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/depend/imphook.py b/3rdparty/pyinstaller-4.3/PyInstaller/depend/imphook.py new file mode 100644 index 0000000000000000000000000000000000000000..e504a1e9cbad1d8dcdd1aac5082db13720325a79 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/depend/imphook.py @@ -0,0 +1,627 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 related to processing of import hooks. +""" + +import glob, sys, weakref +import os.path + +from ..exceptions import ImportErrorWhenRunningHook +from .. import log as logging +from ..compat import expand_path, importlib_load_source +from .imphookapi import PostGraphAPI +from ..building.utils import format_binaries_and_datas + +logger = logging.getLogger(__name__) + +# Safety check: Hook module names need to be unique. Duplicate names might +# occur if the cached PyuModuleGraph has an issue. +HOOKS_MODULE_NAMES = set() + +class ModuleHookCache(dict): + """ + Cache of lazily loadable hook script objects. + + This cache is implemented as a `dict` subclass mapping from the + fully-qualified names of all modules with at least one hook script to lists + of `ModuleHook` instances encapsulating these scripts. As a `dict` subclass, + all cached module names and hook scripts are accessible via standard + dictionary operations. + + Attributes + ---------- + module_graph : ModuleGraph + Current module graph. + _hook_module_name_prefix : str + String prefixing the names of all in-memory modules lazily loaded from + cached hook scripts. See also the `hook_module_name_prefix` parameter + passed to the `ModuleHook.__init__()` method. + """ + + _cache_id_next = 0 + """ + 0-based identifier unique to the next `ModuleHookCache` to be instantiated. + + This identifier is incremented on each instantiation of a new + `ModuleHookCache` to isolate in-memory modules of lazily loaded hook scripts + in that cache to the same cache-specific namespace, preventing edge-case + collisions with existing in-memory modules in other caches. + """ + + def __init__(self, module_graph, hook_dirs): + """ + Cache all hook scripts in the passed directories. + + **Order of caching is significant** with respect to hooks for the same + module, as the values of this dictionary are lists. Hooks for the same + module will be run in the order in which they are cached. Previously + cached hooks are always preserved rather than overidden. + + By default, official hooks are cached _before_ user-defined hooks. For + modules with both official and user-defined hooks, this implies that the + former take priority over and hence will be loaded _before_ the latter. + + Parameters + ---------- + module_graph : ModuleGraph + Current module graph. + hook_dirs : list + List of the absolute or relative paths of all directories containing + **hook scripts** (i.e., Python scripts with filenames matching + `hook-{module_name}.py`, where `{module_name}` is the module hooked + by that script) to be cached. + """ + super(ModuleHookCache, self).__init__() + + # To avoid circular references and hence increased memory consumption, + # a weak rather than strong reference is stored to the passed graph. + # Since this graph is guaranteed to live longer than this cache, this is + # guaranteed to be safe. + self.module_graph = weakref.proxy(module_graph) + + # String unique to this cache prefixing the names of all in-memory + # modules lazily loaded from cached hook scripts, privatized for safety. + self._hook_module_name_prefix = '__PyInstaller_hooks_{}_'.format( + ModuleHookCache._cache_id_next) + ModuleHookCache._cache_id_next += 1 + + # Cache all hook scripts in the passed directories. + self._cache_hook_dirs(hook_dirs) + + + def _cache_hook_dirs(self, hook_dirs): + """ + Cache all hook scripts in the passed directories. + + Parameters + ---------- + hook_dirs : list + List of the absolute or relative paths of all directories containing + hook scripts to be cached. + """ + + for hook_dir in hook_dirs: + # Canonicalize this directory's path and validate its existence. + hook_dir = os.path.abspath(expand_path(hook_dir)) + if not os.path.isdir(hook_dir): + raise FileNotFoundError( + 'Hook directory "{}" not found.'.format(hook_dir)) + + # For each hook script in this directory... + hook_filenames = glob.glob(os.path.join(hook_dir, 'hook-*.py')) + for hook_filename in hook_filenames: + # Fully-qualified name of this hook's corresponding module, + # constructed by removing the "hook-" prefix and ".py" suffix. + module_name = os.path.basename(hook_filename)[5:-3] + if module_name in self: + logger.warning("Several hooks defined for module %r. " + "Please take care they do not conflict.", + module_name) + + # Lazily loadable hook object. + module_hook = ModuleHook( + module_graph=self.module_graph, + module_name=module_name, + hook_filename=hook_filename, + hook_module_name_prefix=self._hook_module_name_prefix, + ) + + # Add this hook to this module's list of hooks. + module_hooks = self.setdefault(module_name, []) + module_hooks.append(module_hook) + + + def remove_modules(self, *module_names): + """ + Remove the passed modules and all hook scripts cached for these modules + from this cache. + + Parameters + ---------- + module_names : list + List of all fully-qualified module names to be removed. + """ + + for module_name in module_names: + # Unload this module's hook script modules from memory. Since these + # are top-level pure-Python modules cached only in the "sys.modules" + # dictionary, popping these modules from this dictionary suffices + # to garbage collect these modules. + module_hooks = self.get(module_name, []) + for module_hook in module_hooks: + sys.modules.pop(module_hook.hook_module_name, None) + + # Remove this module and its hook script objects from this cache. + self.pop(module_name, None) + +# Dictionary mapping the names of magic attributes required by the "ModuleHook" +# class to 2-tuples "(default_type, sanitizer_func)", where: +# +# * "default_type" is the type to which that attribute will be initialized when +# that hook is lazily loaded. +# * "sanitizer_func" is the callable sanitizing the original value of that +# attribute defined by that hook into a safer value consumable by "ModuleHook" +# callers if any or "None" if the original value requires no sanitization. +# +# To avoid subtleties in the ModuleHook.__getattr__() method, this dictionary is +# declared as a module rather than a class attribute. If declared as a class +# attribute and then undefined (...for whatever reason), attempting to access +# this attribute from that method would produce infinite recursion. +_MAGIC_MODULE_HOOK_ATTRS = { + # Collections in which order is insignificant. This includes: + # + # * "datas", sanitized from hook-style 2-tuple lists defined by hooks into + # TOC-style 2-tuple sets consumable by "ModuleHook" callers. + # * "binaries", sanitized in the same way. + 'datas': (set, format_binaries_and_datas), + 'binaries': (set, format_binaries_and_datas), + 'excludedimports': (set, None), + + # Collections in which order is significant. This includes: + # + # * "hiddenimports", as order of importation is significant. On module + # importation, hook scripts are loaded and hook functions declared by + # these scripts are called. As these scripts and functions can have side + # effects dependent on module importation order, module importation itself + # can have side effects dependent on this order! + 'hiddenimports': (list, None), +} + +class ModuleHook(object): + """ + Cached object encapsulating a lazy loadable hook script. + + This object exposes public attributes (e.g., `datas`) of the underlying hook + script as attributes of the same name of this object. On the first access of + any such attribute, this hook script is lazily loaded into an in-memory + private module reused on subsequent accesses. These dynamic attributes are + referred to as "magic." All other static attributes of this object (e.g., + `hook_module_name`) are referred to as "non-magic." + + Attributes (Magic) + ---------- + datas : set + Set of `TOC`-style 2-tuples `(target_file, source_file)` for all + external non-executable files required by the module being hooked, + converted from the `datas` list of hook-style 2-tuples + `(source_dir_or_glob, target_dir)` defined by this hook script. + binaries : set + Set of `TOC`-style 2-tuples `(target_file, source_file)` for all + external executable files required by the module being hooked, converted + from the `binaries` list of hook-style 2-tuples + `(source_dir_or_glob, target_dir)` defined by this hook script. + excludedimports : set + Set of the fully-qualified names of all modules imported by the module + being hooked to be ignored rather than imported from that module, + converted from the `excludedimports` list defined by this hook script. + These modules will only be "locally" rather than "globally" ignored. + These modules will remain importable from all modules other than the + module being hooked. + hiddenimports : set + Set of the fully-qualified names of all modules imported by the module + being hooked that are _not_ automatically detectable by PyInstaller + (usually due to being dynamically imported in that module), converted + from the `hiddenimports` list defined by this hook script. + + Attributes (Non-magic) + ---------- + module_graph : ModuleGraph + Current module graph. + module_name : str + Name of the module hooked by this hook script. + hook_filename : str + Absolute or relative path of this hook script. + hook_module_name : str + Name of the in-memory module of this hook script's interpreted contents. + _hook_module : module + In-memory module of this hook script's interpreted contents, lazily + loaded on the first call to the `_load_hook_module()` method _or_ `None` + if this method has yet to be accessed. + """ + + ## Magic + + def __init__(self, module_graph, module_name, hook_filename, + hook_module_name_prefix): + """ + Initialize this metadata. + + Parameters + ---------- + module_graph : ModuleGraph + Current module graph. + module_name : str + Name of the module hooked by this hook script. + hook_filename : str + Absolute or relative path of this hook script. + hook_module_name_prefix : str + String prefixing the name of the in-memory module for this hook + script. To avoid namespace clashes with similar modules created by + other `ModuleHook` objects in other `ModuleHookCache` containers, + this string _must_ be unique to the `ModuleHookCache` container + containing this `ModuleHook` object. If this string is non-unique, + an existing in-memory module will be erroneously reused when lazily + loading this hook script, thus erroneously resanitizing previously + sanitized hook script attributes (e.g., `datas`) with the + `format_binaries_and_datas()` helper. + """ + + # Note that the passed module graph is already a weak reference, + # avoiding circular reference issues. See ModuleHookCache.__init__(). + # TODO: Add a failure message + assert isinstance(module_graph, weakref.ProxyTypes) + self.module_graph = module_graph + self.module_name = module_name + self.hook_filename = hook_filename + + # Name of the in-memory module fabricated to refer to this hook script. + self.hook_module_name = ( + hook_module_name_prefix + self.module_name.replace('.', '_')) + + # Safety check, see above + global HOOKS_MODULE_NAMES + if self.hook_module_name in HOOKS_MODULE_NAMES: + # When self._shallow is true, this class never loads the hook and + # sets the attributes to empty values + self._shallow = True + else: + self._shallow = False + HOOKS_MODULE_NAMES.add(self.hook_module_name) + + # Attributes subsequently defined by the _load_hook_module() method. + self._hook_module = None + + + def __getattr__(self, attr_name): + """ + Get the magic attribute with the passed name (e.g., `datas`) from this + lazily loaded hook script if any _or_ raise `AttributeError` otherwise. + + This special method is called only for attributes _not_ already defined + by this object. This includes undefined attributes and the first attempt + to access magic attributes. + + This special method is _not_ called for subsequent attempts to access + magic attributes. The first attempt to access magic attributes defines + corresponding instance variables accessible via the `self.__dict__` + instance dictionary (e.g., as `self.datas`) without calling this method. + This approach also allows magic attributes to be deleted from this + object _without_ defining the `__delattr__()` special method. + + See Also + ---------- + Class docstring for supported magic attributes. + """ + + # If this is a magic attribute, initialize this attribute by lazy + # loading this hook script and then return this attribute. To avoid + # recursion, the superclass method rather than getattr() is called. + if attr_name in _MAGIC_MODULE_HOOK_ATTRS: + self._load_hook_module() + return super(ModuleHook, self).__getattr__(attr_name) + # Else, this is an undefined attribute. Raise an exception. + else: + raise AttributeError(attr_name) + + + def __setattr__(self, attr_name, attr_value): + """ + Set the attribute with the passed name to the passed value. + + If this is a magic attribute, this hook script will be lazily loaded + before setting this attribute. Unlike `__getattr__()`, this special + method is called to set _any_ attribute -- including magic, non-magic, + and undefined attributes. + + See Also + ---------- + Class docstring for supported magic attributes. + """ + + # If this is a magic attribute, initialize this attribute by lazy + # loading this hook script before overwriting this attribute. + if attr_name in _MAGIC_MODULE_HOOK_ATTRS: + self._load_hook_module() + + # Set this attribute to the passed value. To avoid recursion, the + # superclass method rather than setattr() is called. + return super(ModuleHook, self).__setattr__(attr_name, attr_value) + + + ## Loading + + def _load_hook_module(self): + """ + Lazily load this hook script into an in-memory private module. + + This method (and, indeed, this class) preserves all attributes and + functions defined by this hook script as is, ensuring sane behaviour in + hook functions _not_ expecting unplanned external modification. Instead, + this method copies public attributes defined by this hook script + (e.g., `binaries`) into private attributes of this object, which the + special `__getattr__()` and `__setattr__()` methods safely expose to + external callers. For public attributes _not_ defined by this hook + script, the corresponding private attributes will be assigned sane + defaults. For some public attributes defined by this hook script, the + corresponding private attributes will be transformed into objects more + readily and safely consumed elsewhere by external callers. + + See Also + ---------- + Class docstring for supported attributes. + """ + + # If this hook script module has already been loaded, + # or we are _shallow, noop. + if self._hook_module is not None or self._shallow: + if self._shallow: + self._hook_module = True # Not None + # Inform the user + logger.debug( + 'Skipping module hook %r from %r because a hook for %s has' + ' already been loaded.', + *os.path.split(self.hook_filename)[::-1], self.module_name + ) + # Set the default attributes to empty instances of the type. + for attr_name, \ + (attr_type, _) in _MAGIC_MODULE_HOOK_ATTRS.items(): + super(ModuleHook, self).__setattr__(attr_name, attr_type()) + return + + # Load and execute the hook script. Even if mechanisms from the import + # machinery are used, this does not import the hook as the module. + head, tail = os.path.split(self.hook_filename) + logger.info( + 'Loading module hook %r from %r...', tail, head) + try: + self._hook_module = importlib_load_source( + self.hook_module_name, self.hook_filename) + except ImportError: + logger.debug("Hook failed with:", exc_info=True) + raise ImportErrorWhenRunningHook( + self.hook_module_name, self.hook_filename) + + + # Copy hook script attributes into magic attributes exposed as instance + # variables of the current "ModuleHook" instance. + for attr_name, (default_type, sanitizer_func) in ( + _MAGIC_MODULE_HOOK_ATTRS.items()): + # Unsanitized value of this attribute. + attr_value = getattr(self._hook_module, attr_name, None) + + # If this attribute is undefined, expose a sane default instead. + if attr_value is None: + attr_value = default_type() + # Else if this attribute requires sanitization, do so. + elif sanitizer_func is not None: + attr_value = sanitizer_func(attr_value) + # Else, expose the unsanitized value of this attribute. + + # Expose this attribute as an instance variable of the same name. + setattr(self, attr_name, attr_value) + + + ## Hooks + + def post_graph(self): + """ + Call the **post-graph hook** (i.e., `hook()` function) defined by this + hook script if any. + + This method is intended to be called _after_ the module graph for this + application is constructed. + """ + + # Lazily load this hook script into an in-memory module. + self._load_hook_module() + + # Call this hook script's hook() function, which modifies attributes + # accessed by subsequent methods and hence must be called first. + self._process_hook_func() + + # Order is insignificant here. + self._process_hidden_imports() + self._process_excluded_imports() + + + def _process_hook_func(self): + """ + Call this hook's `hook()` function if defined. + """ + + # If this hook script defines no hook() function, noop. + if not hasattr(self._hook_module, 'hook'): + return + + # Call this hook() function. + hook_api = PostGraphAPI( + module_name=self.module_name, module_graph=self.module_graph) + try: + self._hook_module.hook(hook_api) + except ImportError: + logger.debug("Hook failed with:", exc_info=True) + raise ImportErrorWhenRunningHook( + self.hook_module_name, self.hook_filename) + + # Update all magic attributes modified by the prior call. + self.datas.update(set(hook_api._added_datas)) + self.binaries.update(set(hook_api._added_binaries)) + self.hiddenimports.extend(hook_api._added_imports) + + #FIXME: Deleted imports should be appended to + #"self.excludedimports" rather than handled here. However, see the + #_process_excluded_imports() FIXME below for a sensible alternative. + for deleted_module_name in hook_api._deleted_imports: + # Remove the graph link between the hooked module and item. + # This removes the 'item' node from the graph if no other + # links go to it (no other modules import it) + self.module_graph.removeReference( + hook_api.node, deleted_module_name) + + + def _process_hidden_imports(self): + """ + Add all imports listed in this hook script's `hiddenimports` attribute + to the module graph as if directly imported by this hooked module. + + These imports are typically _not_ implicitly detectable by PyInstaller + and hence must be explicitly defined by hook scripts. + """ + + # For each hidden import required by the module being hooked... + for import_module_name in self.hiddenimports: + try: + # Graph node for this module. Do not implicitly create namespace + # packages for non-existent packages. + caller = self.module_graph.findNode( + self.module_name, create_nspkg=False) + + # Manually import this hidden import from this module. + self.module_graph.import_hook(import_module_name, caller) + # If this hidden import is unimportable, print a non-fatal warning. + # Hidden imports often become desynchronized from upstream packages + # and hence are only "soft" recommendations. + except ImportError: + logger.warning('Hidden import "%s" not found!', import_module_name) + + + #FIXME: This is pretty... intense. Attempting to cleanly "undo" prior module + #graph operations is a recipe for subtle edge cases and difficult-to-debug + #issues. It would be both safer and simpler to prevent these imports from + #being added to the graph in the first place. To do so: + # + #* Remove the _process_excluded_imports() method below. + #* Remove the PostGraphAPI.del_imports() method, which cannot reasonably be + # supported by the following solution, appears to be currently broken, and + # (in any case) is not called anywhere in the PyInstaller codebase. + #* Override the ModuleGraph._safe_import_hook() superclass method with a new + # PyiModuleGraph._safe_import_hook() subclass method resembling: + # + # def _safe_import_hook( + # self, target_module_name, source_module, fromlist, + # level=DEFAULT_IMPORT_LEVEL, attr=None): + # + # if source_module.identifier in self._module_hook_cache: + # for module_hook in self._module_hook_cache[ + # source_module.identifier]: + # if target_module_name in module_hook.excludedimports: + # return [] + # + # return super(PyiModuleGraph, self)._safe_import_hook( + # target_module_name, source_module, fromlist, + # level=level, attr=attr) + def _process_excluded_imports(self): + """ + 'excludedimports' is a list of Python module names that PyInstaller + should not detect as dependency of this module name. + + So remove all import-edges from the current module (and it's + submodules) to the given `excludedimports` (end their submodules). + """ + + def find_all_package_nodes(name): + mods = [name] + name += '.' + for subnode in self.module_graph.nodes(): + if subnode.identifier.startswith(name): + mods.append(subnode.identifier) + return mods + + # If this hook excludes no imports, noop. + if not self.excludedimports: + return + + # Collect all submodules of this module. + hooked_mods = find_all_package_nodes(self.module_name) + + # Collect all dependencies and their submodules + # TODO: Optimize this by using a pattern and walking the graph + # only once. + for item in set(self.excludedimports): + excluded_node = self.module_graph.findNode(item, create_nspkg=False) + if excluded_node is None: + logger.info("Import to be excluded not found: %r", item) + continue + imports_to_remove = set(find_all_package_nodes(item)) + + # Remove references between module nodes, as though they would + # not be imported from 'name'. + # Note: Doing this in a nested loop is less efficient than + # collecting all import to remove first, but log messages + # are easier to understand since related to the "Excluding ..." + # message above. + for src in hooked_mods: + # modules, this `src` does import + references = set( + node.identifier + for node in self.module_graph.getReferences(src)) + + # Remove all of these imports which are also in + # "imports_to_remove". + for dest in imports_to_remove & references: + self.module_graph.removeReference(src, dest) + logger.debug( + "Excluding import of %s from module %s", dest, src) + + +class AdditionalFilesCache(object): + """ + Cache for storing what binaries and datas were pushed by what modules + when import hooks were processed. + """ + def __init__(self): + self._binaries = {} + self._datas = {} + + def add(self, modname, binaries, datas): + + self._binaries.setdefault(modname, []) + self._binaries[modname].extend(binaries or []) + self._datas.setdefault(modname, []) + self._datas[modname].extend(datas or []) + + def __contains__(self, name): + return name in self._binaries or name in self._datas + + def binaries(self, modname): + """ + Return list of binaries for given module name. + """ + return self._binaries[modname] + + def datas(self, modname): + """ + Return list of datas for given module name. + """ + return self._datas[modname] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/depend/imphookapi.py b/3rdparty/pyinstaller-4.3/PyInstaller/depend/imphookapi.py new file mode 100644 index 0000000000000000000000000000000000000000..208ec0277e92f8ae3789c8b26586e3b137a93785 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/depend/imphookapi.py @@ -0,0 +1,503 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Classes facilitating communication between PyInstaller and import hooks. + +PyInstaller passes instances of classes defined by this module to corresponding +functions defined by external import hooks, which commonly modify the contents +of these instances before returning. PyInstaller then detects and converts these +modifications into appropriate operations on the current `PyiModuleGraph` +instance, thus modifying which modules will be frozen into the executable. +""" + +from ..lib.modulegraph.modulegraph import RuntimeModule, RuntimePackage +from ..building.datastruct import TOC +from ..building.utils import format_binaries_and_datas + + +class PreSafeImportModuleAPI(object): + """ + Metadata communicating changes made by the current **pre-safe import module + hook** (i.e., hook run immediately _before_ a call to + `ModuleGraph._safe_import_module()` recursively adding the hooked module, + package, or C extension and all transitive imports thereof to the module + graph) back to PyInstaller. + + Pre-safe import module hooks _must_ define a `pre_safe_import_module()` + function accepting an instance of this class, whose attributes describe the + subsequent `ModuleGraph._safe_import_module()` call creating the hooked + module's graph node. + + Each pre-safe import module hook is run _only_ on the first attempt to + create the hooked module's graph node and then subsequently ignored. If this + hook successfully creates that graph node, the subsequent + `ModuleGraph._safe_import_module()` call will observe this fact and silently + return without attempting to recreate that graph node. + + Pre-safe import module hooks are typically used to create graph nodes for + **runtime modules** (i.e., modules dynamically defined at runtime). Most + modules are physically defined in external `.py`-suffixed scripts. Some + modules, however, are dynamically defined at runtime (e.g., `six.moves`, + dynamically defined by the physically defined `six.py` module). However, + `ModuleGraph` only parses `import` statements residing in external scripts. + `ModuleGraph` is _not_ a full-fledged, Turing-complete Python interpreter + and hence has no means of parsing `import` statements performed by runtime + modules existing only in-memory. + + 'With great power comes great responsibility.' + + + Attributes (Immutable) + ---------------------------- + The following attributes are **immutable** (i.e., read-only). For + safety, any attempts to change these attributes _will_ result in a + raised exception: + + module_graph : PyiModuleGraph + Current module graph. + parent_package : Package + Graph node for the package providing this module _or_ `None` if this + module is a top-level module. + + Attributes (Mutable) + ----------------------------- + The following attributes are editable. + + module_basename : str + Unqualified name of the module to be imported (e.g., `text`). + module_name : str + Fully-qualified name of this module (e.g., `email.mime.text`). + """ + + def __init__(self, module_graph, module_basename, module_name, + parent_package): + self._module_graph = module_graph + self.module_basename = module_basename + self.module_name = module_name + self._parent_package = parent_package + + + # Immutable properties. No corresponding setters are defined. + @property + def module_graph(self): + """Current module graph""" + return self._module_graph + + @property + def parent_package(self): + """Parent Package of this node""" + return self._parent_package + + + def add_runtime_module(self, module_name): + """ + Add a graph node representing a non-package Python module with the + passed name dynamically defined at runtime. + + Most modules are statically defined on-disk as standard Python files. + Some modules, however, are dynamically defined in-memory at runtime + (e.g., `gi.repository.Gst`, dynamically defined by the statically + defined `gi.repository.__init__` module). + + This method adds a graph node representing such a runtime module. Since + this module is _not_ a package, all attempts to import submodules from + this module in `from`-style import statements (e.g., the `queue` + submodule in `from six.moves import queue`) will be silently ignored. To + circumvent this, simply call `add_runtime_package()` instead. + + Parameters + ---------- + module_name : str + Fully-qualified name of this module (e.g., `gi.repository.Gst`). + + Examples + ---------- + This method is typically called by `pre_safe_import_module()` hooks: + e.g., + + def pre_safe_import_module(api): + api.add_runtime_module(api.module_name) + """ + + self._module_graph.add_module(RuntimeModule(module_name)) + + + def add_runtime_package(self, package_name): + """ + Add a graph node representing a non-namespace Python package with the + passed name dynamically defined at runtime. + + Most packages are statically defined on-disk as standard subdirectories + containing `__init__.py` files. Some packages, however, are dynamically + defined in-memory at runtime (e.g., `six.moves`, dynamically defined by + the statically defined `six` module). + + This method adds a graph node representing such a runtime package. All + attributes imported from this package in `from`-style import statements + that are submodules of this package (e.g., the `queue` submodule in + `from six.moves import queue`) will be imported rather than ignored. + + Parameters + ---------- + package_name : str + Fully-qualified name of this package (e.g., `six.moves`). + + Examples + ---------- + This method is typically called by `pre_safe_import_module()` hooks: + e.g., + + def pre_safe_import_module(api): + api.add_runtime_package(api.module_name) + """ + + self._module_graph.add_module(RuntimePackage(package_name)) + + + def add_alias_module(self, real_module_name, alias_module_name): + """ + Alias the source module to the target module with the passed names. + + This method ensures that the next call to findNode() given the target + module name will resolve this alias. This includes importing and adding + a graph node for the source module if needed as well as adding a + reference from the target to the source module. + + Parameters + ---------- + real_module_name : str + Fully-qualified name of the **existing module** (i.e., the + module being aliased). + alias_module_name : str + Fully-qualified name of the **non-existent module** (i.e., + the alias to be created). + """ + + self._module_graph.alias_module(real_module_name, alias_module_name) + + + def append_package_path(self, directory): + """ + Modulegraph does a good job at simulating Python's, but it cannot + handle packagepath `__path__` modifications packages make at runtime. + + Therefore there is a mechanism whereby you can register extra paths + in this map for a package, and it will be honored. + + Parameters + ---------- + directory : str + Absolute or relative path of the directory to be appended to this + package's `__path__` attribute. + """ + + self._module_graph.append_package_path(self.module_name, directory) + + +class PreFindModulePathAPI(object): + """ + Metadata communicating changes made by the current **pre-find module + path hook** (i.e., hook run immediately _before_ a call to + `ModuleGraph._find_module_path()` finding the hooked module's absolute + path) back to PyInstaller. + + Pre-find module path hooks _must_ define a `pre_find_module_path()` + function accepting an instance of this class, whose attributes describe the + subsequent `ModuleGraph._find_module_path()` call to be performed. + + Pre-find module path hooks are typically used to change the absolute + path from which a module will be subsequently imported and thus frozen into + the executable. To do so, hooks may overwrite the default `search_dirs` list + of the absolute paths of all directories to be searched for that module: + e.g., + + def pre_find_module_path(api): + api.search_dirs = ['/the/one/true/package/providing/this/module'] + + Each pre-find module path hook is run _only_ on the first call to + `ModuleGraph._find_module_path()` for the corresponding module. + + Attributes + ---------- + The following attributes are **mutable** (i.e., modifiable). All changes to + these attributes will be immediately respected by PyInstaller: + + search_dirs : list + List of the absolute paths of all directories to be searched for this + module (in order). Searching will halt at the first directory containing + this module. + + Attributes (Immutable) + ---------- + The following attributes are **immutable** (i.e., read-only). For safety, + any attempts to change these attributes _will_ result in a raised exception: + + module_name : str + Fully-qualified name of this module. + module_graph : PyiModuleGraph + Current module graph. For efficiency, this attribute is technically + mutable. To preserve graph integrity, this attribute should nonetheless + _never_ be modified. While read-only `PyiModuleGraph` methods (e.g., + `findNode()`) are safely callable from within pre-find module path + hooks, methods modifying the graph are _not_. If graph modifications are + required, consider an alternative type of hook (e.g., pre-import module + hooks). + """ + + def __init__( + self, + module_graph, + module_name, + search_dirs, + ): + # Mutable attributes. + self.search_dirs = search_dirs + + # Immutable attributes. + self._module_graph = module_graph + self._module_name = module_name + + # Immutable properties. No corresponding setters are defined. + @property + def module_graph(self): + """ + Current module graph + """ + return self._module_graph + + @property + def module_name(self): + """ + Fully-qualified name of this module. + """ + return self._module_name + + +class PostGraphAPI(object): + """ + Metadata communicating changes made by the current **post-graph hook** + (i.e., hook run for a specific module transitively imported by the current + application _after_ the module graph of all `import` statements performed by + this application has been constructed) back to PyInstaller. + + Post-graph hooks may optionally define a `post_graph()` function accepting + an instance of this class, whose attributes describe the current state of + the module graph and the hooked module's graph node. + + Attributes (Mutable) + ---------- + The following attributes are **mutable** (i.e., modifiable). All changes to + these attributes will be immediately respected by PyInstaller: + + module_graph : PyiModuleGraph + Current module graph. + module : Node + Graph node for the currently hooked module. + + 'With great power comes great responsibility.' + + Attributes (Immutable) + ---------- + The following attributes are **immutable** (i.e., read-only). For safety, + any attempts to change these attributes _will_ result in a raised exception: + + __name__ : str + Fully-qualified name of this module (e.g., `six.moves.tkinter`). + __file__ : str + Absolute path of this module. If this module is: + * A standard (rather than namespace) package, this is the absolute path + of this package's directory. + * A namespace (rather than standard) package, this is the abstract + placeholder `-`. (Don't ask. Don't tell.) + * A non-package module or C extension, this is the absolute path of the + corresponding file. + __path__ : list + List of the absolute paths of all directories comprising this package + if this module is a package _or_ `None` otherwise. If this module is a + standard (rather than namespace) package, this list contains only the + absolute path of this package's directory. + co : code + Code object compiled from the contents of `__file__` (e.g., via the + `compile()` builtin). + + Attributes (Private) + ---------- + The following attributes are technically mutable but private, and hence + should _never_ be externally accessed or modified by hooks. Call the + corresponding public methods instead: + + _added_datas : list + List of the `(name, path)` 2-tuples or TOC objects of all + external data files required by the current hook, defaulting to the + empty list. This is equivalent to the global `datas` hook attribute. + _added_imports : list + List of the fully-qualified names of all modules imported by the current + hook, defaulting to the empty list. This is equivalent to the global + `hiddenimports` hook attribute. + _added_binaries : list + List of the `(name, path)` 2-tuples or TOC objects of all + external C extensions imported by the current hook, defaulting to the + empty list. This is equivalent to the global + `binaries` hook attribute. + """ + + def __init__(self, module_name, module_graph): + # Mutable attributes. + self.module_graph = module_graph + self.module = module_graph.findNode(module_name) + assert self.module is not None # should not occur + + # Immutable attributes. + self.___name__ = module_name + self.___file__ = self.module.filename + self._co = self.module.code + + # To enforce immutability, convert this module's package path if any + # into an immutable tuple. + self.___path__ = tuple(self.module.packagepath) \ + if self.module.packagepath is not None else None + + #FIXME: Refactor "_added_datas", "_added_binaries", and + #"_deleted_imports" into sets. Since order of importation is + #significant, "_added_imports" must remain a list. + + # Private attributes. + self._added_binaries = [] + self._added_datas = [] + self._added_imports = [] + self._deleted_imports = [] + + # Immutable properties. No corresponding setters are defined. + @property + def __file__(self): + """ + Absolute path of this module's file. + """ + return self.___file__ + + @property + def __path__(self): + """ + List of the absolute paths of all directories comprising this package + if this module is a package _or_ `None` otherwise. If this module is a + standard (rather than namespace) package, this list contains only the + absolute path of this package's directory. + """ + return self.___path__ + + @property + def __name__(self): + """ + Fully-qualified name of this module (e.g., `six.moves.tkinter`). + """ + return self.___name__ + + @property + def co(self): + """ + Code object compiled from the contents of `__file__` (e.g., via the + `compile()` builtin). + """ + return self._co + + # Obsolete immutable properties provided to preserve backward compatibility. + @property + def name(self): + """ + Fully-qualified name of this module (e.g., `six.moves.tkinter`). + + **This property has been deprecated by the `__name__` property.** + """ + return self.___name__ + + @property + def graph(self): + """ + Current module graph. + + **This property has been deprecated by the `module_graph` property.** + """ + return self.module_graph + + @property + def node(self): + """ + Graph node for the currently hooked module. + + **This property has been deprecated by the `module` property.** + """ + return self.module + + # TODO: This incorrectly returns the list of the graph nodes of all modules + # *TRANSITIVELY* (rather than directly) imported by this module. + # Unfortunately, this implies that most uses of this property are currently + # broken (e.g., "hook-PIL.SpiderImagePlugin.py"). We only require this for + # the aforementioned hook, so contemplate alternative approaches. + @property + def imports(self): + """ + List of the graph nodes of all modules directly imported by this module. + """ + return self.module_graph.flatten(start=self.module) + + def add_imports(self, *module_names): + """ + Add all Python modules whose fully-qualified names are in the passed + list as "hidden imports" upon which the current module depends. + + This is equivalent to appending such names to the hook-specific + `hiddenimports` attribute. + """ + # Append such names to the current list of all such names. + self._added_imports.extend(module_names) + + def del_imports(self, *module_names): + """ + Remove the named fully-qualified modules from the set of + imports (either hidden or visible) upon which the current + module depends. + + This is equivalent to appending such names to the hook-specific + `excludedimports` attribute. + + """ + self._deleted_imports.extend(module_names) + + def add_binaries(self, list_of_tuples): + """ + Add all external dynamic libraries in the passed list of + `(name, path)` 2-tuples as dependencies of the current module. + This is equivalent to adding to the global `binaries` hook + attribute. + + For convenience, the `list_of_tuples` may also be a single TOC + or TREE instance. + """ + if isinstance(list_of_tuples, TOC): + self._added_binaries.extend(i[:2] for i in list_of_tuples) + else: + self._added_binaries.extend(format_binaries_and_datas(list_of_tuples)) + + def add_datas(self, list_of_tuples): + """ + Add all external data files in the passed list of `(name, + path)` 2-tuples as dependencies of the current module. This is + equivalent to adding to the global `datas` hook attribute. + + For convenience, the `list_of_tuples` may also be a single TOC + or TREE instance. + """ + if isinstance(list_of_tuples, TOC): + self._added_datas.extend(i[:2] for i in list_of_tuples) + else: + self._added_datas.extend(format_binaries_and_datas(list_of_tuples)) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/depend/utils.py b/3rdparty/pyinstaller-4.3/PyInstaller/depend/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..13add7813af919194050f823022866b701165c36 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/depend/utils.py @@ -0,0 +1,469 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Utility functions related to analyzing/bundling dependencies. +""" + +import ctypes +import ctypes.util +import io +import marshal +import os +import re +import struct +import zipfile + +from ..exceptions import ExecCommandFailed +from ..lib.modulegraph import util, modulegraph + +from .. import compat +from ..compat import (is_darwin, is_unix, is_freebsd, is_openbsd, is_py37, + BYTECODE_MAGIC, PY3_BASE_MODULES) +from .dylib import include_library +from .. import log as logging + +try: + # source_hash only exists in Python 3.7 + from importlib.util import source_hash as importlib_source_hash +except ImportError: + pass + +logger = logging.getLogger(__name__) + + +# TODO find out if modules from base_library.zip could be somehow bundled into the .exe file. +def create_py3_base_library(libzip_filename, graph): + """ + Package basic Python modules into .zip file. The .zip file with basic + modules is necessary to have on PYTHONPATH for initializing libpython3 + in order to run the frozen executable with Python 3. + """ + # Import strip_paths_in_code locally to avoid cyclic import between + # building.utils and depend.utils (this module); building.utils + # imports depend.bindepend, which in turn imports depend.utils. + from ..building.utils import strip_paths_in_code + # Construct regular expression for matching modules that should be bundled + # into base_library.zip. + # Excluded are plain 'modules' or 'submodules.ANY_NAME'. + # The match has to be exact - start and end of string not substring. + regex_modules = '|'.join([r'(^%s$)' % x for x in PY3_BASE_MODULES]) + regex_submod = '|'.join([r'(^%s\..*$)' % x for x in PY3_BASE_MODULES]) + regex_str = regex_modules + '|' + regex_submod + module_filter = re.compile(regex_str) + + try: + # Remove .zip from previous run. + if os.path.exists(libzip_filename): + os.remove(libzip_filename) + logger.debug('Adding python files to base_library.zip') + # Class zipfile.PyZipFile is not suitable for PyInstaller needs. + with zipfile.ZipFile(libzip_filename, mode='w') as zf: + zf.debug = 3 + # Sort the graph nodes by identifier to ensure repeatable builds + graph_nodes = list(graph.flatten()) + graph_nodes.sort(key=lambda item: item.identifier) + for mod in graph_nodes: + if type(mod) in (modulegraph.SourceModule, + modulegraph.Package, + modulegraph.CompiledModule): + # Bundling just required modules. + if module_filter.match(mod.identifier): + st = os.stat(mod.filename) + timestamp = int(st.st_mtime) + size = st.st_size & 0xFFFFFFFF + # Name inside the archive. The ZIP format + # specification requires forward slashes as + # directory separator. + # TODO use .pyo suffix if optimize flag is enabled. + if type(mod) is modulegraph.Package: + new_name = mod.identifier.replace('.', '/') \ + + '/__init__.pyc' + else: + new_name = mod.identifier.replace('.', '/') \ + + '.pyc' + + # Write code to a file. + # This code is similar to py_compile.compile(). + with io.BytesIO() as fc: + # Prepare all data in byte stream file-like object. + fc.write(BYTECODE_MAGIC) + if is_py37: + # Additional bitfield according to PEP 552 + # 0b01 means hash based but don't check the hash + fc.write(struct.pack('>> _resolveCtypesImports(['libgs.so']) + [(libgs.so', ''/usr/lib/libgs.so', 'BINARY')] + + """ + from ctypes.util import find_library + from ..config import CONF + + if is_unix: + envvar = "LD_LIBRARY_PATH" + elif is_darwin: + envvar = "DYLD_LIBRARY_PATH" + else: + envvar = "PATH" + + def _setPaths(): + path = os.pathsep.join(CONF['pathex']) + old = compat.getenv(envvar) + if old is not None: + path = os.pathsep.join((path, old)) + compat.setenv(envvar, path) + return old + + def _restorePaths(old): + if old is None: + compat.unsetenv(envvar) + else: + compat.setenv(envvar, old) + + ret = [] + + # Try to locate the shared library on disk. This is done by + # executing ctypes.util.find_library prepending ImportTracker's + # local paths to library search paths, then replaces original values. + old = _setPaths() + for cbin in cbinaries: + try: + # There is an issue with find_library() where it can run into + # errors trying to locate the library. See #5734. + cpath = find_library(os.path.splitext(cbin)[0]) + except FileNotFoundError: + # In these cases, find_library() should return None. + cpath = None + if is_unix: + # CAVEAT: find_library() is not the correct function. Ctype's + # documentation says that it is meant to resolve only the filename + # (as a *compiler* does) not the full path. Anyway, it works well + # enough on Windows and Mac. On Linux, we need to implement + # more code to find out the full path. + if cpath is None: + cpath = cbin + # "man ld.so" says that we should first search LD_LIBRARY_PATH + # and then the ldcache + for d in compat.getenv(envvar, '').split(os.pathsep): + if os.path.isfile(os.path.join(d, cpath)): + cpath = os.path.join(d, cpath) + break + else: + if LDCONFIG_CACHE is None: + load_ldconfig_cache() + if cpath in LDCONFIG_CACHE: + cpath = LDCONFIG_CACHE[cpath] + assert os.path.isfile(cpath) + else: + cpath = None + if cpath is None: + # Skip warning message if cbin (basename of library) is ignored. + # This prevents messages like: + # 'W: library kernel32.dll required via ctypes not found' + if not include_library(cbin): + continue + logger.warning("library %s required via ctypes not found", cbin) + else: + if not include_library(cpath): + continue + ret.append((cbin, cpath, "BINARY")) + _restorePaths(old) + return ret + + +LDCONFIG_CACHE = None # cache the output of `/sbin/ldconfig -p` + +def load_ldconfig_cache(): + """ + Create a cache of the `ldconfig`-output to call it only once. + It contains thousands of libraries and running it on every dylib + is expensive. + """ + global LDCONFIG_CACHE + + if LDCONFIG_CACHE is not None: + return + + from distutils.spawn import find_executable + ldconfig = find_executable('ldconfig') + if ldconfig is None: + # If `lsconfig` is not found in $PATH, search it in some fixed + # directories. Simply use a second call instead of fiddling + # around with checks for empty env-vars and string-concat. + ldconfig = find_executable('ldconfig', + '/usr/sbin:/sbin:/usr/bin:/usr/sbin') + + # if we still couldn't find 'ldconfig' command + if ldconfig is None: + LDCONFIG_CACHE = {} + return + + if is_freebsd or is_openbsd: + # This has a quite different format than other Unixes + # [vagrant@freebsd-10 ~]$ ldconfig -r + # /var/run/ld-elf.so.hints: + # search directories: /lib:/usr/lib:/usr/lib/compat:... + # 0:-lgeom.5 => /lib/libgeom.so.5 + # 184:-lpython2.7.1 => /usr/local/lib/libpython2.7.so.1 + ldconfig_arg = '-r' + splitlines_count = 2 + pattern = re.compile(r'^\s+\d+:-l(\S+)(\s.*)? => (\S+)') + else: + # Skip first line of the library list because it is just + # an informative line and might contain localized characters. + # Example of first line with local cs_CZ.UTF-8: + #$ /sbin/ldconfig -p + #V keši „/etc/ld.so.cache“ nalezeno knihoven: 2799 + # libzvbi.so.0 (libc6,x86-64) => /lib64/libzvbi.so.0 + # libzvbi-chains.so.0 (libc6,x86-64) => /lib64/libzvbi-chains.so.0 + ldconfig_arg = '-p' + splitlines_count = 1 + pattern = re.compile(r'^\s+(\S+)(\s.*)? => (\S+)') + + try: + text = compat.exec_command(ldconfig, ldconfig_arg) + except ExecCommandFailed: + logger.warning("Failed to execute ldconfig. Disabling LD cache.") + LDCONFIG_CACHE = {} + return + + text = text.strip().splitlines()[splitlines_count:] + + LDCONFIG_CACHE = {} + for line in text: + # :fixme: this assumes libary names do not contain whitespace + m = pattern.match(line) + + # Sanitize away any abnormal lines of output. + if m is None: + # Warn about it then skip the rest of this iteration. + if re.search("Cache generated by:", line): + # See #5540. This particular line is harmless. + pass + else: + logger.warning( + "Unrecognised line of output %r from ldconfig", line) + continue + + path = m.groups()[-1] + if is_freebsd or is_openbsd: + # Insert `.so` at the end of the lib's basename. soname + # and filename may have (different) trailing versions. We + # assume the `.so` in the filename to mark the end of the + # lib's basename. + bname = os.path.basename(path).split('.so', 1)[0] + name = 'lib' + m.group(1) + assert name.startswith(bname) + name = bname + '.so' + name[len(bname):] + else: + name = m.group(1) + # ldconfig may know about several versions of the same lib, + # e.g. differents arch, different libc, etc. Use the first + # entry. + if not name in LDCONFIG_CACHE: + LDCONFIG_CACHE[name] = path + + +def get_path_to_egg(path): + """ + Return the path to the python egg file, if the path points to a + file inside a (or to an egg directly). + Return `None` otherwise. + """ + # This assumes, eggs are not nested. + # TODO add support for unpacked eggs and for new .whl packages. + lastpath = None # marker to stop recursion + while path and path != lastpath: + if os.path.splitext(path)[1].lower() == (".egg"): + if os.path.isfile(path) or os.path.isdir(path): + return path + lastpath = path + path = os.path.dirname(path) + return None + + +def is_path_to_egg(path): + """ + Check if path points to a file inside a python egg file (or to an egg + directly). + """ + return get_path_to_egg(path) is not None diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/exceptions.py b/3rdparty/pyinstaller-4.3/PyInstaller/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..1b969a738dea3777359b99c492362fb23f4827d3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/exceptions.py @@ -0,0 +1,29 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 ExecCommandFailed(SystemExit): + pass + + +class HookError(Exception): + """Base class for hook related errors.""" + pass + + +class ImportErrorWhenRunningHook(HookError): + def __str__(self): + return ("Failed to import module {0} required by hook for module {1}. " + "Please check whether module {0} actually exists and whether " + "the hook is compatible with your version of {1}: You might " + "want to read more about hooks in the manual and provide " + "a pull-request to improve PyInstaller.".format( + self.args[0], self.args[1])) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/fake-modules/site.py b/3rdparty/pyinstaller-4.3/PyInstaller/fake-modules/site.py new file mode 100644 index 0000000000000000000000000000000000000000..781af38374e277b4327209ba643c8abec8270622 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/fake-modules/site.py @@ -0,0 +1,61 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 is a fake 'site' module available in default Python Library. + +The real 'site' does some magic to find paths to other possible +Python modules. We do not want this behaviour for frozen applications. + +Fake 'site' makes PyInstaller to work with distutils and to work inside +virtualenv environment. +""" + +# Marker to be used in our test-suite. +__pyinstaller__faked__site__module__ = True + +# TODO test the following code stub from real 'site' module. + + +# Prefixes for site-packages; add additional prefixes like /usr/local here +PREFIXES = [] + +# Enable per user site-packages directory +# set it to False to disable the feature or True to force the feature +ENABLE_USER_SITE = False + + +# For distutils.commands.install +# These values are initialized by the getuserbase() and getusersitepackages() +# functions, through the main() function when Python starts. +# Issue #1699: Freezing pip requires 'site.USER_SITE' to be a 'str' not None. +USER_SITE = '' +# Freezing Jupyter Notebook requires 'site.USER_BASE' to be a 'str' not None. +USER_BASE = '' + + +# Package IPython depends on the following functionality from real site.py. +# This code could be probably removed when the following bug is fixed: +# https://github.com/ipython/ipython/issues/2606 +class _Helper(object): + """ + Define the builtin 'help'. + This is a wrapper around pydoc.help (with a twist). + """ + def __repr__(self): + return "Type help() for interactive help, " \ + "or help(object) for help about object." + def __call__(self, *args, **kwds): + # Do *not* use `import` here, otherwise pydoc will be included in + # *every* frozen app + pydoc = __import__(''.join('pydoc')) + return pydoc.help(*args, **kwds) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.Image.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.Image.py new file mode 100644 index 0000000000000000000000000000000000000000..0ad387b3a36f8513f571ef79cebd0fb852fbd6f5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.Image.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook was tested with Pillow 2.9.0 (Maintained fork of PIL): +# https://pypi.python.org/pypi/Pillow + +from PyInstaller.utils.hooks import collect_submodules + +# Include all PIL image plugins - module names containing 'ImagePlugin'. +# e.g. PIL.JpegImagePlugin +hiddenimports = collect_submodules('PIL', lambda name: 'ImagePlugin' in name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.ImageFilter.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.ImageFilter.py new file mode 100644 index 0000000000000000000000000000000000000000..7033ff42ca2100cb8113a5bb59515d2876762290 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.ImageFilter.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Only used if installed, not mean to pull in numpy. +excludedimports = ["numpy"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.SpiderImagePlugin.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.SpiderImagePlugin.py new file mode 100644 index 0000000000000000000000000000000000000000..b945937efefa68c9c3222f46e987eee90c56d6ce --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.SpiderImagePlugin.py @@ -0,0 +1,19 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.compat import modname_tkinter + + +# PIL's SpiderImagePlugin features a tkPhotoImage() method which imports +# ImageTk (and thus brings the whole Tcl/Tk library in). +# Assume that if people are really using tkinter in their application, they +# will also import it directly. +excludedimports = [modname_tkinter, 'FixTk'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.py new file mode 100644 index 0000000000000000000000000000000000000000..56bab92dcbdef97ab692ef3242ed6a640e21526d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PIL.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook was tested with Pillow 2.9.0 (Maintained fork of PIL): +# https://pypi.python.org/pypi/Pillow + +from PyInstaller.compat import modname_tkinter + +# Ignore 'FixTk' (Python 2) or tkinter to prevent inclusion of Tcl/Tk library +# and other GUI libraries. +# Assume that if people are really using tkinter in their application, they +# will also import it directly and thus PyInstaller bundles the right GUI +# library. +excludedimports = [modname_tkinter, 'FixTk', 'PyQt5'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.Qt.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.Qt.py new file mode 100644 index 0000000000000000000000000000000000000000..3da0c909a7996cc473603b1de8871caf1c3d8488 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.Qt.py @@ -0,0 +1,29 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +# When PyQt5.Qt is imported it implies the import of all PyQt5 modules. See +# http://pyqt.sourceforge.net/Docs/PyQt5/Qt.html. +import os + +from PyInstaller.utils.hooks import get_module_file_attribute + +# Only do this if PyQt5 is found. +mfi = get_module_file_attribute('PyQt5') +if mfi: + # Determine the name of all these modules by looking in the PyQt5 directory. + hiddenimports = [] + for f in os.listdir(os.path.dirname(mfi)): + root, ext = os.path.splitext(os.path.basename(f)) + if root.startswith('Qt') and root != 'Qt': + # On Linux and OS X, PyQt 5.14.1 has a ``.abi3`` suffix on all library names. Remove it. + if root.endswith('.abi3'): + root = root[:-5] + hiddenimports.append('PyQt5.' + root) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtCore.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtCore.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtCore.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtGui.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtGui.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtGui.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtHelp.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtHelp.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtHelp.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtMultimedia.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtMultimedia.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtMultimedia.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtNetwork.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtNetwork.py new file mode 100644 index 0000000000000000000000000000000000000000..1cfcdaa5cc1e0b07cf6990554376640235a25057 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtNetwork.py @@ -0,0 +1,32 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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.path + +from PyInstaller.utils.hooks import eval_statement +from PyInstaller.utils.hooks.qt import add_qt5_dependencies, pyqt5_library_info +from PyInstaller.compat import is_win + +# Ensure PyQt5 is importable before adding info depending on it. +if pyqt5_library_info.version is not None: + hiddenimports, binaries, datas = add_qt5_dependencies(__file__) + + # Add libraries needed for SSL if these are available. See issue #3520, #4048. + if (is_win and eval_statement(""" + from PyQt5.QtNetwork import QSslSocket + print(QSslSocket.supportsSsl())""")): + + binaries = [] + for dll in ('libeay32.dll', 'ssleay32.dll', 'libssl-1_1-x64.dll', + 'libcrypto-1_1-x64.dll'): + dll_path = os.path.join(pyqt5_library_info.location['BinariesPath'], + dll) + if os.path.exists(dll_path): + binaries.append((dll_path, '.')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtOpenGL.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtOpenGL.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtOpenGL.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtPrintSupport.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtPrintSupport.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtPrintSupport.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQml.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQml.py new file mode 100644 index 0000000000000000000000000000000000000000..b21d281f32a0bf432bd1cc1b380a0b80553595d5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQml.py @@ -0,0 +1,42 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 + +from PyInstaller.utils import misc +from PyInstaller.utils.hooks.qt import pyqt5_library_info, add_qt5_dependencies +from PyInstaller import log as logging + +# Ensure PyQt5 is importable before adding info depending on it. +if pyqt5_library_info.version is not None: + logger = logging.getLogger(__name__) + + hiddenimports, binaries, datas = add_qt5_dependencies(__file__) + + qmldir = pyqt5_library_info.location['Qml2ImportsPath'] + # Per https://github.com/pyinstaller/pyinstaller/pull/3229#issuecomment-359735031, + # not all PyQt5 installs have QML files. In this case, ``qmldir`` is empty. In + # addition, the directory may not exist even if ``qmldir`` is not empty, per + # https://github.com/pyinstaller/pyinstaller/issues/3864. + if not os.path.exists(qmldir): + logger.warning('Unable to find Qt5 QML files. QML files not packaged.') + else: + qml_rel_dir = ['PyQt5', 'Qt', 'qml'] + datas += [(qmldir, os.path.join(*qml_rel_dir))] + binaries += [ + # Produce ``/path/to/Qt/Qml/path_to_qml_binary/qml_binary, + # PyQt5/Qt/Qml/path_to_qml_binary``. When Python 3.4 goes EOL (see + # PEP 448), this is better written as + # ``os.path.join(*qml_rel_dir, + # os.path.dirname(os.path.relpath(f, qmldir))))``. + (f, os.path.join(*(qml_rel_dir + + [os.path.dirname(os.path.relpath(f, qmldir))]))) + for f in misc.dlls_in_subdirs(qmldir) + ] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQuick.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQuick.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQuick.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQuickWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQuickWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtQuickWidgets.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtScript.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtScript.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtScript.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSensors.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSensors.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSensors.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSerialPort.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSerialPort.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSerialPort.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSql.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSql.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSql.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSvg.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSvg.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtSvg.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtTest.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtTest.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtTest.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..576102c1a38c5620044f1f34c6c4c5bc9e61cde9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py @@ -0,0 +1,83 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 glob +from PyInstaller.utils.hooks.qt import add_qt5_dependencies, pyqt5_library_info +from PyInstaller.utils.hooks import remove_prefix, get_module_file_attribute, \ + collect_system_data_files +from PyInstaller.depend.bindepend import getImports +import PyInstaller.compat as compat + +# Ensure PyQt5 is importable before adding info depending on it. +if pyqt5_library_info.version is not None: + hiddenimports, binaries, datas = add_qt5_dependencies(__file__) + + # Include the web engine process, translations, and resources. + rel_data_path = ['PyQt5', 'Qt'] + if compat.is_darwin: + # This is based on the layout of the Mac wheel from PyPi. + data_path = pyqt5_library_info.location['DataPath'] + libraries = ['QtCore', 'QtWebEngineCore', 'QtQuick', 'QtQml', + 'QtQmlModels', 'QtNetwork', 'QtGui', 'QtWebChannel', + 'QtPositioning'] + for i in libraries: + framework_dir = i + '.framework' + datas += collect_system_data_files( + os.path.join(data_path, 'lib', framework_dir), + os.path.join(*rel_data_path, 'lib', framework_dir), True) + datas += [(os.path.join(data_path, 'lib', 'QtWebEngineCore.framework', + 'Resources'), os.curdir)] + else: + locales = 'qtwebengine_locales' + resources = 'resources' + datas += [ + # Gather translations needed by Chromium. + (os.path.join(pyqt5_library_info.location['TranslationsPath'], + locales), + os.path.join('PyQt5', 'Qt', 'translations', locales)), + # Per the `docs `_, + # ``DataPath`` is the base directory for ``resources``. + # + # When Python 3.4 goes EOL (see `PEP 448`_, this is better written as + # ``os.path.join(*rel_data_path, resources)``. + (os.path.join(pyqt5_library_info.location['DataPath'], resources), + os.path.join(*(rel_data_path + [resources]))), + # Include the webengine process. The ``LibraryExecutablesPath`` is only + # valid on Windows and Linux. + # + # Again, rewrite when Python 3.4 is EOL to + # ``os.path.join(*rel_data_path, remove_prefix(...``. + (os.path.join(pyqt5_library_info.location['LibraryExecutablesPath'], + 'QtWebEngineProcess*'), + os.path.join(*(rel_data_path + + [remove_prefix(pyqt5_library_info.location['LibraryExecutablesPath'], + pyqt5_library_info.location['PrefixPath'] + '/')]))) + ] + + # Add Linux-specific libraries. + if compat.is_linux: + # The automatic library detection fails for `NSS + # `_, which is used by + # QtWebEngine. In some distributions, the ``libnss`` supporting libraries + # are stored in a subdirectory ``nss``. Since ``libnss`` is not statically + # linked to these, but dynamically loads them, we need to search for and add + # them. + # + # First, get all libraries linked to ``PyQt5.QtWebEngineWidgets``. + for imp in getImports(get_module_file_attribute('PyQt5.QtWebEngineWidgets')): + # Look for ``libnss3.so``. + if os.path.basename(imp).startswith('libnss3.so'): + # Find the location of NSS: given a ``/path/to/libnss.so``, + # add ``/path/to/nss/*.so`` to get the missing NSS libraries. + nss_glob = os.path.join(os.path.dirname(imp), 'nss', '*.so') + if glob.glob(nss_glob): + binaries.append((nss_glob, 'nss')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebKit.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebKit.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebKit.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebKitWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebKitWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..fdd00ba61693f023399c86761c41380a25e2b7bf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWebKitWidgets.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtWidgets.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtXml.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtXml.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.QtXml.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.py new file mode 100644 index 0000000000000000000000000000000000000000..f5cd841e9716cc94946a22d6359cd75a94293fe3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.py @@ -0,0 +1,32 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +from PyInstaller.utils.hooks import collect_system_data_files +from PyInstaller.utils.hooks.qt import pyqt5_library_info, get_qt_binaries + +# Ensure PyQt5 is importable before adding info depending on it. +if pyqt5_library_info.version is not None: + hiddenimports = [ + # PyQt5.10 and earlier uses sip in an separate package; + 'sip', + # PyQt5.11 and later provides SIP in a private package. Support both. + 'PyQt5.sip' + ] + + # Collect the ``qt.conf`` file. + datas = [x for x in + collect_system_data_files(pyqt5_library_info.location['PrefixPath'], + os.path.join('PyQt5', 'Qt')) + if os.path.basename(x[0]) == 'qt.conf'] + + # Collect required Qt binaries. + binaries = get_qt_binaries(pyqt5_library_info) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.uic.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.uic.py new file mode 100644 index 0000000000000000000000000000000000000000..6e2dc27e3d7140b0c8198a58ab8f0be45ef24b6f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PyQt5.uic.py @@ -0,0 +1,19 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_data_files + +# Need to include modules in PyQt5.uic.widget-plugins, so they can be +# dynamically loaded by uic. They should both be included as separate +# (data-like) files, so they can be found by os.listdir and friends. However, +# this directory isn't a package, refer to it using the package (PyQt5.uic) +# followed by the subdirectory name (``widget-plugins/``). +datas = collect_data_files('PyQt5.uic', True, 'widget-plugins') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtCore.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtCore.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtCore.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtGui.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtGui.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtGui.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtHelp.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtHelp.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtHelp.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtMultimedia.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtMultimedia.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtMultimedia.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtNetwork.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtNetwork.py new file mode 100644 index 0000000000000000000000000000000000000000..bf25f1f371f584abb8612f1ca379d3740aa6fffa --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtNetwork.py @@ -0,0 +1,42 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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.path + +from PyInstaller.utils.hooks import eval_statement +from PyInstaller.utils.hooks.qt import add_qt5_dependencies, \ + pyside2_library_info +from PyInstaller.compat import is_win + +# Only proceed if PySide2 can be imported. +if pyside2_library_info.version is not None: + hiddenimports, binaries, datas = add_qt5_dependencies(__file__) + + # Add libraries needed for SSL if these are available. See issue #3520, #4048. + if (is_win and eval_statement(""" + from PySide2.QtNetwork import QSslSocket + print(QSslSocket.supportsSsl())""")): + + # PyPI version of PySide2 requires user to manually install SSL + # libraries into the PrefixPath. Other versions (e.g., the one + # provided by Conda) put the libraries into the BinariesPath. + # Accommodate both options by searching both locations... + locations = ( + pyside2_library_info.location['BinariesPath'], + pyside2_library_info.location['PrefixPath'] + ) + + binaries = [] + for location in locations: + for dll in ('libeay32.dll', 'ssleay32.dll', 'libssl-1_1-x64.dll', + 'libcrypto-1_1-x64.dll'): + dll_path = os.path.join(location, dll) + if os.path.exists(dll_path): + binaries.append((dll_path, '.')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtOpenGL.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtOpenGL.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtOpenGL.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtPrintSupport.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtPrintSupport.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtPrintSupport.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQml.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQml.py new file mode 100644 index 0000000000000000000000000000000000000000..6e9791ea2e52fd056bd625f7ae8eff8cf2a97c0f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQml.py @@ -0,0 +1,40 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 + +from PyInstaller.utils import misc +from PyInstaller.utils.hooks.qt import pyside2_library_info, add_qt5_dependencies +from PyInstaller import log as logging + +# Only proceed if PySide2 can be imported. +if pyside2_library_info.version is not None: + logger = logging.getLogger(__name__) + + hiddenimports, binaries, datas = add_qt5_dependencies(__file__) + + qmldir = pyside2_library_info.location['Qml2ImportsPath'] + # Not all PySide2 installs have QML files. In this case, ``qmldir`` is empty. In + # addition, the directory may not exist even if ``qmldir`` is not empty. + if not os.path.exists(qmldir): + logger.warning('Unable to find Qt5 QML files. QML files not packaged.') + else: + qml_rel_dir = ['PySide2', 'qml'] + datas += [(qmldir, os.path.join(*qml_rel_dir))] + binaries += [ + # Produce ``/path/to/Qt/Qml/path_to_qml_binary/qml_binary, + # PyQt5/Qt/Qml/path_to_qml_binary``. When Python 3.4 goes EOL (see + # PEP 448), this is better written as + # ``os.path.join(*qml_rel_dir, + # os.path.dirname(os.path.relpath(f, qmldir))))``. + (f, os.path.join(*(qml_rel_dir + + [os.path.dirname(os.path.relpath(f, qmldir))]))) + for f in misc.dlls_in_subdirs(qmldir) + ] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQuick.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQuick.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQuick.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQuickWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQuickWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtQuickWidgets.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtScript.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtScript.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtScript.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSensors.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSensors.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSensors.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSerialPort.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSerialPort.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSerialPort.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSql.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSql.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSql.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSvg.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSvg.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtSvg.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtTest.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtTest.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtTest.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebEngineWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebEngineWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..1529f60081400b6987e122f009f4c31cf7192b98 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebEngineWidgets.py @@ -0,0 +1,101 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 glob +from PyInstaller.utils.hooks.qt import add_qt5_dependencies, \ + pyside2_library_info +from PyInstaller.utils.hooks import get_module_file_attribute, \ + collect_system_data_files +from PyInstaller.depend.bindepend import getImports +import PyInstaller.compat as compat + + +def get_relative_path_if_possible(actual, possible_prefix): + possible_relative_path = os.path.relpath(actual, possible_prefix) + if possible_relative_path.startswith(os.pardir): + return actual + else: + return possible_relative_path + + +def prefix_with_path(prefix_path, *paths): + return os.path.join(*prefix_path, *paths) # noqa: E999 + + +# Ensure PySide2 is importable before adding info depending on it. +if pyside2_library_info.version is not None: + hiddenimports, binaries, datas = add_qt5_dependencies(__file__) + + # Include the web engine process, translations, and resources. + # According to https://bugreports.qt.io/browse/PYSIDE-642 + # there's no subdir for windows + if compat.is_win: + rel_data_path = ['PySide2'] + else: + rel_data_path = ['PySide2', 'Qt'] + + pyside2_locations = pyside2_library_info.location + if compat.is_darwin: + # This is based on the layout of the Mac wheel from PyPi. + data_path = pyside2_locations['DataPath'] + libraries = ['QtCore', 'QtWebEngineCore', 'QtQuick', 'QtQml', + 'QtQmlModels', 'QtNetwork', 'QtGui', 'QtWebChannel', + 'QtPositioning'] + for i in libraries: + framework_dir = i + '.framework' + datas += collect_system_data_files( + os.path.join(data_path, 'lib', framework_dir), + prefix_with_path(rel_data_path, 'lib', framework_dir), True) + datas += [(os.path.join(data_path, 'lib', 'QtWebEngineCore.framework', + 'Resources'), os.curdir)] + else: + locales = 'qtwebengine_locales' + resources = 'resources' + datas += [ + # Gather translations needed by Chromium. + (os.path.join(pyside2_locations['TranslationsPath'], locales), + prefix_with_path(rel_data_path, 'translations', locales)), + # Per the `docs + # `_, + # ``DataPath`` is the base directory for ``resources``. + # + (os.path.join(pyside2_locations['DataPath'], resources), + prefix_with_path(rel_data_path, resources)), + # Include the webengine process. The ``LibraryExecutablesPath`` + # is only valid on Windows and Linux. + # + (os.path.join(pyside2_locations['LibraryExecutablesPath'], + 'QtWebEngineProcess*'), + prefix_with_path(rel_data_path, get_relative_path_if_possible( + pyside2_locations['LibraryExecutablesPath'], + pyside2_locations['PrefixPath'] + '/'))) + ] + + # Add Linux-specific libraries. + if compat.is_linux: + # The automatic library detection fails for `NSS + # `_, which is + # used by QtWebEngine. In some distributions, the ``libnss`` + # supporting libraries are stored in a subdirectory ``nss``. Since + # ``libnss`` is not statically linked to these, but dynamically loads + # them, we need to search for and add them. + # + # First, get all libraries linked to ``PyQt5.QtWebEngineWidgets``. + for imp in getImports( + get_module_file_attribute('PySide2.QtWebEngineWidgets')): + # Look for ``libnss3.so``. + if os.path.basename(imp).startswith('libnss3.so'): + # Find the location of NSS: given a ``/path/to/libnss.so``, + # add ``/path/to/nss/*.so`` to get the missing NSS libraries. + nss_glob = os.path.join(os.path.dirname(imp), 'nss', '*.so') + if glob.glob(nss_glob): + binaries.append((nss_glob, 'nss')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebKit.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebKit.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebKit.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebKitWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebKitWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..fdd00ba61693f023399c86761c41380a25e2b7bf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWebKitWidgets.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWidgets.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWidgets.py new file mode 100644 index 0000000000000000000000000000000000000000..4c220f4df4d349027cd91d01d1a1730bb38858b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtWidgets.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtXml.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtXml.py new file mode 100644 index 0000000000000000000000000000000000000000..0a10991c5b551ecc6a95186bcb8b659cdb52d537 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.QtXml.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks.qt import add_qt5_dependencies + +hiddenimports, binaries, datas = add_qt5_dependencies(__file__) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.Qwt5.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.Qwt5.py new file mode 100644 index 0000000000000000000000000000000000000000..78642501dfad638608b19b927a3af51109219b0c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.Qwt5.py @@ -0,0 +1,24 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import eval_statement + +hiddenimports = ['PySide2.QtCore', + 'PySide2.QtWidgets', + 'PySide2.QtGui', + 'PySide2.QtSvg'] + +if eval_statement("from PySide2 import Qwt5; print(hasattr(Qwt5, 'toNumpy'))"): + hiddenimports.append("numpy") +if eval_statement("from PySide2 import Qwt5; print(hasattr(Qwt5, 'toNumeric'))"): + hiddenimports.append("Numeric") +if eval_statement("from PySide2 import Qwt5; print(hasattr(Qwt5, 'toNumarray'))"): + hiddenimports.append("numarray") diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.py new file mode 100644 index 0000000000000000000000000000000000000000..ff5c742a48bf5223c3d399a60e313c4c34f5d3a0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.py @@ -0,0 +1,34 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 +from PyInstaller.utils.hooks import collect_system_data_files +from PyInstaller.utils.hooks.qt import pyside2_library_info, get_qt_binaries +from PyInstaller.compat import is_win + +# Only proceed if PySide2 can be imported. +if pyside2_library_info.version is not None: + + hiddenimports = ['shiboken2'] + + # Collect the ``qt.conf`` file. + if is_win: + target_qt_conf_dir = ['PySide2'] + else: + target_qt_conf_dir = ['PySide2', 'Qt'] + + datas = [x for x in + collect_system_data_files(pyside2_library_info.location['PrefixPath'], + os.path.join(*target_qt_conf_dir)) + if os.path.basename(x[0]) == 'qt.conf'] + + # Collect required Qt binaries. + binaries = get_qt_binaries(pyside2_library_info) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.uic.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.uic.py new file mode 100644 index 0000000000000000000000000000000000000000..3e2240c68bcac5913a499b05cc08642f0188ab3f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-PySide2.uic.py @@ -0,0 +1,19 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_data_files + +# Need to include modules in PySide2.uic.widget-plugins, so they can be +# dynamically loaded by uic. They should both be included as separate +# (data-like) files, so they can be found by os.listdir and friends. However, +# this directory isn't a package, refer to it using the package (PySide2.uic) +# followed by the subdirectory name (``widget-plugins/``). +datas = collect_data_files('PySide2.uic', True, 'widget-plugins') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-_tkinter.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-_tkinter.py new file mode 100644 index 0000000000000000000000000000000000000000..d7a9ebeffd253d6debcb6e273d37f43d50af2605 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-_tkinter.py @@ -0,0 +1,278 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys +import locale + +from PyInstaller.compat import is_win, is_darwin, is_unix, is_venv, \ + base_prefix, open_file, text_read_mode +from PyInstaller.depend.bindepend import selectImports, getImports +from PyInstaller.building.datastruct import Tree +from PyInstaller.utils.hooks import exec_statement, logger + + +def _warn_if_activetcl_or_teapot_installed(tcl_root, tcltree): + """ + If the current Tcl installation is a Teapot-distributed version of ActiveTcl + *and* the current platform is OS X, log a non-fatal warning that the + resulting executable will (probably) fail to run on non-host systems. + + PyInstaller does *not* freeze all ActiveTcl dependencies -- including + Teapot, which is typically ignorable. Since Teapot is *not* ignorable in + this case, this function warns of impending failure. + + See Also + ------- + https://github.com/pyinstaller/pyinstaller/issues/621 + """ + from macholib import util + + # System libraries do not experience this problem. + if util.in_system_path(tcl_root): + return + + # Absolute path of the "init.tcl" script. + try: + init_resource = [r[1] for r in tcltree if r[1].endswith('init.tcl')][0] + # If such script could not be found, silently return. + except IndexError: + return + + mentions_activetcl = False + mentions_teapot = False + # TCL/TK reads files using the `system encoding `_. + with open_file(init_resource, text_read_mode, + encoding=locale.getpreferredencoding()) as init_file: + for line in init_file.readlines(): + line = line.strip().lower() + if line.startswith('#'): + continue + if 'activetcl' in line: + mentions_activetcl = True + if 'teapot' in line: + mentions_teapot = True + if mentions_activetcl and mentions_teapot: + break + + if mentions_activetcl and mentions_teapot: + logger.warning( + """ +You appear to be using an ActiveTcl build of Tcl/Tk, which PyInstaller has +difficulty freezing. To fix this, comment out all references to "teapot" in: + + %s + +See https://github.com/pyinstaller/pyinstaller/issues/621 for more information. + """ % init_resource) + + +def _find_tcl_tk_darwin_system_frameworks(binaries): + """ + Get an OS X-specific 2-tuple of the absolute paths of the top-level + external data directories for both Tcl and Tk, respectively. + + This function finds the OS X system installation of Tcl and Tk. + System OS X Tcl and Tk are installed as Frameworks requiring special care. + + Returns + ------- + list + 2-tuple whose first element is the value of `${TCL_LIBRARY}` and whose + second element is the value of `${TK_LIBRARY}`. + """ + tcl_root = tk_root = None + for nm, fnm in binaries: + if nm == 'Tcl': + tcl_root = os.path.join(os.path.dirname(fnm), 'Resources/Scripts') + elif nm == 'Tk': + tk_root = os.path.join(os.path.dirname(fnm), 'Resources/Scripts') + return tcl_root, tk_root + + +def _find_tcl_tk_dir(): + """ + Get a platform-agnostic 2-tuple of the absolute paths of the top-level + external data directories for both Tcl and Tk, respectively. + + Returns + ------- + list + 2-tuple whose first element is the value of `${TCL_LIBRARY}` and whose + second element is the value of `${TK_LIBRARY}`. + """ + # Python code to get path to TCL_LIBRARY. + tcl_root = exec_statement( + 'from tkinter import Tcl; print(Tcl().eval("info library"))') + tk_version = exec_statement( + 'from _tkinter import TK_VERSION; print(TK_VERSION)') + + # TK_LIBRARY is in the same prefix as Tcl. + tk_root = os.path.join(os.path.dirname(tcl_root), 'tk%s' % tk_version) + return tcl_root, tk_root + + +def _find_tcl_tk(hook_api): + """ + Get a platform-specific 2-tuple of the absolute paths of the top-level + external data directories for both Tcl and Tk, respectively. + + Returns + ------- + list + 2-tuple whose first element is the value of `${TCL_LIBRARY}` and whose + second element is the value of `${TK_LIBRARY}`. + """ + bins = selectImports(hook_api.__file__) + + if is_darwin: + # _tkinter depends on system Tcl/Tk frameworks. + # For example this is the case of Python from homebrew. + if not bins: + # 'hook_api.binaries' can't be used because on Mac OS X _tkinter.so + # might depend on system Tcl/Tk frameworks and these are not + # included in 'hook_api.binaries'. + bins = getImports(hook_api.__file__) + + if bins: + # Reformat data structure from + # set(['lib1', 'lib2', 'lib3']) + # to + # [('Tcl', '/path/to/Tcl'), ('Tk', '/path/to/Tk')] + mapping = {} + for lib in bins: + mapping[os.path.basename(lib)] = lib + bins = [ + ('Tcl', mapping['Tcl']), + ('Tk', mapping['Tk']), + ] + else: + # Starting with macOS 11, system libraries are hidden. + # Until we adjust library discovery accordingly, bins + # will end up empty. But this implicitly indicates that + # the system framework is used, so return None, None + # to inform the caller. + return None, None + + # _tkinter depends on Tcl/Tk compiled as frameworks. + path_to_tcl = bins[0][1] + # OS X system installation of Tcl/Tk. + # [/System]/Library/Frameworks/Tcl.framework/Resources/Scripts/Tcl + if 'Library/Frameworks/Tcl.framework' in path_to_tcl: + #tcl_tk = _find_tcl_tk_darwin_system_frameworks(bins) + tcl_tk = None, None # Do not gather system framework's data + + # Tcl/Tk compiled as on Linux other Unixes. + # This is the case of Tcl/Tk from macports and Tck/Tk built into + # python.org OS X python distributions. + # python.org built-in tcl/tk is located at + # /Library/Frameworks/Python.framework/Versions/3.x/lib/libtcl8.6.dylib + else: + tcl_tk = _find_tcl_tk_dir() + + else: + tcl_tk = _find_tcl_tk_dir() + + return tcl_tk + + +def _collect_tcl_modules(tcl_root): + """ + Get a list of TOC-style 3-tuples describing Tcl modules. The modules + directory is separate from the library/data one, and is located + at $tcl_root/../tclX, where X is the major Tcl version. + + Returns + ------- + Tree + Such list, if the modules directory exists. + """ + + # Obtain Tcl major version. + tcl_version = exec_statement( + 'from tkinter import Tcl; print(Tcl().eval("info tclversion"))') + tcl_version = tcl_version.split('.')[0] + + modules_dirname = 'tcl' + str(tcl_version) + modules_path = os.path.join(tcl_root, '..', modules_dirname) + + if not os.path.isdir(modules_path): + logger.warn('Tcl modules directory %s does not exist.', modules_path) + return [] + + return Tree(modules_path, prefix=modules_dirname) + + +def _collect_tcl_tk_files(hook_api): + """ + Get a list of TOC-style 3-tuples describing all external Tcl/Tk data files. + + Returns + ------- + Tree + Such list. + """ + tcl_root, tk_root = _find_tcl_tk(hook_api) + + # On macOS, we do not collect system libraries. Therefore, if system + # Tcl/Tk framework is used, it makes no sense to collect its data, + # either. In this case, _find_tcl_tk() will return None, None - either + # deliberately (we found the data paths, but ignore them) or not + # (starting with macOS 11, the data path cannot be found until shared + # library discovery is fixed). + if is_darwin and not tcl_root and not tk_root: + logger.info('Not collecting Tcl/Tk data - either python is using ' + 'macOS\' system Tcl/Tk framework, or Tcl/Tk data ' + 'directories could not be found.') + return [] + + # TODO Shouldn't these be fatal exceptions? + if not tcl_root: + logger.error('Tcl/Tk improperly installed on this system.') + return [] + if not os.path.isdir(tcl_root): + logger.error('Tcl data directory "%s" not found.', tcl_root) + return [] + if not os.path.isdir(tk_root): + logger.error('Tk data directory "%s" not found.', tk_root) + return [] + + tcltree = Tree( + tcl_root, prefix='tcl', excludes=['demos', '*.lib', 'tclConfig.sh']) + tktree = Tree( + tk_root, prefix='tk', excludes=['demos', '*.lib', 'tkConfig.sh']) + + # If the current Tcl installation is a Teapot-distributed version of + # ActiveTcl and the current platform is OS X, warn that this is bad. + if is_darwin: + _warn_if_activetcl_or_teapot_installed(tcl_root, tcltree) + + # Collect Tcl modules + tclmodulestree = _collect_tcl_modules(tcl_root) + + return (tcltree + tktree + tclmodulestree) + + +def hook(hook_api): + # Use a hook-function to get the module's attr:`__file__` easily. + """ + Freeze all external Tcl/Tk data files if this is a supported platform *or* + log a non-fatal error otherwise. + """ + if is_win or is_darwin or is_unix: + # _collect_tcl_tk_files(hook_api) returns a Tree (which is okay), + # so we need to store it into `hook_api.datas` to prevent + # `building.imphook.format_binaries_and_datas` from crashing + # with "too many values to unpack". + hook_api.add_datas(_collect_tcl_tk_files(hook_api)) + else: + logger.error("... skipping Tcl/Tk handling on unsupported platform %s", sys.platform) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-babel.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-babel.py new file mode 100644 index 0000000000000000000000000000000000000000..c3a9843fb05d513a68ffcae1f2d3836d6dcc1243 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-babel.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_data_files + +hiddenimports = ["babel.dates"] + +datas = collect_data_files('babel') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-difflib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-difflib.py new file mode 100644 index 0000000000000000000000000000000000000000..7d7659bbd42a4824faf99998a1567c1bec869eb4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-difflib.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +# only required when run as `__main__` +excludedimports = ["doctest"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-distutils.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-distutils.py new file mode 100644 index 0000000000000000000000000000000000000000..b804d0dfb331e5a90c1b63d3b9fe0fc963d19d4e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-distutils.py @@ -0,0 +1,30 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +`distutils`-specific post-import hook. + +This hook freezes the external `Makefile` and `pyconfig.h` files bundled with +the active Python interpreter, which the `distutils.sysconfig` module parses at +runtime for platform-specific metadata. +""" + +from PyInstaller import compat + +# From Python 3.6 and later ``distutils.sysconfig`` takes on the same +# behaviour as regular ``sysconfig`` of moving the config vars to a +# module (see hook-sysconfig.py). It doesn't use a nice +# `get module name` function like ``sysconfig`` does to help us +# locate it but the module is the same file that ``sysconfig`` uses so +# we can use the ``_get_sysconfigdata_name()`` from regular ``sysconfig``. +import sysconfig +if not compat.is_win and hasattr(sysconfig, '_get_sysconfigdata_name'): + hiddenimports = [sysconfig._get_sysconfigdata_name()] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-distutils.util.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-distutils.util.py new file mode 100644 index 0000000000000000000000000000000000000000..52b07f54cdb85962dd97f0df3f4d02556787f967 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-distutils.util.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# distutils.util.run_2to3() import lib2to3. Strip it sinse chances are low +# this is used by a frozen package. + +excludedimports = ['lib2to3.refactor'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.contrib.sessions.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.contrib.sessions.py new file mode 100644 index 0000000000000000000000000000000000000000..4481d5aa1e33e5c07e5a89e8f1473555904c3e60 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.contrib.sessions.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.hooks import collect_submodules +hiddenimports = collect_submodules('django.contrib.sessions.backends') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.cache.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.cache.py new file mode 100644 index 0000000000000000000000000000000000000000..c9eba4ba1600803cad35ab9b1659a2d9a0641d53 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.cache.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.hooks import collect_submodules +hiddenimports = collect_submodules('django.core.cache.backends') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.mail.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.mail.py new file mode 100644 index 0000000000000000000000000000000000000000..ba0adb49e41f2d3a782d5fbb1119f16c5dd1c281 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.mail.py @@ -0,0 +1,30 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +django.core.mail uses part of the email package. +Problem is: when using runserver with autoreload mode, the thread that +checks fore changed files unwillingly trigger further imports within +the email package because of the LazyImporter in email (used in 2.5 for +backward compatibility). +We then need to name those modules as hidden imports, otherwise at +runtime the autoreload thread will complain with a traceback. +""" + + +hiddenimports = [ + 'email.mime.message', + 'email.mime.image', + 'email.mime.text', + 'email.mime.multipart', + 'email.mime.audio' +] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.management.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.management.py new file mode 100644 index 0000000000000000000000000000000000000000..881148ee4e47c35a9e1c4f5a6ee332c95eae1398 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.core.management.py @@ -0,0 +1,22 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.compat import modname_tkinter +from PyInstaller.utils.hooks import collect_submodules + +# Module django.core.management.commands.shell imports IPython but it +# introduces many other dependencies that are not necessary for simple +# django project. Ignore then IPython module. +excludedimports = ['IPython', 'matplotlib', modname_tkinter] + +# Django requres management modules for the script 'manage.py'. +hiddenimports = collect_submodules('django.core.management.commands') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.mysql.base.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.mysql.base.py new file mode 100644 index 0000000000000000000000000000000000000000..e9bae91a09eefefb5937acdffcd25f3567a95453 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.mysql.base.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# Compiler module (see class DatabaseOperations) +hiddenimports = ["django.db.backends.mysql.compiler"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.oracle.base.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.oracle.base.py new file mode 100644 index 0000000000000000000000000000000000000000..bd5b4af38ac4b7632f221f3009fe771a8de993b0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.oracle.base.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +hiddenimports = ["django.db.backends.oracle.compiler"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.py new file mode 100644 index 0000000000000000000000000000000000000000..abf7a8ba3dbfa34034aaaccfd634e12352df9449 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.db.backends.py @@ -0,0 +1,26 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 glob + +from PyInstaller.utils.hooks import get_module_file_attribute + +# Compiler (see class BaseDatabaseOperations) +hiddenimports = ['django.db.models.sql.compiler'] + +# Include all available Django backends. +modpath = os.path.dirname(get_module_file_attribute('django.db.backends')) +for fn in glob.glob(os.path.join(modpath, '*')): + if os.path.isdir(fn): + fn = os.path.basename(fn) + hiddenimports.append('django.db.backends.' + fn + '.base') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.py new file mode 100644 index 0000000000000000000000000000000000000000..a4a8d705571e6487d19c1e36aab3eb468ffe1f03 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.py @@ -0,0 +1,99 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# Tested with django 2.2 + + +import sys +import glob +import os +from PyInstaller import log as logging +from PyInstaller.utils.hooks import ( + django_find_root_dir, django_dottedstring_imports, collect_all, + collect_submodules, collect_data_files, get_module_file_attribute, + get_module_attribute) + + +logger = logging.getLogger(__name__) + +datas, binaries, hiddenimports = collect_all('django') + + +root_dir = django_find_root_dir() +if root_dir: + logger.info('Django root directory %s', root_dir) + # Include imports from the mysite.settings.py module. + settings_py_imports = django_dottedstring_imports(root_dir) + # Include all submodules of all imports detected in mysite.settings.py. + for submod in settings_py_imports: + hiddenimports.append(submod) + hiddenimports += collect_submodules(submod) + # Include main django modules - settings.py, urls.py, wsgi.py. + # Without them the django server won't run. + package_name = os.path.basename(root_dir) + default_settings_module = f'{package_name}.settings' + settings_module = os.environ.get('DJANGO_SETTINGS_MODULE', default_settings_module) + hiddenimports += [ + # TODO Consider including 'mysite.settings.py' in source code as a data files. + # Since users might need to edit this file. + settings_module, + package_name + '.urls', + package_name + '.wsgi', + ] + # Django hiddenimports from the standard Python library. + hiddenimports += [ + 'http.cookies', + 'html.parser', + ] + + # Bundle django DB schema migration scripts as data files. + # They are necessary for some commands. + logger.info('Collecting Django migration scripts.') + migration_modules = [ + 'django.conf.app_template.migrations', + 'django.contrib.admin.migrations', + 'django.contrib.auth.migrations', + 'django.contrib.contenttypes.migrations', + 'django.contrib.flatpages.migrations', + 'django.contrib.redirects.migrations', + 'django.contrib.sessions.migrations', + 'django.contrib.sites.migrations', + ] + # Include migration scripts of Django-based apps too. + installed_apps = eval(get_module_attribute(settings_module, 'INSTALLED_APPS')) + migration_modules.extend(set(app + '.migrations' for app in installed_apps)) + # Copy migration files. + for mod in migration_modules: + mod_name, bundle_name = mod.split('.', 1) + mod_dir = os.path.dirname(get_module_file_attribute(mod_name)) + bundle_dir = bundle_name.replace('.', os.sep) + pattern = os.path.join(mod_dir, bundle_dir, '*.py') + files = glob.glob(pattern) + for f in files: + datas.append((f, os.path.join(mod_name, bundle_dir))) + + # Include data files from your Django project found in your django root package. + datas += collect_data_files(package_name) + + # Include database file if using sqlite. The sqlite database is usually next to the manage.py script. + root_dir_parent = os.path.dirname(root_dir) + # TODO Add more patterns if necessary. + _patterns = ['*.db', 'db.*'] + for p in _patterns: + files = glob.glob(os.path.join(root_dir_parent, p)) + for f in files: + # Place those files next to the executable. + datas.append((f, '.')) + + +else: + logger.warning('No django root directory could be found!') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.template.loaders.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.template.loaders.py new file mode 100644 index 0000000000000000000000000000000000000000..ace02df8cf3c6d37f2a7e4d9c9a4cd54ecff9cdc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django.template.loaders.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.hooks import collect_submodules +hiddenimports = collect_submodules('django.template.loaders') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django_babel.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django_babel.py new file mode 100644 index 0000000000000000000000000000000000000000..0a58e0b426871861a8032e2732f52aff392af3a2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-django_babel.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Tested with django-babel v0.6.2. +# Hook for https://github.com/python-babel/django-babel + +from PyInstaller.utils.hooks import copy_metadata +datas = copy_metadata('django-babel') + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-encodings.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-encodings.py new file mode 100644 index 0000000000000000000000000000000000000000..84cf0ff1a563e2f5f1872603a68873ea1b63f486 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-encodings.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.hooks import collect_submodules + +hiddenimports = collect_submodules('encodings') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gevent.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gevent.py new file mode 100644 index 0000000000000000000000000000000000000000..616459ed2437a83f55b24ee18e7201faebb48fa1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gevent.py @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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 PyInstaller.utils.hooks import collect_all + +excludedimports = ["gevent.testing", "gevent.tests"] + +datas, binaries, hiddenimports = collect_all( + 'gevent', + filter_submodules=lambda name: ( + "gevent.testing" not in name or "gevent.tests" not in name), + include_py_files=False, + exclude_datas=["**/tests"]) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.py new file mode 100644 index 0000000000000000000000000000000000000000..b48aa81b16a53b25c33de9e6287ae56c898c572b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject + +Tested with PyGObject 3.16.2 from MacPorts on Mac OS X 10.10 and +PyGobject 3.14.0 on Windows 7 +""" + +hiddenimports = ['gi._error', 'gi._option'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Atk.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Atk.py new file mode 100644 index 0000000000000000000000000000000000000000..cc1b53bc6d067cba952cf327d99234932fdbf718 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Atk.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" +from PyInstaller.utils.hooks import collect_glib_translations, get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('Atk', '1.0') +datas += collect_glib_translations('atk10') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Champlain.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Champlain.py new file mode 100644 index 0000000000000000000000000000000000000000..f030251880e292236ca6679a7c372f17e351bc9f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Champlain.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject's "gi.repository.Champlain" package. +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('Champlain', '0.12') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Clutter.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Clutter.py new file mode 100644 index 0000000000000000000000000000000000000000..a1cf637b3ea1472f8cec1f967d7a83df34d4dada --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Clutter.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject's "gi.repository.Clutter" package. +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('Clutter', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GIRepository.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GIRepository.py new file mode 100644 index 0000000000000000000000000000000000000000..c970958ce2efa4e4d249d99cbd9092b4fcf1b642 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GIRepository.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GIRepository', '2.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GLib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GLib.py new file mode 100644 index 0000000000000000000000000000000000000000..e0abbc83f58826eb3cbdf8194e5e20cbb5d4154e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GLib.py @@ -0,0 +1,36 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for the GLib library https://wiki.gnome.org/Projects/GLib introspected through +PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware layer +https://wiki.gnome.org/Projects/GObjectIntrospection + +Tested with GLib 2.44.1, PyGObject 3.16.2, and GObject Introspection 1.44.0 on Mac OS X 10.10 and +GLib 2.42.2, PyGObject 3.14.0, and GObject Introspection 1.42 on Windows 7 +""" + +import os +import glob + +from PyInstaller.utils.hooks import collect_glib_translations, \ + collect_glib_share_files, get_gi_typelibs, get_gi_libdir +from PyInstaller.compat import is_win + +binaries, datas, hiddenimports = get_gi_typelibs('GLib', '2.0') +datas += collect_glib_translations('glib20') +datas += collect_glib_share_files('glib-2.0', 'schemas') + +# On Windows, glib needs a spawn helper for g_spawn* API +if is_win: + libdir = get_gi_libdir('GLib', '2.0') + pattern = os.path.join(libdir, 'gspawn-*-helper*.exe') + for f in glob.glob(pattern): + binaries.append((f, '.')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GModule.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GModule.py new file mode 100644 index 0000000000000000000000000000000000000000..f67da7eac374c3dbaa4add81a277cd2d3739a15d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GModule.py @@ -0,0 +1,22 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for GModule https://developer.gnome.org/glib/stable/glib-Dynamic-Loading-of-Modules.html from the GLib +library https://wiki.gnome.org/Projects/GLib introspected through PyGobject https://wiki.gnome.org/PyGObject +via the GObject Introspection middleware layer https://wiki.gnome.org/Projects/GObjectIntrospection + +Tested with GLib 2.44.1, PyGObject 3.16.2, and GObject Introspection 1.44.0 on Mac OS X 10.10 and +GLib 2.42.2, PyGObject 3.14.0, and GObject Introspection 1.42 on Windows 7 +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GModule', '2.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GObject.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GObject.py new file mode 100644 index 0000000000000000000000000000000000000000..47b653389d327928f50e65bb8492b97f922745fc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GObject.py @@ -0,0 +1,24 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for GObject https://developer.gnome.org/gobject/stable/ from the GLib +library https://wiki.gnome.org/Projects/GLib introspected through PyGobject https://wiki.gnome.org/PyGObject +via the GObject Introspection middleware layer https://wiki.gnome.org/Projects/GObjectIntrospection + +Tested with GLib 2.44.1, PyGObject 3.16.2, and GObject Introspection 1.44.0 on Mac OS X 10.10 and +GLib 2.42.2, PyGObject 3.14.0, and GObject Introspection 1.42 on Windows 7 +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GObject', '2.0') + +hiddenimports += ['gi._gobject'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gdk.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gdk.py new file mode 100644 index 0000000000000000000000000000000000000000..63ad005a86835c59f39a1a902102a9b40151d3e3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gdk.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('Gdk', '3.0') +hiddenimports += ['gi._gi_cairo', 'gi.repository.cairo'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GdkPixbuf.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GdkPixbuf.py new file mode 100644 index 0000000000000000000000000000000000000000..070421986ebd12aa7ea0789b97172fac6ad95a77 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GdkPixbuf.py @@ -0,0 +1,160 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject's "gi.repository.GdkPixbuf" package. +""" + +import glob +import os +from shutil import which + +from PyInstaller.config import CONF +from PyInstaller.compat import ( + exec_command_stdout, is_darwin, is_win, is_linux, open_file) +from PyInstaller.utils.hooks import ( + collect_glib_translations, get_gi_typelibs, get_gi_libdir, logger) + +loaders_path = os.path.join('gdk-pixbuf-2.0', '2.10.0', 'loaders') + +destpath = "lib/gdk-pixbuf/loaders" +cachedest = "lib/gdk-pixbuf" + +# If the "gdk-pixbuf-query-loaders" command is not in the current ${PATH}, or +# is not in the GI lib path, GDK and thus GdkPixbuf is unavailable. Return with +# a non-fatal warning. +gdk_pixbuf_query_loaders = None + +try: + libdir = get_gi_libdir('GdkPixbuf', '2.0') +except ValueError: + logger.warning( + '"hook-gi.repository.GdkPixbuf" ignored, ' + 'since GdkPixbuf library not found' + ) + libdir = None + +if libdir: + + # Distributions either package gdk-pixbuf-query-loaders in the GI libs + # directory (not on the path), or on the path with or without a -x64 suffix + # depending on the architecture + cmds = [ + os.path.join(libdir, 'gdk-pixbuf-2.0/gdk-pixbuf-query-loaders'), + 'gdk-pixbuf-query-loaders-64', + 'gdk-pixbuf-query-loaders', + ] + + for cmd in cmds: + gdk_pixbuf_query_loaders = which(cmd) + if gdk_pixbuf_query_loaders is not None: + break + + if gdk_pixbuf_query_loaders is None: + logger.warning( + '"hook-gi.repository.GdkPixbuf" ignored, since ' + '"gdk-pixbuf-query-loaders" is not in $PATH or gi lib dir.' + ) + + # Else, GDK is available. Let's do this. + else: + binaries, datas, hiddenimports = get_gi_typelibs('GdkPixbuf', '2.0') + datas += collect_glib_translations('gdk-pixbuf') + + # To add support for a new platform, add a new "elif" branch below with + # the proper is_() test and glob for finding loaders on that + # platform. + if is_win: + ext = "*.dll" + elif is_darwin or is_linux: + ext = "*.so" + + # If loader detection is supported on this platform, bundle all + # detected loaders and an updated loader cache. + if ext: + loader_libs = [] + + # Bundle all found loaders with this user application. + pattern = os.path.join(libdir, loaders_path, ext) + for f in glob.glob(pattern): + binaries.append((f, destpath)) + loader_libs.append(f) + + # Sometimes the loaders are stored in a different directory from + # the library (msys2) + if not loader_libs: + pattern = os.path.join(libdir, '..', 'lib', loaders_path, ext) + for f in glob.glob(pattern): + binaries.append((f, destpath)) + loader_libs.append(f) + + # Filename of the loader cache to be written below. + cachefile = os.path.join(CONF['workpath'], 'loaders.cache') + + # Run the "gdk-pixbuf-query-loaders" command and capture its + # standard output providing an updated loader cache; then write + # this output to the loader cache bundled with this frozen + # application. On all platforms, we also move the package structure + # to point to lib/gdk-pixbuf instead of lib/gdk-pixbuf-2.0/2.10.0 + # in order to make compatible for OSX application signing. + # + # On OSX we use @executable_path to specify a path relative to the + # generated bundle. However, on non-Windows we need to rewrite the + # loader cache because it isn't relocatable by default. See + # https://bugzilla.gnome.org/show_bug.cgi?id=737523 + # + # To make it easier to rewrite, we just always write + # @executable_path, since its significantly easier to find/replace + # at runtime. :) + # + # To permit string munging, decode the encoded bytes output by + # this command (i.e., enable the "universal_newlines" option). + # + # On Fedora, the default loaders cache is /usr/lib64, but the + # libdir is actually /lib64. To get around this, we pass the + # path to the loader command, and it will create a cache with + # the right path. + # + # On Windows, the loaders lib directory is relative, starts with + # 'lib', and uses \\ as path separators (escaped \). + cachedata = exec_command_stdout(gdk_pixbuf_query_loaders, + *loader_libs) + + cd = [] + prefix = '"' + os.path.join(libdir, 'gdk-pixbuf-2.0', '2.10.0') + plen = len(prefix) + + win_prefix = '"' + '\\\\'.join(['lib', 'gdk-pixbuf-2.0', '2.10.0']) + win_plen = len(win_prefix) + + # For each line in the updated loader cache... + for line in cachedata.splitlines(): + if line.startswith('#'): + continue + if line.startswith(prefix): + line = '"@executable_path/' + cachedest + line[plen:] + elif line.startswith(win_prefix): + line = '"' + cachedest.replace( + '/', '\\\\') + line[win_plen:] + cd.append(line) + + cachedata = '\n'.join(cd) + + # Write the updated loader cache to this file. + with open_file(cachefile, 'w') as fp: + fp.write(cachedata) + + # Bundle this loader cache with this frozen application. + datas.append((cachefile, cachedest)) + # Else, loader detection is unsupported on this platform. + else: + logger.warning( + 'GdkPixbuf loader bundling unsupported on your platform.' + ) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gio.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gio.py new file mode 100644 index 0000000000000000000000000000000000000000..660f781f332ae1a39dd704889f76f27349842b35 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gio.py @@ -0,0 +1,68 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for Gio https://developer.gnome.org/gio/stable/ from the GLib library https://wiki.gnome.org/Projects/GLib +introspected through PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware layer +https://wiki.gnome.org/Projects/GObjectIntrospection + +Tested with GLib 2.44.1, PyGObject 3.16.2, GObject Introspection 1.44.0 on Mac OS X 10.10.5 and +GLib 2.42.2, PyGObject 3.14.0, and GObject Introspection 1.42 on Windows 7 +""" + +import glob +import os +import sys + +import PyInstaller.log as logging +from PyInstaller.compat import is_darwin, is_win, is_linux, base_prefix +from PyInstaller.utils.hooks import get_gi_typelibs, get_gi_libdir, exec_statement + +logger = logging.getLogger(__name__) + +binaries, datas, hiddenimports = get_gi_typelibs('Gio', '2.0') + +libdir = get_gi_libdir('Gio', '2.0') +path = None + +if is_win: + pattern = os.path.join(libdir, 'gio', 'modules', '*.dll') +elif is_darwin or is_linux: + gio_libdir = os.path.join(libdir, 'gio', 'modules') + if not os.path.exists(gio_libdir): + # homebrew installs the files elsewhere.. + gio_libdir = os.path.join(os.path.commonprefix([base_prefix, gio_libdir]), 'lib', 'gio', 'modules') + + pattern = os.path.join(gio_libdir, '*.so') + +if pattern: + for f in glob.glob(pattern): + binaries.append((f, 'gio_modules')) +else: + # To add a new platform add a new elif above with the proper is_ and + # proper pattern for finding the Gio modules on your platform. + logger.warning('Bundling Gio modules is currently not supported on your platform.') + + +# Bundle the mime cache -- might not be needed on Windows +# -> this is used for content type detection (also used by GdkPixbuf) +# -> gio/xdgmime/xdgmime.c looks for mime/mime.cache in the users home directory, +# followed by XDG_DATA_DIRS if specified in the environment, otherwise +# it searches /usr/local/share/ and /usr/share/ +if not is_win: + _mime_searchdirs = ['/usr/local/share', '/usr/share'] + if 'XDG_DATA_DIRS' in os.environ: + _mime_searchdirs.insert(0, os.environ['XDG_DATA_DIRS']) + + for sd in _mime_searchdirs: + spath = os.path.join(sd, 'mime', 'mime.cache') + if os.path.exists(spath): + datas.append((spath, 'share/mime')) + break diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gst.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gst.py new file mode 100644 index 0000000000000000000000000000000000000000..56cb34bd6c0a97e08b2ef32cca7b62af28b5dbb0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gst.py @@ -0,0 +1,60 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for Gst(GStreamer) http://gstreamer.freedesktop.org/ introspected through +PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware +layer https://wiki.gnome.org/Projects/GObjectIntrospection + +Tested with GStreamer 1.4.5, gst-python 1.4.0, PyGObject 3.16.2, and GObject Introspection 1.44.0 on Mac OS X 10.10 and +GStreamer 1.4.5, gst-python 1.4.0, PyGObject 3.14.0, and GObject Introspection 1.42 on Windows 7 +""" + + +# GStreamer contains a lot of plugins. We need to collect them and bundle them wih the exe file. +# We also need to resolve binary dependencies of these GStreamer plugins. + + +import glob +import os +from PyInstaller.utils.hooks import collect_glib_share_files, collect_glib_translations, exec_statement, get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('Gst', '1.0') + +datas += collect_glib_share_files('gstreamer-1.0') + +hiddenimports += ["gi.repository.Gio"] + +for prog in ['gst-plugins-bad-1.0', + 'gst-plugins-base-1.0', + 'gst-plugins-good-1.0', + 'gst-plugins-ugly-1.0', + 'gstreamer-1.0']: + datas += collect_glib_translations(prog) + +statement = """ +import os +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst +Gst.init(None) +reg = Gst.Registry.get() +plug = reg.find_plugin('coreelements') +path = plug.get_filename() +print(os.path.dirname(path)) +""" + +plugin_path = exec_statement(statement) + +# Use a pattern of libgst* since all GStreamer plugins that conform to GStreamer standards start with libgst +# and we may have mixed plugin extensions, e.g., .so and .dylib. +for pattern in ['libgst*.dll', 'libgst*.dylib', 'libgst*.so']: + pattern = os.path.join(plugin_path, pattern) + binaries += [(f, os.path.join('gst_plugins')) for f in glob.glob(pattern)] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstAudio.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstAudio.py new file mode 100644 index 0000000000000000000000000000000000000000..9924a1b84736a894f7222ac6c1888831dd7f2d21 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstAudio.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for Gst(GStreamer) http://gstreamer.freedesktop.org/ introspected through +PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware +layer https://wiki.gnome.org/Projects/GObjectIntrospection +""" +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GstAudio', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstBase.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstBase.py new file mode 100644 index 0000000000000000000000000000000000000000..833e40066bfb0a99466a9bca0c0b798be282897d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstBase.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for Gst(GStreamer) http://gstreamer.freedesktop.org/ introspected through +PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware +layer https://wiki.gnome.org/Projects/GObjectIntrospection +""" +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GstBase', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstPbutils.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstPbutils.py new file mode 100644 index 0000000000000000000000000000000000000000..4de493d3053ec68c2a2dcda918ecd8ba80256b90 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstPbutils.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for Gst(GStreamer) http://gstreamer.freedesktop.org/ introspected through +PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware +layer https://wiki.gnome.org/Projects/GObjectIntrospection +""" +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GstPbutils', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstTag.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstTag.py new file mode 100644 index 0000000000000000000000000000000000000000..3e0a4f15a2d0e2ababa66fbaf415ebd562247c04 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstTag.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for Gst(GStreamer) http://gstreamer.freedesktop.org/ introspected through +PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware +layer https://wiki.gnome.org/Projects/GObjectIntrospection +""" +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GstTag', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstVideo.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstVideo.py new file mode 100644 index 0000000000000000000000000000000000000000..c60f1f616a56d001ffba7c629693c0a8cb96d958 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GstVideo.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for Gst(GStreamer) http://gstreamer.freedesktop.org/ introspected through +PyGobject https://wiki.gnome.org/PyGObject via the GObject Introspection middleware +layer https://wiki.gnome.org/Projects/GObjectIntrospection +""" +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GstVideo', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gtk.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gtk.py new file mode 100644 index 0000000000000000000000000000000000000000..45bb64c006687888d40dc93c16c774fd5613b57e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Gtk.py @@ -0,0 +1,33 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +import os +import os.path +import glob + +from PyInstaller.compat import is_win +from PyInstaller.utils.hooks import collect_glib_share_files, collect_glib_etc_files, collect_glib_translations, exec_statement, get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('Gtk', '3.0') + +datas += collect_glib_share_files('fontconfig') +datas += collect_glib_share_files('icons') +datas += collect_glib_share_files('themes') +datas += collect_glib_translations('gtk30') + +# these only seem to be required on Windows +if is_win: + datas += collect_glib_etc_files('fonts') + datas += collect_glib_etc_files('pango') + datas += collect_glib_share_files('fonts') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkChamplain.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkChamplain.py new file mode 100644 index 0000000000000000000000000000000000000000..fb1623beb75c9a876d2b50818c0ce9f3d271293a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkChamplain.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject's "gi.repository.GtkChamplain" package. +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GtkChamplain', '0.12') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkClutter.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkClutter.py new file mode 100644 index 0000000000000000000000000000000000000000..93a73e4e43adfcef787bbfe67e4858e8414d9ba4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkClutter.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject's "gi.repository.GtkClutter" package. +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GtkClutter', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkSource.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkSource.py new file mode 100644 index 0000000000000000000000000000000000000000..1cba2e873062a4b470212ed85263a3d6771363cf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkSource.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.hooks import collect_glib_share_files, get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('GtkSource', '3.0') + +datas += collect_glib_share_files('gtksourceview-3.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkosxApplication.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkosxApplication.py new file mode 100644 index 0000000000000000000000000000000000000000..8d806d2297f28a05af0bacaba703fb7d10ac960f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.GtkosxApplication.py @@ -0,0 +1,22 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +from PyInstaller.compat import is_darwin +from PyInstaller.utils.hooks import get_gi_typelibs + + +if is_darwin: + binaries, datas, hiddenimports = get_gi_typelibs( + 'GtkosxApplication', '1.0' + ) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.HarfBuzz.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.HarfBuzz.py new file mode 100644 index 0000000000000000000000000000000000000000..d6dd836d88e5c39a78f45d70af151e97380917bc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.HarfBuzz.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + + +binaries, datas, hiddenimports = get_gi_typelibs('HarfBuzz', '0.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Pango.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Pango.py new file mode 100644 index 0000000000000000000000000000000000000000..2a2efa9a5c4428aaae52a18869513cc149f20130 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.Pango.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('Pango', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.PangoCairo.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.PangoCairo.py new file mode 100644 index 0000000000000000000000000000000000000000..ece47cc719b30ed97df981cb99cebba50e1881dd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.PangoCairo.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('PangoCairo', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.cairo.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.cairo.py new file mode 100644 index 0000000000000000000000000000000000000000..fbd0fcebca499ac30a0a972a30cf8ffbef035b55 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.cairo.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('cairo', '1.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.xlib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.xlib.py new file mode 100644 index 0000000000000000000000000000000000000000..63890ebea78009d957e659cabe4c0f358175632e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-gi.repository.xlib.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 hook for PyGObject https://wiki.gnome.org/PyGObject +""" + +from PyInstaller.utils.hooks import get_gi_typelibs + +binaries, datas, hiddenimports = get_gi_typelibs('xlib', '2.0') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-heapq.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-heapq.py new file mode 100644 index 0000000000000000000000000000000000000000..7d7659bbd42a4824faf99998a1567c1bec869eb4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-heapq.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +# only required when run as `__main__` +excludedimports = ["doctest"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-idlelib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-idlelib.py new file mode 100644 index 0000000000000000000000000000000000000000..9d8ee9080a45fe4db7e7a0b9c9962f3e8c22660e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-idlelib.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_data_files +datas = collect_data_files('idlelib') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-importlib_metadata.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-importlib_metadata.py new file mode 100644 index 0000000000000000000000000000000000000000..74a15a891a5a5cfcc9484e79612c0a1afd06b40c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-importlib_metadata.py @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2019-2021, 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) +#----------------------------------------------------------------------------- + + +""" +importlib_metadata is a library to access the metadata for a Python package. +This functionality intends to replace most uses of pkg_resources entry point +API and metadata API. +""" + +from PyInstaller.utils.hooks import copy_metadata + +datas = copy_metadata('importlib_metadata') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-importlib_resources.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-importlib_resources.py new file mode 100644 index 0000000000000000000000000000000000000000..89b11dc93f13ced2e5eeb4ee760329f39a4ceed6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-importlib_resources.py @@ -0,0 +1,33 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2019-2021, 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) +#----------------------------------------------------------------------------- +""" +`importlib_resources` is a backport of the 3.9+ module `importlib.resources` +""" + +import os +from PyInstaller.utils.hooks import get_module_file_attribute, \ + is_module_satisfies, copy_metadata + +if is_module_satisfies("importlib_resources >= 1.2.0"): + # since 1.2.0 importlib.metadata is used + datas = copy_metadata('importlib_resources') +else: + # include the version.txt file, used to set __version__ + res_loc = os.path.dirname(get_module_file_attribute('importlib_resources')) + datas = [ + (os.path.join(res_loc, 'version.txt'), 'importlib_resources'), + ] + +if is_module_satisfies("importlib_resources >= 1.3.1"): + hiddenimports = ['importlib_resources.trees'] + +# this is only required for python2 support +excludedimports = ['importlib_resources._py2'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-keyring.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-keyring.py new file mode 100644 index 0000000000000000000000000000000000000000..fa0161d3c6b00770e5f31179b449d54322a55cea --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-keyring.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 PyInstaller.utils.hooks import collect_submodules, copy_metadata + +# Collect backends +hiddenimports = collect_submodules('keyring.backends') + +# Keyring performs backend plugin discovery using setuptools entry points, +# which are listed in the metadata. Therefore, we need to copy the +# metadata, otherwise no backends will be found at run-time. +datas = copy_metadata('keyring') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-kivy.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-kivy.py new file mode 100644 index 0000000000000000000000000000000000000000..db4b46e05838b7c182ed437552f63f50e0b8d52c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-kivy.py @@ -0,0 +1,27 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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 PyInstaller.utils.hooks import is_module_satisfies +from PyInstaller import log as logging + +if is_module_satisfies('kivy >= 1.9.1'): + from kivy.tools.packaging.pyinstaller_hooks import ( + add_dep_paths, excludedimports, datas, get_deps_all, + get_factory_modules, kivy_modules) + + add_dep_paths() + + hiddenimports = get_deps_all()['hiddenimports'] + hiddenimports = list(set(get_factory_modules() + kivy_modules + + hiddenimports)) +else: + logger = logging.getLogger(__name__) + logger.warning('Hook disabled because of Kivy version < 1.9.1') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-lib2to3.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-lib2to3.py new file mode 100644 index 0000000000000000000000000000000000000000..1e5c12b4081864ffda47b19edd51cc0f67e2ab05 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-lib2to3.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 is needed to bundle lib2to3 Grammars files + +from PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files('lib2to3') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.backends.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.backends.py new file mode 100644 index 0000000000000000000000000000000000000000..0e3ee49abf05598b01a004a74bebb793e69e1c39 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.backends.py @@ -0,0 +1,84 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.compat import is_darwin +from PyInstaller.utils.hooks import ( + eval_statement, exec_statement, logger) + + +def get_matplotlib_backend_module_names(): + """ + List the names of all matplotlib backend modules importable under the + current Python installation. + + Returns + ---------- + list + List of the fully-qualified names of all such modules. + """ + # Statement safely importing a single backend module. + import_statement = """ +import os, sys + +# Preserve stdout. +sys_stdout = sys.stdout + +try: + # Redirect output printed by this importation to "/dev/null", preventing + # such output from being erroneously interpreted as an error. + with open(os.devnull, 'w') as dev_null: + sys.stdout = dev_null + __import__('%s') +# If this is an ImportError, print this exception's message without a traceback. +# ImportError messages are human-readable and require no additional context. +except ImportError as exc: + sys.stdout = sys_stdout + print(exc) +# Else, print this exception preceded by a traceback. traceback.print_exc() +# prints to stderr rather than stdout and must not be called here! +except Exception: + sys.stdout = sys_stdout + import traceback + print(traceback.format_exc()) +""" + + # List of the human-readable names of all available backends. + backend_names = eval_statement( + 'import matplotlib; print(matplotlib.rcsetup.all_backends)') + + # List of the fully-qualified names of all importable backend modules. + module_names = [] + + # If the current system is not OS X and the "CocoaAgg" backend is available, + # remove this backend from consideration. Attempting to import this backend + # on non-OS X systems halts the current subprocess without printing output + # or raising exceptions, preventing its reliable detection. + if not is_darwin and 'CocoaAgg' in backend_names: + backend_names.remove('CocoaAgg') + + # For safety, attempt to import each backend in a unique subprocess. + for backend_name in backend_names: + module_name = 'matplotlib.backends.backend_%s' % backend_name.lower() + stdout = exec_statement(import_statement % module_name) + + # If no output was printed, this backend is importable. + if not stdout: + module_names.append(module_name) + logger.info(' Matplotlib backend "%s": added' % backend_name) + else: + logger.info(' Matplotlib backend "%s": ignored\n %s' % (backend_name, stdout)) + + return module_names + +# Freeze all importable backends, as PyInstaller is unable to determine exactly +# which backends are required by the current program. +hiddenimports = get_matplotlib_backend_module_names() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.numerix.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.numerix.py new file mode 100644 index 0000000000000000000000000000000000000000..0424cdb36074b7733e93fe326eff33881b003af4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.numerix.py @@ -0,0 +1,25 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +The matplotlib.numerix package sneaks these imports in under the radar: +""" + + +hiddenimports = [ + 'fft', + 'linear_algebra', + 'random_array', + 'ma', + 'mlab', +] + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.py new file mode 100644 index 0000000000000000000000000000000000000000..91d12e12fe19409190a301b12a1d81cf7b6b11b3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-matplotlib.py @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import exec_statement + +mpl_data_dir = exec_statement( + "import matplotlib; print(matplotlib.get_data_path())") +assert mpl_data_dir, "Failed to determine matplotlib's data directory!" + +datas = [ + (mpl_data_dir, "matplotlib/mpl-data"), +] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-multiprocessing.util.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-multiprocessing.util.py new file mode 100644 index 0000000000000000000000000000000000000000..05f95c425a2c4f6adeb3ae91782c9d8202ec9dec --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-multiprocessing.util.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# In Python 3.8 mutliprocess.utils has _cleanup_tests() to cleanup +# multiprocessing resources when multiprocessing tests completed. This +# function import `tests` which is the complete Python test-suite, pulling in +# many more dependencies, e.g. tkinter. + +excludedimports = ['test'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-numpy._pytesttester.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-numpy._pytesttester.py new file mode 100644 index 0000000000000000000000000000000000000000..60e5d4467179fbe1ed63e27ceca709bb78448ee4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-numpy._pytesttester.py @@ -0,0 +1,17 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +# ----------------------------------------------------------------------------- + +# numpy._pytesttester is unconditionally imported by numpy.core, thus we can +# not exclude _pytesttester (which would be preferred). Anway we can avoid +# importing pytest, which pulls in anotehr 150+ modules. +# See https://github.com/numpy/numpy/issues/17183 + +excludedimports = ["pytest"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-numpy.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-numpy.py new file mode 100644 index 0000000000000000000000000000000000000000..06aa7a80bbff7e677fdf3f82a1a540c9db69dbee --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-numpy.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +# --- Copyright Disclaimer --- +# +# In order to support PyInstaller with numpy<1.20.0 this file will be +# duplicated for a short period inside PyInstaller's repository [1]. However +# this file is the intellectual property of the NumPy team and is under the +# terms and conditions outlined their repository [2]. +# +# .. refs: +# +# [1] PyInstaller: https://github.com/pyinstaller/pyinstaller/ +# [2] NumPy's license: https://github.com/numpy/numpy/blob/master/LICENSE.txt +# +""" +This hook should collect all binary files and any hidden modules that numpy +needs. + +Our (some-what inadequate) docs for writing PyInstaller hooks are kept here: +https://pyinstaller.readthedocs.io/en/stable/hooks.html + +PyInstaller has a lot of NumPy users so we'd consider maintaining this hook to +be high priority. Feel free to @mention either bwoodsend or Legorooj on Github +for help keeping it working. +""" + +from PyInstaller.utils.hooks import collect_dynamic_libs +from PyInstaller.compat import is_conda, is_pure_conda + +# Collect all DLLs inside numpy's installation folder, dump them into built +# app's root. +binaries = collect_dynamic_libs("numpy", ".") + +# If using Conda without any non-conda virtual environment manager: +if is_pure_conda: + # Assume running the NumPy from Conda-forge and collect it's DLLs from the + # communal Conda bin directory. DLLs from NumPy's dependencies must also be + # collected to capture MKL, OpenBlas, OpenMP, etc. + from PyInstaller.utils.hooks import conda_support + datas = conda_support.collect_dynamic_libs("numpy", dependencies=True) + +# Submodules PyInstaller can't detect (probably because they're only imported +# by extension modules which PyInstaller can't read). +hiddenimports = ['numpy.core._dtype_ctypes'] +if is_conda: + hiddenimports.append("six") + +# Remove testing and building code and packages which are referenced throughout +# NumPy but aren't really dependencies. +excludedimports = [ + "scipy", + "pytest", + "nose", + "distutils", + "f2py", + "setuptools", + "numpy.f2py", + "numpy.distutils", +] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-packaging.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-packaging.py new file mode 100644 index 0000000000000000000000000000000000000000..f772b3ae1faae06372ec15fb310cc4f6941de3d9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-packaging.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Duplicate hook-pkg_resources.py. +hiddenimports = ["pkg_resources"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pandas.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pandas.py new file mode 100644 index 0000000000000000000000000000000000000000..0873bb9d4c0803adbb7fa450edee068b8265c263 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pandas.py @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2017-2021, 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 PyInstaller.utils.hooks import collect_submodules, is_module_satisfies + +# Pandas keeps Python extensions loaded with dynamic imports here. +hiddenimports = collect_submodules('pandas._libs') + +# Pandas 1.2.0 and later require cmath hidden import on linux and macOS. +# On Windows, this is not strictly required, but we add it anyway to keep +# things simple (and future-proof). +if is_module_satisfies('pandas >= 1.2.0'): + hiddenimports += ['cmath'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pickle.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pickle.py new file mode 100644 index 0000000000000000000000000000000000000000..d43144bb6fec5738d38e0efbcb565c31a7e1dce2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pickle.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +# only required when run as `__main__` +excludedimports = ["argparse"] + +# pickle also imports `doctest`, which also is only used when run an +# `__main__`. Anyway, excluding it made some Qt related tests fail terribly +# with "ModuleNotFoundError: No module named '__future__'" when running the +# executable. diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pkg_resources.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pkg_resources.py new file mode 100644 index 0000000000000000000000000000000000000000..dc0343d084e21c43820347151cf6e7ea67cd07d2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pkg_resources.py @@ -0,0 +1,31 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.hooks import collect_submodules + +# pkg_resources keeps vendored modules in its _vendor subpackage, and does +# sys.meta_path based import magic to expose them as pkg_resources.extern.* +hiddenimports = collect_submodules('pkg_resources._vendor') + +# pkg_resources v45.0 dropped support for Python 2 and added this +# module printing a warning. We could save some bytes if we would +# replace this by a fake module. +hiddenimports.append('pkg_resources.py2_warn') + +excludedimports = ['__main__'] + +# Some more hidden imports. See: +# https://github.com/pyinstaller/pyinstaller-hooks-contrib/issues/15#issuecomment-663699288 +# `packaging` can either be its own package, or embeded in +# `pkg_resources._vendor.packaging`, or both. +# Assume the worst and include both if present. +hiddenimports += collect_submodules('packaging') + +hiddenimports += ['pkg_resources.markers'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pygments.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pygments.py new file mode 100644 index 0000000000000000000000000000000000000000..0d70299a2ffd2e5d05228a0bbf31fe3b25d6c423 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pygments.py @@ -0,0 +1,32 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +""" +PyInstaller hook file for Pygments. Tested with version 2.0.2. +""" + +from PyInstaller.utils.hooks import collect_submodules + +# The following applies to pygments version 2.0.2, as reported by ``pip show +# pygments``. +# +# From pygments.formatters, line 37:: +# +# def _load_formatters(module_name): +# """Load a formatter (and all others in the module too).""" +# mod = __import__(module_name, None, None, ['__all__']) +# +# Therefore, we need all the modules in ``pygments.formatters``. + +hiddenimports = collect_submodules('pygments.formatters') +hiddenimports.extend(collect_submodules('pygments.lexers')) +hiddenimports.extend(collect_submodules('pygments.styles')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pytz.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pytz.py new file mode 100644 index 0000000000000000000000000000000000000000..8119293f971aca32cc13cb5e59d1898cb82ce6a0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pytz.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_data_files + + +# On Linux pytz installed from distribution repository uses zoneinfo +# fron /usr/share/zoneinfo/ and no data files might be collected. +datas = collect_data_files('pytz') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pytzdata.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pytzdata.py new file mode 100644 index 0000000000000000000000000000000000000000..66631c4c6e7bf44443c6e2cd3f45d29f54cab9b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-pytzdata.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.hooks import collect_data_files + + +datas = collect_data_files("pytzdata") diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-qtawesome.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-qtawesome.py new file mode 100644 index 0000000000000000000000000000000000000000..eb21f637d8b20ae703d7dc76de08106b50ac47c1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-qtawesome.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2017-2021, 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) +#----------------------------------------------------------------------------- + +""" +Hook for QtAwesome (https://github.com/spyder-ide/qtawesome). +Font files and charmaps need to be included with module. +Tested with QtAwesome 0.4.4 and Python 3.6 on macOS 10.12.4. +""" + +from PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files('qtawesome') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scapy.layers.all.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scapy.layers.all.py new file mode 100644 index 0000000000000000000000000000000000000000..b88c474973032237caa857829cfdf9834980d542 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scapy.layers.all.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_submodules + +# The layers to load can be configured using scapy's conf.load_layers. +# from scapy.config import conf; print(conf.load_layers) +# I decided not to use this, but to include all layer modules. The +# reason is: When building the package, load_layers may not include +# all the layer modules the program will use later. + +hiddenimports = collect_submodules('scapy.layers') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.io.matlab.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.io.matlab.py new file mode 100644 index 0000000000000000000000000000000000000000..d8d7f495e457f860d774f7c1840de43c4274849d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.io.matlab.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# Module scipy.io.matlab allows to parse matlab files. +# The hidden import is necessary for SciPy 0.11+. +hiddenimports = ['scipy.io.matlab.streams'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.linalg.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.linalg.py new file mode 100644 index 0000000000000000000000000000000000000000..c83ef14f2726a8f64ea5127f8721d8ff22196188 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.linalg.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# The hidden import is necessary for SciPy 0.16+. +hiddenimports = ['scipy.linalg.cython_blas', 'scipy.linalg.cython_lapack'] + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.py new file mode 100644 index 0000000000000000000000000000000000000000..284901ec7775afe8c4be29f3c015bec591242eec --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.py @@ -0,0 +1,33 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 glob +from PyInstaller.utils.hooks import get_module_file_attribute +from PyInstaller.compat import is_win + +binaries = [] + +# package the DLL bundle that official scipy wheels for Windows ship +# The DLL bundle will either be in extra-dll on windows proper +# and in .libs if installed on a virtualenv created from MinGW (Git-Bash +# for example) +if is_win: + extra_dll_locations = ['extra-dll', '.libs'] + for location in extra_dll_locations: + dll_glob = os.path.join(os.path.dirname( + get_module_file_attribute('scipy')), location, "*.dll") + if glob.glob(dll_glob): + binaries.append((dll_glob, ".")) + +# collect library-wide utility extension modules +hiddenimports = ['scipy._lib.%s' % m for m in [ + 'messagestream', "_ccallback_c", "_fpumode"]] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.sparse.csgraph.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.sparse.csgraph.py new file mode 100644 index 0000000000000000000000000000000000000000..028f4c51099442664728c64b79456d8abd8c3d42 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.sparse.csgraph.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# The hidden import is necessary for SciPy 0.11+. +hiddenimports = ['scipy.sparse.csgraph._validation'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.spatial.transform.rotation.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.spatial.transform.rotation.py new file mode 100644 index 0000000000000000000000000000000000000000..61f5dd335a62ca94c0fe5b0be63857073af0f53c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.spatial.transform.rotation.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2021, 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 PyInstaller.utils.hooks import is_module_satisfies + +# As of scipy 1.6.0, scipy.spatial.transform.rotation is cython-compiled, +# so we fail to automatically pick up its imports +if is_module_satisfies("scipy >= 1.6.0"): + hiddenimports = ['scipy.spatial.transform._rotation_groups'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.special._ellip_harm_2.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.special._ellip_harm_2.py new file mode 100644 index 0000000000000000000000000000000000000000..142ebc0a5cb2437a02d00bce39e657efbbf62df0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.special._ellip_harm_2.py @@ -0,0 +1,33 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +""" +Module hook for the `scipy.special._ellip_harm_2` C extension first introduced +by SciPy >= 0.15.0. + +See Also +---------- +https://github.com/scipy/scipy/blob/master/scipy/special/_ellip_harm_2.pyx + This C extension's Cython-based implementation. +""" + +# In SciPy >= 0.15.0: +# +# 1. The "scipy.special.__init__" module imports... +# 2. The "scipy.special._ellip_harm" module imports... +# 3. The "scipy.special._ellip_harm_2" C extension imports... +# 4. The "scipy.integrate" package. +# +# The third import is undetectable by PyInstaller and hence explicitly listed. +# Since "_ellip_harm" and "_ellip_harm_2" were first introduced by SciPy 0.15.0, +# the following hidden import will only be applied for versions of SciPy +# guaranteed to provide these modules and C extensions. +hiddenimports = ['scipy.integrate'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.special._ufuncs.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.special._ufuncs.py new file mode 100644 index 0000000000000000000000000000000000000000..9008c30f95d5db89fa2936a2d5dbbd34577ae4da --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.special._ufuncs.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# Module scipy.io._ufunc on some other C/C++ extensions. +# The hidden import is necessary for SciPy 0.13+. +# Thanks to dyadkin, see issue #826. +hiddenimports = ['scipy.special._ufuncs_cxx'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.stats._stats.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.stats._stats.py new file mode 100644 index 0000000000000000000000000000000000000000..3e26bf22536571edfe7d9717de41c0fd9be699eb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scipy.stats._stats.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import is_module_satisfies + +if is_module_satisfies("scipy >= 1.5.0"): + hiddenimports = ['scipy.special.cython_special'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scrapy.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scrapy.py new file mode 100644 index 0000000000000000000000000000000000000000..f1d8f08ea05265ae550c3f79eb717415ce15a28c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-scrapy.py @@ -0,0 +1,24 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +# hook for https://pypi.org/project/Scrapy/ +# https://stackoverflow.com/questions/49085970/no-such-file-or-directory-error-using-pyinstaller-and-scrapy + +from PyInstaller.utils.hooks import collect_data_files, collect_submodules + +datas = collect_data_files('scrapy') + +hiddenimports = ( + collect_submodules('scrapy') + + collect_submodules('scrapy.pipelines') + + collect_submodules('scrapy.utils') + + collect_submodules('scrapy.extensions') +) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-setuptools.msvc.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-setuptools.msvc.py new file mode 100644 index 0000000000000000000000000000000000000000..db028e30fb5b81f93b1126ff7b416a6cadf6f02d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-setuptools.msvc.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +# Only imported if numpy is installed, not mean to pull in numpy. +excludedimports = ["numpy"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-setuptools.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-setuptools.py new file mode 100644 index 0000000000000000000000000000000000000000..e062b46c5dc747aaddd845315c7340b8efe53e9b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-setuptools.py @@ -0,0 +1,34 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.compat import is_unix, is_darwin +from PyInstaller.utils.hooks import collect_submodules + +excludedimports = ["setuptools.py27compat", "setuptools.py33compat"] + +hiddenimports = [ + # Test case import/test_zipimport2 fails during importing + # pkg_resources or setuptools when module not present. + 'distutils.command.build_ext', + 'setuptools.msvc', +] + +# Necessary for setuptools on Mac/Unix +if is_unix or is_darwin: + hiddenimports.append('syslog') + +# setuptools >= 39.0.0 is "vendoring" its own direct dependencies from +# "_vendor" to "extern". This also requires +# 'pre_safe_import_module/hook-setuptools.extern.six.moves.py' to make the +# moves defined in 'setuptools._vendor.six' importable under +# 'setuptools.extern.six'. +hiddenimports.extend(collect_submodules('setuptools._vendor')) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-shelve.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-shelve.py new file mode 100644 index 0000000000000000000000000000000000000000..2b21038617882a2d642820754e5b88b7a6c43fda --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-shelve.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- +# Tested on Windows 7 x64 With Pyhton 3.5 + +hiddenimports = ["dbm.ndbm", "dbm.dumb", "dbm.gnu"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sphinx.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sphinx.py new file mode 100644 index 0000000000000000000000000000000000000000..098e4bfbea58d006d95153e1a8d83975c6a17e61 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sphinx.py @@ -0,0 +1,65 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- +# +# ******************************************** +# hook-sphinx.py - Pyinstaller hook for Sphinx +# ******************************************** +from PyInstaller.utils.hooks import collect_submodules, collect_data_files, \ + eval_statement + +hiddenimports = ( + # Per http://sphinx-doc.org/extensions.html#builtin-sphinx-extensions, + # Sphinx extensions are all placed in ``sphinx.ext``. Include these. + collect_submodules('sphinx.ext') + + # + # The following analysis applies to Sphinx v. 1.3.1, reported by "pip show + # sphinx". + # + # From sphinx.application line 429: + # + # mod = __import__(extension, None, None, ['setup']) + # + # From sphinx.search line 228: + # + # lang_class = getattr(__import__(module, None, None, [classname]), + # classname) + # + # From sphinx.search line 119: + # + # languages = { + # 'da': 'sphinx.search.da.SearchDanish', + # 'de': 'sphinx.search.de.SearchGerman', + # 'en': SearchEnglish, + # + # So, we need all the languages in "sphinx.search". + collect_submodules('sphinx.search') + + collect_submodules('sphinx.websupport.search') + + collect_submodules('sphinx.domains') + + # + # From sphinx.cmdline line 173: + # + # locale = __import__('locale') # due to submodule of the same name + # + # Add this module. + ['locale'] + + # + # Sphinx relies on a number of built-in extensions that are dynamically + # imported. Collect all those. + list(eval_statement(""" + from sphinx.application import builtin_extensions + print(builtin_extensions) + """)) +) + +# Sphinx also relies on a number of data files in its directory hierarchy: for +# example, *.html and *.conf files in ``sphinx.themes``, translation files in +# ``sphinx.locale``, etc. +datas = collect_data_files('sphinx') + collect_data_files('alabaster') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sqlalchemy.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sqlalchemy.py new file mode 100644 index 0000000000000000000000000000000000000000..a705cac2ceda35c66f6110f67aec436d43c3d9e7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sqlalchemy.py @@ -0,0 +1,82 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 re +from PyInstaller.utils.hooks import ( + exec_statement, is_module_satisfies, logger) +from PyInstaller.compat import open_file, text_read_mode +from PyInstaller.lib.modulegraph.modulegraph import SourceModule +from PyInstaller.lib.modulegraph.util import guess_encoding + +# 'sqlalchemy.testing' causes bundling a lot of unnecessary modules. +excludedimports = ['sqlalchemy.testing'] + +# include most common database bindings +# some database bindings are detected and include some +# are not. We should explicitly include database backends. +hiddenimports = ['pysqlite2', 'MySQLdb', 'psycopg2', 'sqlalchemy.ext.baked'] + +if is_module_satisfies('sqlalchemy >= 1.4'): + hiddenimports.append("sqlalchemy.sql.default_comparator") + +# In SQLAlchemy >= 0.6, the "sqlalchemy.dialects" package provides dialects. +if is_module_satisfies('sqlalchemy >= 0.6'): + dialects = exec_statement("import sqlalchemy.dialects;print(sqlalchemy.dialects.__all__)") + dialects = eval(dialects.strip()) + + for n in dialects: + hiddenimports.append("sqlalchemy.dialects." + n) +# In SQLAlchemy <= 0.5, the "sqlalchemy.databases" package provides dialects. +else: + databases = exec_statement("import sqlalchemy.databases; print(sqlalchemy.databases.__all__)") + databases = eval(databases.strip()) + + for n in databases: + hiddenimports.append("sqlalchemy.databases." + n) + + +def hook(hook_api): + """ + SQLAlchemy 0.9 introduced the decorator 'util.dependencies'. This + decorator does imports. eg: + + @util.dependencies("sqlalchemy.sql.schema") + + This hook scans for included SQLAlchemy modules and then scans those modules + for any util.dependencies and marks those modules as hidden imports. + """ + + if not is_module_satisfies('sqlalchemy >= 0.9'): + return + + # this parser is very simplistic but seems to catch all cases as of V1.1 + depend_regex = re.compile(r'@util.dependencies\([\'"](.*?)[\'"]\)') + + hidden_imports_set = set() + known_imports = set() + for node in hook_api.module_graph.flatten(start=hook_api.module): + if isinstance(node, SourceModule) and \ + node.identifier.startswith('sqlalchemy.'): + known_imports.add(node.identifier) + # Determine the encoding of the source file. + with open_file(node.filename, 'rb') as f: + encoding = guess_encoding(f) + # Use that to open the file. + with open_file(node.filename, text_read_mode, + encoding=encoding) as f: + for match in depend_regex.findall(f.read()): + hidden_imports_set.add(match) + + hidden_imports_set -= known_imports + if len(hidden_imports_set): + logger.info(" Found %d sqlalchemy hidden imports", + len(hidden_imports_set)) + hook_api.add_imports(*list(hidden_imports_set)) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sqlite3.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sqlite3.py new file mode 100644 index 0000000000000000000000000000000000000000..31e1cd13065b0e38c6213270fc0ce8c369451ec9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sqlite3.py @@ -0,0 +1,22 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_submodules + +hiddenimports = [] + +# On Windows in Python 3.4 'sqlite3' package might contain tests. +# these tests are not necessary for the final executable. +for mod in collect_submodules('sqlite3'): + if not mod.startswith('sqlite3.test'): + hiddenimports.append(mod) + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sysconfig.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sysconfig.py new file mode 100644 index 0000000000000000000000000000000000000000..ebb496552cbad6b71e49daf753a4fbb6b5b1db14 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-sysconfig.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sysconfig + +from PyInstaller.compat import is_win + + +if not is_win and hasattr(sysconfig, '_get_sysconfigdata_name'): + # Python 3.6 uses additional modules like + # `_sysconfigdata_m_linux_x86_64-linux-gnu`, see + # https://github.com/python/cpython/blob/3.6/Lib/sysconfig.py#L417 + # Note: Some versions of Anaconda backport this feature to before 3.6. + # See issue #3105 + hiddenimports = [sysconfig._get_sysconfigdata_name()] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-wcwidth.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-wcwidth.py new file mode 100644 index 0000000000000000000000000000000000000000..0eb1b468df81bbaa2c12dfc23dbb8cda08794b87 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-wcwidth.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2017-2021, 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 PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files('wcwidth') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-win32ctypes.core.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-win32ctypes.core.py new file mode 100644 index 0000000000000000000000000000000000000000..9bec8aced6cf5f543821200cca101e3305c37c1f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-win32ctypes.core.py @@ -0,0 +1,22 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2020-2021, 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) +#----------------------------------------------------------------------------- + +# TODO: remove this hook during PyInstaller 4.5 release cycle! + +from PyInstaller.utils.hooks import can_import_module, collect_submodules + +# We need to collect submodules from win32ctypes.core.cffi or +# win32ctypes.core.ctypes for win32ctypes.core to work. The use of +# the backend is determined by availability of cffi. +if can_import_module('cffi'): + hiddenimports = collect_submodules('win32ctypes.core.cffi') +else: + hiddenimports = collect_submodules('win32ctypes.core.ctypes') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.dom.domreg.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.dom.domreg.py new file mode 100644 index 0000000000000000000000000000000000000000..da8e798cdf0170a839ee09d596fec96e6f4ec1dd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.dom.domreg.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# xml.dom.domreg line 54 +hiddenimports = ['xml.dom.minidom'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.etree.cElementTree.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.etree.cElementTree.py new file mode 100644 index 0000000000000000000000000000000000000000..95943242b9fe9e0167b198534fcd420a204a8460 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.etree.cElementTree.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# cElementTree has a hidden import (Python >=2.5 stdlib version) +hiddenimports = ['xml.etree.ElementTree'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.py new file mode 100644 index 0000000000000000000000000000000000000000..503850b429c16da08bc8daabf54c7a6bbee67a10 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-xml.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +hiddenimports = ['xml.sax.xmlreader','xml.sax.expatreader'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-zope.interface.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-zope.interface.py new file mode 100644 index 0000000000000000000000000000000000000000..fb5bcb9da19d9597dd908ab328c257fbb292ca78 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/hook-zope.interface.py @@ -0,0 +1,12 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +excludedimports = ["unittest"] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-PyQt5.uic.port_v2.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-PyQt5.uic.port_v2.py new file mode 100644 index 0000000000000000000000000000000000000000..cbf89dd7854a265f0a3e25371f443621db08120f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-PyQt5.uic.port_v2.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys + +def pre_find_module_path(hook_api): + # Forbid imports in the port_v2 directory under Python 3 + # The code wouldn't import and would crash the build process. + hook_api.search_dirs = [] + + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-distutils.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-distutils.py new file mode 100644 index 0000000000000000000000000000000000000000..1804d01084f6b6ba7fc7e70d8d82d12811fc4b53 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-distutils.py @@ -0,0 +1,43 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +# ----------------------------------------------------------------------------- + +""" +`distutils`-specific pre-find module path hook. + +When run from within a venv (virtual environment), this hook changes the +`__path__` of the `distutils` package to that of the system-wide rather than +venv-specific `distutils` package. While the former is suitable for freezing, +the latter is intended for use _only_ from within venvs. +""" + + +import distutils +import os + +from PyInstaller.utils.hooks import logger + + +def pre_find_module_path(api): + # Absolute path of the system-wide "distutils" package when run from within + # a venv or None otherwise. + + # opcode is not a virtualenv module, so we can use it to find the stdlib. + # Technique taken from virtualenv's "distutils" package detection at + # https://github.com/pypa/virtualenv/blob/16.3.0/virtualenv_embedded/distutils-init.py#L5 + import opcode + + system_module_path = os.path.normpath(os.path.dirname(opcode.__file__)) + loaded_module_path = os.path.normpath(os.path.dirname(distutils.__file__)) + if system_module_path != loaded_module_path: + # Find this package in its parent directory. + api.search_dirs = [system_module_path] + logger.info('distutils: retargeting to non-venv dir %r', + system_module_path) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-site.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-site.py new file mode 100644 index 0000000000000000000000000000000000000000..2f01ae72830ab24ea671b0750eca8e1e82afe7a1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_find_module_path/hook-site.py @@ -0,0 +1,34 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Replace the code of real 'site' module by fake code doing nothing. + +The real 'site' does some magic to find paths to other possible +Python modules. We do not want this behaviour for frozen applications. + +Fake 'site' makes PyInstaller to work with distutils and to work inside +virtualenv environment. +""" + +import os + +from PyInstaller.utils.hooks import logger +from PyInstaller import PACKAGEPATH + +def pre_find_module_path(api): + #FIXME: For reusability, move this into a new + #PyInstaller.configure.get_fake_modules_dir() utility function. + # Absolute path of the faked sub-package. + fake_dir = os.path.join(PACKAGEPATH, 'fake-modules') + + api.search_dirs = [fake_dir] + logger.info('site: retargeting to fake-dir %r', fake_dir) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Atk.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Atk.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Atk.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Champlain.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Champlain.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Champlain.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Clutter.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Clutter.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Clutter.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GIRepository.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GIRepository.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GIRepository.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GLib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GLib.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GLib.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GModule.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GModule.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GModule.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GObject.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GObject.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GObject.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gdk.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gdk.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gdk.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GdkPixbuf.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GdkPixbuf.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GdkPixbuf.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gio.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gio.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gio.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gst.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gst.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gst.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstAudio.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstAudio.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstAudio.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstBase.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstBase.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstBase.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstPbutils.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstPbutils.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstPbutils.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstTag.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstTag.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstTag.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstVideo.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstVideo.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GstVideo.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gtk.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gtk.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Gtk.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkChamplain.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkChamplain.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkChamplain.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkClutter.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkClutter.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkClutter.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkSource.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkosxApplication.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkosxApplication.py new file mode 100644 index 0000000000000000000000000000000000000000..52253189578a331a1b9f8c7383206fafeb4c9254 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.GtkosxApplication.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.HarfBuzz.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.HarfBuzz.py new file mode 100644 index 0000000000000000000000000000000000000000..52253189578a331a1b9f8c7383206fafeb4c9254 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.HarfBuzz.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Pango.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Pango.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.Pango.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.PangoCairo.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.PangoCairo.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.PangoCairo.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.cairo.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.cairo.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.cairo.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.xlib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.xlib.py new file mode 100644 index 0000000000000000000000000000000000000000..fb0d5f623f6f7ad5b1bcaa320e05a27ff056bcb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-gi.repository.xlib.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + # PyGObject modules loaded through the gi repository are marked as + # MissingModules by modulegraph so we convert them to + # RuntimeModules so their hooks are loaded and run. + api.add_runtime_module(api.module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-setuptools.extern.six.moves.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-setuptools.extern.six.moves.py new file mode 100644 index 0000000000000000000000000000000000000000..f4ca731e44c1f3383f96fa1827215137e1a22e74 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-setuptools.extern.six.moves.py @@ -0,0 +1,43 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import eval_statement + +### This basically is a copy of pre_safe_import_module/hook-six.moves.py +### adopted to setuptools.extern.six resp. setuptools._vendor.six. +### Please see pre_safe_import_module/hook-six.moves.py for documentation. + +# Note that the moves are defined in 'setuptools._vendor.six' but are imported +# under 'setuptools.extern.six'. + +def pre_safe_import_module(api): + real_to_six_module_name = eval_statement( +''' +try: + import setuptools._vendor.six as six +except ImportError: + import setuptools.extern.six as six + +print('{') + +for moved in six._moved_attributes: + if isinstance(moved, (six.MovedModule, six.MovedAttribute)): + print(' %r: %r,' % ( + moved.mod, + 'setuptools.extern.six.moves.' + moved.name)) + +print('}') +''') + if isinstance(real_to_six_module_name, str): + raise SystemExit("pre-safe-import-module hook failed, needs fixing.") + api.add_runtime_package(api.module_name) + for real_module_name, six_module_name in real_to_six_module_name.items(): + api.add_alias_module(real_module_name, six_module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-six.moves.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-six.moves.py new file mode 100644 index 0000000000000000000000000000000000000000..90f10d33162e217305b0c69c95940ed7cb33f5a6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-six.moves.py @@ -0,0 +1,74 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import eval_statement + + +def pre_safe_import_module(api): + """ + Add the `six.moves` module as a dynamically defined runtime module node and + all modules mapped by `six._SixMetaPathImporter` as aliased module nodes to + the passed graph. + + The `six.moves` module is dynamically defined at runtime by the `six` module + and hence cannot be imported in the standard way. Instead, this hook adds a + placeholder node for the `six.moves` module to the graph, which implicitly + adds an edge from that node to the node for its parent `six` module. This + ensures that the `six` module will be frozen into the executable. (Phew!) + + `six._SixMetaPathImporter` is a PEP 302-compliant module importer converting + imports independent of the current Python version into imports specific to + that version (e.g., under Python 3, from `from six.moves import tkinter_tix` + to `import tkinter.tix`). For each such mapping, this hook adds a + corresponding module alias to the graph allowing PyInstaller to translate + the former to the latter. + """ + # Dictionary from conventional module names to "six.moves" attribute names + # (e.g., from `tkinter.tix` to `six.moves.tkinter_tix`). + real_to_six_module_name = eval_statement( +''' +import six +print('{') + +# Iterate over the "six._moved_attributes" list rather than the +# "six._importer.known_modules" dictionary, as "urllib"-specific moved modules +# are overwritten in the latter with unhelpful "LazyModule" objects. +for moved_module in six._moved_attributes: + # If this is a moved module or attribute, map the corresponding module. In + # the case of moved attributes, the attribute's module is mapped while the + # attribute itself is mapped at runtime and hence ignored here. + if isinstance(moved_module, (six.MovedModule, six.MovedAttribute)): + print(' %r: %r,' % ( + moved_module.mod, 'six.moves.' + moved_module.name)) + +print('}') +''') + + # Add "six.moves" as a runtime package rather than module. Modules cannot + # physically contain submodules; only packages can. In "from"-style import + # statements (e.g., "from six.moves import queue"), this implies that: + # + # * Attributes imported from customary modules are guaranteed *NOT* to be + # submodules. Hence, ModuleGraph justifiably ignores these attributes. + # While some attributes declared by "six.moves" are ignorable non-modules + # (e.g., functions, classes), others are non-ignorable submodules that + # must be imported. Adding "six.moves" as a runtime module causes + # ModuleGraph to ignore these submodules, which defeats the entire point. + # * Attributes imported from packages could be submodules. To disambiguate + # non-ignorable submodules from ignorable non-submodules (e.g., classes, + # variables), ModuleGraph first attempts to import these attributes as + # submodules. This is exactly what we want. + if isinstance(real_to_six_module_name, str): + raise SystemExit("pre-safe-import-module hook failed, needs fixing.") + api.add_runtime_package(api.module_name) + for real_module_name, six_module_name in real_to_six_module_name.items(): + api.add_alias_module(real_module_name, six_module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-urllib3.packages.six.moves.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-urllib3.packages.six.moves.py new file mode 100644 index 0000000000000000000000000000000000000000..f1de73c953d330d6dcf15c978b28f41e9c8c51fd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/pre_safe_import_module/hook-urllib3.packages.six.moves.py @@ -0,0 +1,36 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import eval_statement + +### This basically is a copy of pre_safe_import_module/hook-six.moves.py +### adopted to urllib3.packages.six. +### Please see pre_safe_import_module/hook-six.moves.py for documentation. + +def pre_safe_import_module(api): + real_to_six_module_name = eval_statement( +''' +import urllib3.packages.six as six +print('{') + +for moved in six._moved_attributes: + if isinstance(moved, (six.MovedModule, six.MovedAttribute)): + print(' %r: %r,' % ( + moved.mod, + 'urllib3.packages.six.moves.' + moved.name)) + +print('}') +''') + if isinstance(real_to_six_module_name, str): + raise SystemExit("pre-safe-import-module hook failed, needs fixing.") + api.add_runtime_package(api.module_name) + for real_module_name, six_module_name in real_to_six_module_name.items(): + api.add_alias_module(real_module_name, six_module_name) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks.dat b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks.dat new file mode 100644 index 0000000000000000000000000000000000000000..b43ff9b3a7f8f99d2682713cf3102c2b104e89c4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks.dat @@ -0,0 +1,22 @@ +{ + 'django': ['pyi_rth_django.py'], + 'gi': ['pyi_rth_gi.py'], + 'gi.repository.Gio': ['pyi_rth_gio.py'], + 'gi.repository.GLib': ['pyi_rth_glib.py'], + 'gi.repository.GdkPixbuf': ['pyi_rth_gdkpixbuf.py'], + 'gi.repository.Gtk': ['pyi_rth_gtk.py'], + 'gi.repository.Gst': ['pyi_rth_gstreamer.py'], + 'gst': ['pyi_rth_gstreamer.py'], + 'kivy': ['pyi_rth_kivy.py'], + 'kivy.lib.gstplayer': ['pyi_rth_gstreamer.py'], + 'matplotlib': ['pyi_rth_mplconfig.py'], + 'pkg_resources': ['pyi_rth_pkgres.py'], + 'PyQt5': ['pyi_rth_pyqt5.py'], + 'PyQt5.QtWebEngineWidgets': ['pyi_rth_pyqt5webengine.py'], + 'PySide2': ['pyi_rth_pyside2.py'], + 'PySide2.QtWebEngineWidgets': ['pyi_rth_pyside2webengine.py'], + '_tkinter': ['pyi_rth__tkinter.py'], + 'win32api': ['pyi_rth_win32api.py'], + 'win32com': ['pyi_rth_win32comgenpy.py'], + 'multiprocessing': ['pyi_rth_multiprocessing.py'], +} diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth__tkinter.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth__tkinter.py new file mode 100644 index 0000000000000000000000000000000000000000..2766faee2aba1afac13d74a5b552c8ef1b3fff54 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth__tkinter.py @@ -0,0 +1,33 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + + +import os +import sys + +tcldir = os.path.join(sys._MEIPASS, 'tcl') +tkdir = os.path.join(sys._MEIPASS, 'tk') + +# Notify "tkinter" of data directories. +# On macOS, we do not collect data directories if system Tcl/Tk +# framework is used. On other OSes, we always collect them, so their +# absence is considered an error. +is_darwin = sys.platform == 'darwin' + +if os.path.isdir(tcldir): + os.environ["TCL_LIBRARY"] = tcldir +elif not is_darwin: + raise FileNotFoundError('Tcl data directory "%s" not found.' % (tcldir)) + +if os.path.isdir(tkdir): + os.environ["TK_LIBRARY"] = tkdir +elif not is_darwin: + raise FileNotFoundError('Tk data directory "%s" not found.' % (tkdir)) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_django.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_django.py new file mode 100644 index 0000000000000000000000000000000000000000..cd0a54f9433c91a221d4ed93c98868898bd7b90d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_django.py @@ -0,0 +1,82 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + + +# This Django rthook was tested with Django 1.8.3. + + +import django.core.management +import django.utils.autoreload + + +def _get_commands(): + # Django groupss commands by app. + # This returns static dict() as it is for django 1.8 and the default project. + commands = { + 'changepassword': 'django.contrib.auth', + 'check': 'django.core', + 'clearsessions': 'django.contrib.sessions', + 'collectstatic': 'django.contrib.staticfiles', + 'compilemessages': 'django.core', + 'createcachetable': 'django.core', + 'createsuperuser': 'django.contrib.auth', + 'dbshell': 'django.core', + 'diffsettings': 'django.core', + 'dumpdata': 'django.core', + 'findstatic': 'django.contrib.staticfiles', + 'flush': 'django.core', + 'inspectdb': 'django.core', + 'loaddata': 'django.core', + 'makemessages': 'django.core', + 'makemigrations': 'django.core', + 'migrate': 'django.core', + 'runfcgi': 'django.core', + 'runserver': 'django.core', + 'shell': 'django.core', + 'showmigrations': 'django.core', + 'sql': 'django.core', + 'sqlall': 'django.core', + 'sqlclear': 'django.core', + 'sqlcustom': 'django.core', + 'sqldropindexes': 'django.core', + 'sqlflush': 'django.core', + 'sqlindexes': 'django.core', + 'sqlmigrate': 'django.core', + 'sqlsequencereset': 'django.core', + 'squashmigrations': 'django.core', + 'startapp': 'django.core', + 'startproject': 'django.core', + 'syncdb': 'django.core', + 'test': 'django.core', + 'testserver': 'django.core', + 'validate': 'django.core' + } + return commands + + +_old_restart_with_reloader = django.utils.autoreload.restart_with_reloader + + +def _restart_with_reloader(*args): + import sys + a0 = sys.argv.pop(0) + try: + return _old_restart_with_reloader(*args) + finally: + sys.argv.insert(0, a0) + + +# Override get_commands() function otherwise the app will complain that +# there are no commands. +django.core.management.get_commands = _get_commands +# Override restart_with_reloader() function otherwise the app might +# complain that some commands do not exist. e.g. runserver. +django.utils.autoreload.restart_with_reloader = _restart_with_reloader diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gdkpixbuf.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gdkpixbuf.py new file mode 100644 index 0000000000000000000000000000000000000000..7357aa8f4b1e97ae59d66c45dc4ab0bcd107a271 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gdkpixbuf.py @@ -0,0 +1,39 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import atexit +import os +import tempfile +import sys + +pixbuf_file = os.path.join(sys._MEIPASS, 'lib', 'gdk-pixbuf', 'loaders.cache') + +# If we're not on Windows we need to rewrite the cache +# -> we rewrite on OSX to support --onefile mode +if os.path.exists(pixbuf_file) and sys.platform != 'win32': + + with open(pixbuf_file, 'rb') as fp: + contents = fp.read() + + # create a temporary file with the cache and cleverly replace the prefix + # we injected with the actual path + fd, pixbuf_file = tempfile.mkstemp() + with os.fdopen(fd, 'wb') as fp: + libpath = os.path.join(sys._MEIPASS, 'lib').encode('utf-8') + fp.write(contents.replace(b'@executable_path/lib', libpath)) + + try: + atexit.register(os.unlink, pixbuf_file) + except OSError: + pass + + +os.environ['GDK_PIXBUF_MODULE_FILE'] = pixbuf_file diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gi.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gi.py new file mode 100644 index 0000000000000000000000000000000000000000..c03160007dcd484139c11f92cc5538c37af5f52f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gi.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +os.environ['GI_TYPELIB_PATH'] = os.path.join(sys._MEIPASS, 'gi_typelibs') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gio.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gio.py new file mode 100644 index 0000000000000000000000000000000000000000..da15d9d44b38e8f578e21a9da4235800493ebd47 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gio.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +os.environ['GIO_MODULE_DIR'] = os.path.join(sys._MEIPASS, 'gio_modules') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_glib.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_glib.py new file mode 100644 index 0000000000000000000000000000000000000000..52e9c204b76d931b7b9c07d9af6392980598a205 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_glib.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +os.environ['XDG_DATA_DIRS'] = os.path.join(sys._MEIPASS, 'share') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gstreamer.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gstreamer.py new file mode 100644 index 0000000000000000000000000000000000000000..c65da41ea64857dd614885067604779f55678d0d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gstreamer.py @@ -0,0 +1,29 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + + +import os +import sys + + +# Without this environment variable set to 'no' importing 'gst' +# causes 100% CPU load. (Tested on OSX.) +os.environ['GST_REGISTRY_FORK'] = 'no' + +gst_plugin_paths = [sys._MEIPASS, os.path.join(sys._MEIPASS, 'gst-plugins')] +os.environ['GST_PLUGIN_PATH'] = os.pathsep.join(gst_plugin_paths) + +# Prevent permission issues on Windows +os.environ['GST_REGISTRY'] = os.path.join(sys._MEIPASS, 'registry.bin') + +# Only use packaged plugins to prevent GStreamer from crashing when it finds +# plugins from another version which are installed system wide. +os.environ['GST_PLUGIN_SYSTEM_PATH'] = '' diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gtk.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gtk.py new file mode 100644 index 0000000000000000000000000000000000000000..16a13a52a820be592d3e9da6314831cb5dc6524c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_gtk.py @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +os.environ['GTK_DATA_PREFIX'] = sys._MEIPASS +os.environ['GTK_EXE_PREFIX'] = sys._MEIPASS +os.environ['GTK_PATH'] = sys._MEIPASS + +# Include these here, as GTK will import pango automatically +os.environ['PANGO_LIBDIR'] = sys._MEIPASS +os.environ['PANGO_SYSCONFDIR'] = os.path.join(sys._MEIPASS, 'etc') # TODO? diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_kivy.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_kivy.py new file mode 100644 index 0000000000000000000000000000000000000000..cf8bb879e9b89dc28a79896e033689e5fd41b2f2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_kivy.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +root = os.path.join(sys._MEIPASS, 'kivy_install') + +os.environ['KIVY_DATA_DIR'] = os.path.join(root, 'data') +os.environ['KIVY_MODULES_DIR'] = os.path.join(root, 'modules') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_mplconfig.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_mplconfig.py new file mode 100644 index 0000000000000000000000000000000000000000..a8c0c4fee5e02995389fd35b98a0799431def43e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_mplconfig.py @@ -0,0 +1,46 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + + +# matplotlib will create $HOME/.matplotlib folder in user's home directory. +# In this directory there is fontList.cache file which lists paths +# to matplotlib fonts. +# +# When you run your onefile exe for the first time it's extracted to for example +# "_MEIxxxxx" temp directory and fontList.cache file is created with fonts paths +# pointing to this directory. +# +# Second time you run your exe new directory is created "_MEIyyyyy" but +# fontList.cache file still points to previous directory which was deleted. +# And then you will get error like: +# +# RuntimeError: Could not open facefile +# +# We need to force matplotlib to recreate config directory every time you run +# your app. + + +import atexit +import os +import shutil +import tempfile + + +# Put matplot config dir to temp directory. +configdir = tempfile.mkdtemp() +os.environ['MPLCONFIGDIR'] = configdir + + +try: + # Remove temp directory at application exit and ignore any errors. + atexit.register(shutil.rmtree, configdir, ignore_errors=True) +except OSError: + pass diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_multiprocessing.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_multiprocessing.py new file mode 100644 index 0000000000000000000000000000000000000000..f6310641978f5c0d032fcdc04a8d4df2cd962a32 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_multiprocessing.py @@ -0,0 +1,105 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2017-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import sys + +# 'spawn' multiprocessing needs some adjustments on osx +import os +import re +import multiprocessing +import multiprocessing.spawn as spawn +from subprocess import _args_from_interpreter_flags + +# prevent spawn from trying to read __main__ in from the main script +multiprocessing.process.ORIGINAL_DIR = None + +def _freeze_support(): + # we want to catch the two processes that are spawned by the + # multiprocessing code: + # - the semaphore tracker, which cleans up named semaphores in + # the spawn multiprocessing mode + # - the fork server, which keeps track of worker processes in + # forkserver mode. + # both of these processes are started by spawning a new copy of the + # running executable, passing it the flags from + # _args_from_interpreter_flags and then "-c" and an import statement. + # look for those flags and the import statement, then exec() the + # code ourselves. + + if (len(sys.argv) >= 2 and + sys.argv[-2] == '-c' and + sys.argv[-1].startswith( + ('from multiprocessing.semaphore_tracker import main', # Py<3.8 + 'from multiprocessing.resource_tracker import main', # Py>=3.8 + 'from multiprocessing.forkserver import main')) and + set(sys.argv[1:-2]) == set(_args_from_interpreter_flags())): + exec(sys.argv[-1]) + sys.exit() + + if spawn.is_forking(sys.argv): + kwds = {} + for arg in sys.argv[2:]: + name, value = arg.split('=') + if value == 'None': + kwds[name] = None + else: + kwds[name] = int(value) + spawn.spawn_main(**kwds) + sys.exit() + +multiprocessing.freeze_support = spawn.freeze_support = _freeze_support + +# Bootloader unsets _MEIPASS2 for child processes to allow running +# PyInstaller binaries inside pyinstaller binaries. +# This is ok for mac or unix with fork() system call. +# But on Windows we need to overcome missing fork() function. + +if sys.platform.startswith('win'): + import multiprocessing.popen_spawn_win32 as forking +else: + import multiprocessing.popen_fork as forking + import multiprocessing.popen_spawn_posix as spawning + + + +# Mix-in to re-set _MEIPASS2 from sys._MEIPASS. +class FrozenSupportMixIn(): + def __init__(self, *args, **kw): + if hasattr(sys, 'frozen'): + # We have to set original _MEIPASS2 value from sys._MEIPASS + # to get --onefile mode working. + os.putenv('_MEIPASS2', sys._MEIPASS) # @UndefinedVariable + try: + super().__init__(*args, **kw) + finally: + if hasattr(sys, 'frozen'): + # On some platforms (e.g. AIX) 'os.unsetenv()' is not + # available. In those cases we cannot delete the variable + # but only set it to the empty string. The bootloader + # can handle this case. + if hasattr(os, 'unsetenv'): + os.unsetenv('_MEIPASS2') + else: + os.putenv('_MEIPASS2', '') + + +# Patch forking.Popen to re-set _MEIPASS2 from sys._MEIPASS. +class _Popen(FrozenSupportMixIn, forking.Popen): + pass + +forking.Popen = _Popen + +if not sys.platform.startswith('win'): + # Patch spawning.Popen to re-set _MEIPASS2 from sys._MEIPASS. + class _Spawning_Popen(FrozenSupportMixIn, spawning.Popen): + pass + + spawning.Popen = _Spawning_Popen diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pkgres.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pkgres.py new file mode 100644 index 0000000000000000000000000000000000000000..f47865de18f22854c0ce8aae6940fe670aecb6f5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pkgres.py @@ -0,0 +1,216 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys +import pathlib + +import pkg_resources +from pyimod03_importers import FrozenImporter + +SYS_PREFIX = pathlib.PurePath(sys._MEIPASS) + + +# To make pkg_resources work with frozen modules we need to set the 'Provider' +# class for FrozenImporter. This class decides where to look for resources +# and other stuff. 'pkg_resources.NullProvider' is dedicated to PEP302 +# import hooks like FrozenImporter is. It uses method __loader__.get_data() in +# methods pkg_resources.resource_string() and pkg_resources.resource_stream() +# +# We provide PyiFrozenProvider, which subclasses the NullProvider and +# implements _has(), _isdir(), and _listdir() methods, which are needed +# for pkg_resources.resource_exists(), resource_isdir(), and resource_listdir() +# to work. We cannot use the DefaultProvider, because it provides +# filesystem-only implementations (and overrides _get() with a filesystem-only +# one), whereas our provider needs to also support embedded resources. +# +# The PyiFrozenProvider allows querying/listing both PYZ-embedded and +# on-filesystem resources in a frozen package. The results are typically +# combined for both types of resources (e.g., when listing a directory +# or checking whether a resource exists). When the order of precedence +# matters, the PYZ-embedded resources take precedence over the on-filesystem +# ones, to keep the behavior consistent with the actual file content +# retrieval via _get() method (which in turn uses FrozenImporter's get_data() +# method). For example, when checking whether a resource is a directory +# via _isdir(), a PYZ-embedded file will take precedence over a potential +# on-filesystem directory. Also, in contrast to unfrozen packages, the frozen +# ones do not contain source .py files, which are therefore absent from +# content listings. + + +class _TocFilesystem: + """A prefix tree implementation for embedded filesystem reconstruction.""" + + def __init__(self, toc_files, toc_dirs=[]): + # Reconstruct the fileystem hierarchy by building a prefix tree from + # the given file and directory paths + self._tree = dict() + + # Data files + for path in toc_files: + path = pathlib.PurePath(path) + current = self._tree + for component in path.parts[:-1]: + current = current.setdefault(component, {}) + current[path.parts[-1]] = '' + + # Extra directories + for path in toc_dirs: + path = pathlib.PurePath(path) + current = self._tree + for component in path.parts: + current = current.setdefault(component, {}) + + def _get_tree_node(self, path): + path = pathlib.PurePath(path) + current = self._tree + for component in path.parts: + if component not in current: + return None + current = current[component] + return current + + def path_exists(self, path): + node = self._get_tree_node(path) + return node is not None # File or directory + + def path_isdir(self, path): + node = self._get_tree_node(path) + if node is None: + return False # Non-existant + if isinstance(node, str): + return False # File + return True + + def path_listdir(self, path): + node = self._get_tree_node(path) + if not isinstance(node, dict): + return [] # Non-existant or file + return list(node.keys()) + + +# Cache for reconstructed embedded trees +_toc_tree_cache = {} + + +class PyiFrozenProvider(pkg_resources.NullProvider): + """Custom pkg_resourvces provider for FrozenImporter.""" + + def __init__(self, module): + super().__init__(module) + + # Get top-level path; if "module" corresponds to a package, + # we need the path to the package itself. If "module" is a + # submodule in a package, we need the path to the parent + # package. + self._pkg_path = pathlib.PurePath(module.__file__).parent + + # Defer initialization of PYZ-embedded resources tree to the + # first access + self._embedded_tree = None + + def _init_embedded_tree(self, rel_pkg_path, pkg_name): + # Collect relevant entries from TOC. We are interested in either + # files that are located in the package/module's directory (data + # files) or in packages that are prefixed with package/module's + # name (to reconstruct subpackage directories) + data_files = [] + package_dirs = [] + for entry in self.loader.toc: + entry_path = pathlib.PurePath(entry) + if rel_pkg_path in entry_path.parents: + # Data file path + data_files.append(entry_path) + elif entry.startswith(pkg_name) and self.loader.is_package(entry): + # Package or subpackage; convert the name to directory path + package_dir = pathlib.PurePath(*entry.split('.')) + package_dirs.append(package_dir) + + # Reconstruct the filesystem + return _TocFilesystem(data_files, package_dirs) + + @property + def embedded_tree(self): + if self._embedded_tree is None: + # Construct a path relative to _MEIPASS directory for + # searching the TOC + rel_pkg_path = self._pkg_path.relative_to(SYS_PREFIX) + + # Reconstruct package name prefix (use package path to + # obtain correct prefix in case of a module) + pkg_name = '.'.join(rel_pkg_path.parts) + + # Initialize and cache the tree, if necessary + if pkg_name not in _toc_tree_cache: + _toc_tree_cache[pkg_name] = \ + self._init_embedded_tree(rel_pkg_path, pkg_name) + self._embedded_tree = _toc_tree_cache[pkg_name] + return self._embedded_tree + + def _normalize_path(self, path): + # Avoid using Path.resolve(), because it resolves symlinks. This + # is undesirable, because the pure path in self._pkg_path does + # not have symlinks resolved, so comparison between the two + # would be faulty. So use os.path.abspath() instead to normalize + # the path + return pathlib.Path(os.path.abspath(path)) + + def _is_relative_to_package(self, path): + return path == self._pkg_path or self._pkg_path in path.parents + + def _has(self, path): + # Prevent access outside the package + path = self._normalize_path(path) + if not self._is_relative_to_package(path): + return False + + # Check the filesystem first to avoid unnecessarily computing + # the relative path... + if path.exists(): + return True + rel_path = path.relative_to(SYS_PREFIX) + return self.embedded_tree.path_exists(rel_path) + + def _isdir(self, path): + # Prevent access outside the package + path = self._normalize_path(path) + if not self._is_relative_to_package(path): + return False + + # Embedded resources have precedence over filesystem... + rel_path = path.relative_to(SYS_PREFIX) + node = self.embedded_tree._get_tree_node(rel_path) + if node is None: + return path.is_dir() # No match found; try the filesystem + else: + # str = file, dict = directory + return not isinstance(node, str) + + def _listdir(self, path): + # Prevent access outside the package + path = self._normalize_path(path) + if not self._is_relative_to_package(path): + return [] + + # Relative path for searching embedded resources + rel_path = path.relative_to(SYS_PREFIX) + # List content from embedded filesystem... + content = self.embedded_tree.path_listdir(rel_path) + # ... as well as the actual one + if path.is_dir(): + # Use os.listdir() to avoid having to convert Path objects + # to strings... Also make sure to de-duplicate the results + path = str(path) # not is_py36 + content = list(set(content + os.listdir(path))) + return content + + +pkg_resources.register_loader_type(FrozenImporter, PyiFrozenProvider) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyqt5.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyqt5.py new file mode 100644 index 0000000000000000000000000000000000000000..037a9214b471c02ab511b77def7312e0d54ebc0d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyqt5.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +# The path to Qt's components may not default to the wheel layout for +# self-compiled PyQt5 installations. Mandate the wheel layout. See +# ``utils/hooks/qt.py`` for more details. +pyqt_path = os.path.join(sys._MEIPASS, 'PyQt5', 'Qt') +os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins') +os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml') +# This is required starting in PyQt5 5.12.3. See discussion in #4293. +if 'PATH' in os.environ: + os.environ['PATH'] = sys._MEIPASS + os.pathsep + os.environ['PATH'] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyqt5webengine.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyqt5webengine.py new file mode 100644 index 0000000000000000000000000000000000000000..67f09adec48cd3df153e50e0b69a676ca86405f5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyqt5webengine.py @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +# See ``pyi_rth_qt5.py`: use a "standard" PyQt5 layout. +if sys.platform == 'darwin': + os.environ['QTWEBENGINEPROCESS_PATH'] = os.path.normpath(os.path.join( + sys._MEIPASS, 'PyQt5', 'Qt', 'lib', + 'QtWebEngineCore.framework', 'Helpers', 'QtWebEngineProcess.app', + 'Contents', 'MacOS', 'QtWebEngineProcess' + )) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyside2.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyside2.py new file mode 100644 index 0000000000000000000000000000000000000000..8d0ee97f63c9a1aeb947d52761dadd7100ca6489 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyside2.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +# The path to Qt's components may not default to the wheel layout for +# self-compiled PySide2 installations. Mandate the wheel layout. See +# ``utils/hooks/qt.py`` for more details. +pyqt_path = os.path.join(sys._MEIPASS, 'PySide2') +os.environ['QT_PLUGIN_PATH'] = os.path.join(pyqt_path, 'plugins') +os.environ['QML2_IMPORT_PATH'] = os.path.join(pyqt_path, 'qml') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyside2webengine.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyside2webengine.py new file mode 100644 index 0000000000000000000000000000000000000000..f5496d65938a812ec0cc6992718f733d63f12751 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_pyside2webengine.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +import os +import sys + +if sys.platform == 'darwin': + os.environ['QTWEBENGINEPROCESS_PATH'] = os.path.normpath(os.path.join( + sys._MEIPASS, 'PySide2', 'Qt', 'lib', + 'QtWebEngineCore.framework', 'Helpers', 'QtWebEngineProcess.app', + 'Contents', 'MacOS', 'QtWebEngineProcess' + )) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_win32api.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_win32api.py new file mode 100644 index 0000000000000000000000000000000000000000..012ab0479358b965e74e1d2757e47b4654cdb2fb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_win32api.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + +# Starting with Python 3.8, win32api failed with "ImportError: DLL load failed +# while importing win32clipboard: The specified module could not be found." +# This seems to be caused by pywintypes.dll not being found in various +# situations. +# See https://github.com/mhammond/pywin32/pull/1430 and +# https://github.com/mhammond/pywin32/commit/71afa71e11e6631be611ca5cb57cda526 +# As a work-around, import pywintypes prior to win32api. + +import pywintypes diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_win32comgenpy.py b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_win32comgenpy.py new file mode 100644 index 0000000000000000000000000000000000000000..4af01b6a93167f588a32d1f50d270e78b88eea0e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/hooks/rthooks/pyi_rth_win32comgenpy.py @@ -0,0 +1,62 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# +# The full license is in the file COPYING.txt, distributed with this software. +# +# SPDX-License-Identifier: Apache-2.0 +#----------------------------------------------------------------------------- + + +# The win32.client.gencache code must be allowed to create the cache in %temp% +# (user's temp). It is necessary to get the gencache code to use a suitable +# directory other than the default in lib\site-packages\win32com\client\gen_py. +# PyInstaller does not provide this directory structure and the frozen +# executable could be placed in a non-writable directory like 'C:\Program Files. +# That's the reason for %temp% directory. +# +# http://www.py2exe.org/index.cgi/UsingEnsureDispatch + + +import atexit +import os +import shutil +import tempfile + + +# Put gen_py cache in temp directory. +supportdir = tempfile.mkdtemp() +# gen_py has to be put into directory 'gen_py'. +genpydir = os.path.join(supportdir, 'gen_py') + + +# Create 'gen_py' directory. This directory does not need +# to contain '__init__.py' file. +try: + # win32com gencache cannot be put directly to 'supportdir' with any + # random name. It has to be put in a directory called 'gen_py'. + # This is the reason why to create this directory in supportdir'. + os.makedirs(genpydir) + # Remove temp directory at application exit and ignore any errors. + atexit.register(shutil.rmtree, supportdir, ignore_errors=True) +except OSError: + pass + + +# Override the default path to gen_py cache. +import win32com +win32com.__gen_path__ = genpydir + + +# The attribute __loader__ makes module 'pkg_resources' working but On Windows +# it breaks pywin32 (win32com) and test 'basic/test_pyttsx' will fail. Just +# removing that attribute for win32com fixes that and gencache is created properly. +if hasattr(win32com, '__loader__'): + del win32com.__loader__ + + +# Ensure genpydir is in 'gen_py' module paths. +import win32com.gen_py +win32com.gen_py.__path__.insert(0, genpydir) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/README.rst b/3rdparty/pyinstaller-4.3/PyInstaller/lib/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..1f0889797f6f24f4a8de362d319b429105320b59 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/README.rst @@ -0,0 +1,49 @@ +Custom modifications of 3rd party libraries +=========================================== + +NOTE: PyInstaller does not extend PYTHONPATH (sys.path) with this directory +that contains bundled 3rd party libraries. + +Some users complained that PyInstaller failed because their apps were using +too old versions of some libraries that PyInstaller uses too and that's why +extending sys.path was dropped. + +All libraries are tweaked to be importable as:: + + from PyInstaller.lib.LIB_NAME import xyz + +In libraries replace imports like:: + + from altgraph import y + from modulegraph import z + +with relative prefix:: + + from ..altgraph import y + from ..modulegraph import z + + +altgraph +---------- + +- add fixed version string to ./altgraph/__init__.py:: + + # For PyInstaller/lib/ define the version here, since there is no + # package-resource. + __version__ = '0.13' + + +modulegraph +----------- + +https://bitbucket.org/ronaldoussoren/modulegraph/downloads + +- TODO Use official modulegraph version when following issue is resolved and pull request merged + https://bitbucket.org/ronaldoussoren/modulegraph/issues/28/__main__-module-being-analyzed-for-wheel + +- add fixed version string to ./modulegraph/__init__.py:: + + # For PyInstaller/lib/ define the version here, since there is no + # package-resource. + __version__ = '0.13' + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5910c51e6594dc1be5a9bf4a8b4ce9e7bb91e0c3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/__init__.py @@ -0,0 +1 @@ +__version__ = '0.17' diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/__main__.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..7fe6f5019fdd71efc4b561e248e83f3002ea7271 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/__main__.py @@ -0,0 +1,89 @@ +import sys +import os +import argparse +from .modulegraph import ModuleGraph + + +def parse_arguments(): + parser = argparse.ArgumentParser( + conflict_handler='resolve', prog='%s -mmodulegraph' % ( + os.path.basename(sys.executable))) + parser.add_argument( + '-d', action='count', dest='debug', default=1, + help='Increase debug level') + parser.add_argument( + '-q', action='store_const', dest='debug', const=0, + help='Clear debug level') + parser.add_argument( + '-m', '--modules', action='store_true', + dest='domods', default=False, + help='arguments are module names, not script files') + parser.add_argument( + '-x', metavar='NAME', action='append', dest='excludes', + default=[], help='Add NAME to the excludes list') + parser.add_argument( + '-p', action='append', metavar='PATH', dest='addpath', default=[], + help='Add PATH to the module search path') + parser.add_argument( + '-g', '--dot', action='store_const', dest='output', const='dot', + help='Output a .dot graph') + parser.add_argument( + '-h', '--html', action='store_const', + dest='output', const='html', help='Output a HTML file') + parser.add_argument( + 'scripts', metavar='SCRIPT', nargs='+', help='scripts to analyse') + + opts = parser.parse_args() + return opts + + +def create_graph(scripts, domods, debuglevel, excludes, path_extras): + # Set the path based on sys.path and the script directory + path = sys.path[:] + + if domods: + del path[0] + else: + path[0] = os.path.dirname(scripts[0]) + + path = path_extras + path + if debuglevel > 1: + print("path:", file=sys.stderr) + for item in path: + print(" ", repr(item), file=sys.stderr) + + # Create the module finder and turn its crank + mf = ModuleGraph(path, excludes=excludes, debug=debuglevel) + for arg in scripts: + if domods: + if arg[-2:] == '.*': + mf.import_hook(arg[:-2], None, ["*"]) + else: + mf.import_hook(arg) + else: + mf.run_script(arg) + return mf + + +def output_graph(output_format, mf): + if output_format == 'dot': + mf.graphreport() + elif output_format == 'html': + mf.create_xref() + else: + mf.report() + + +def main(): + opts = parse_arguments() + mf = create_graph( + opts.scripts, opts.domods, opts.debug, + opts.excludes, opts.addpath) + output_graph(opts.output, mf) + + +if __name__ == '__main__': # pragma: no cover + try: + main() + except KeyboardInterrupt: + print("\n[interrupt]") diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/_compat.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/_compat.py new file mode 100644 index 0000000000000000000000000000000000000000..90447ef848281c7dff1ae8b91378e8165de3a5bc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/_compat.py @@ -0,0 +1,25 @@ +import sys + +if sys.version_info[0] == 2: + PY2 = True + + from StringIO import StringIO + BytesIO = StringIO + from urllib import pathname2url + _cOrd = ord + + # File open mode for reading (univeral newlines) + _READ_MODE = "rU" + +else: + PY2 = False + + from urllib.request import pathname2url + from io import BytesIO, StringIO + _cOrd = int + _READ_MODE = "r" + +if sys.version_info < (3,): + from dis3 import get_instructions +else: + from dis import get_instructions diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/find_modules.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/find_modules.py new file mode 100644 index 0000000000000000000000000000000000000000..981ac1acd7218f66e72eb77a5aed9f2abbdb9816 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/find_modules.py @@ -0,0 +1,338 @@ +""" +modulegraph.find_modules - High-level module dependency finding interface +========================================================================= + +History +........ + +Originally (loosely) based on code in py2exe's build_exe.py by Thomas Heller. +""" +import sys +import os +import imp +import warnings +import pkgutil + +from . import modulegraph +from .modulegraph import Alias, Script, Extension +from .util import imp_find_module + +__all__ = [ + 'find_modules', 'parse_mf_results' +] + +_PLATFORM_MODULES = {'posix', 'nt', 'os2', 'mac', 'ce', 'riscos'} + + +def get_implies(): + result = { + # imports done from builtin modules in C code + # (untrackable by modulegraph) + "_curses": ["curses"], + "posix": ["resource"], + "gc": ["time"], + "time": ["_strptime"], + "datetime": ["time"], + "MacOS": ["macresource"], + "cPickle": ["copy_reg", "cStringIO"], + "parser": ["copy_reg"], + "codecs": ["encodings"], + "cStringIO": ["copy_reg"], + "_sre": ["copy", "string", "sre"], + "zipimport": ["zlib"], + + # Python 3.2: + "_datetime": ["time", "_strptime"], + "_json": ["json.decoder"], + "_pickle": ["codecs", "copyreg", "_compat_pickle"], + "_posixsubprocess": ["gc"], + "_ssl": ["socket"], + + # Python 3.3: + "_elementtree": ["copy", "xml.etree.ElementPath"], + + # mactoolboxglue can do a bunch more of these + # that are far harder to predict, these should be tracked + # manually for now. + + # this isn't C, but it uses __import__ + "anydbm": ["dbhash", "gdbm", "dbm", "dumbdbm", "whichdb"], + # package aliases + "wxPython.wx": Alias('wx'), + + } + + if sys.version_info[0] == 3: + result["_sre"] = ["copy", "re"] + result["parser"] = ["copyreg"] + + # _frozen_importlib is part of the interpreter itself + result["_frozen_importlib"] = None + + if sys.version_info[0] == 2 and sys.version_info[1] >= 5: + result.update({ + "email.base64MIME": Alias("email.base64mime"), + "email.Charset": Alias("email.charset"), + "email.Encoders": Alias("email.encoders"), + "email.Errors": Alias("email.errors"), + "email.Feedparser": Alias("email.feedParser"), + "email.Generator": Alias("email.generator"), + "email.Header": Alias("email.header"), + "email.Iterators": Alias("email.iterators"), + "email.Message": Alias("email.message"), + "email.Parser": Alias("email.parser"), + "email.quopriMIME": Alias("email.quoprimime"), + "email.Utils": Alias("email.utils"), + "email.MIMEAudio": Alias("email.mime.audio"), + "email.MIMEBase": Alias("email.mime.base"), + "email.MIMEImage": Alias("email.mime.image"), + "email.MIMEMessage": Alias("email.mime.message"), + "email.MIMEMultipart": Alias("email.mime.multipart"), + "email.MIMENonMultipart": Alias("email.mime.nonmultipart"), + "email.MIMEText": Alias("email.mime.text"), + }) + + if sys.version_info[:2] >= (2, 5): + result["_elementtree"] = ["pyexpat"] + + import xml.etree + for _, module_name, is_package in pkgutil.iter_modules(xml.etree.__path__): + if not is_package: + result["_elementtree"].append("xml.etree.%s" % (module_name,)) + + if sys.version_info[:2] >= (2, 6): + result['future_builtins'] = ['itertools'] + + # os.path is an alias for a platform specific submodule, + # ensure that the graph shows this. + result['os.path'] = Alias(os.path.__name__) + + return result + + +def parse_mf_results(mf): + """ + Return two lists: the first one contains the python files in the graph, + the second the C extensions. + + :param mf: a :class:`modulegraph.modulegraph.ModuleGraph` instance + """ + # Retrieve modules from modulegraph + py_files = [] + extensions = [] + + for item in mf.flatten(): + # There may be __main__ modules (from mf.run_script), but + # we don't need it in the zipfile we build. + if item.identifier == "__main__": + continue + src = item.filename + if src and src != '-': + if isinstance(item, Script): + # Scripts are python files + py_files.append(item) + + elif isinstance(item, Extension): + extensions.append(item) + + else: + py_files.append(item) + + # sort on the file names, the output is nicer to read + py_files.sort(key=lambda v: v.filename) + extensions.sort(key=lambda v: v.filename) + return py_files, extensions + + +def plat_prepare(includes, packages, excludes): + # used by Python itself + includes.update(["warnings", "unicodedata", "weakref"]) + + if not sys.platform.startswith('irix'): + excludes.update([ + 'AL', + 'sgi', + 'vms_lib', + ]) + + if sys.platform not in ('mac', 'darwin'): + # XXX - this doesn't look nearly complete + excludes.update([ + 'Audio_mac', + 'Carbon.File', + 'Carbon.Folder', + 'Carbon.Folders', + 'EasyDialogs', + 'MacOS', + 'macfs', + 'macostools', + '_scproxy', + ]) + + if not sys.platform == 'win32': + # only win32 + excludes.update([ + 'nturl2path', + 'win32api', + 'win32con', + 'win32ctypes', + 'win32event', + 'win32evtlogutil', + 'win32evtlog', + 'win32file', + 'win32gui', + 'win32pipe', + 'win32process', + 'win32security', + 'pywintypes', + 'winsound', + 'win32', + '_winreg', + '_winapi', + 'msvcrt', + 'winreg', + '_subprocess', + ]) + + if not sys.platform == 'riscos': + excludes.update([ + 'riscosenviron', + 'rourl2path', + ]) + + if not sys.platform == 'dos' or sys.platform.startswith('ms-dos'): + excludes.update([ + 'dos', + ]) + + if not sys.platform == 'os2emx': + excludes.update([ + '_emx_link', + ]) + + excludes.update(_PLATFORM_MODULES - set(sys.builtin_module_names)) + + # Carbon.Res depends on this, but the module hasn't been present + # for a while... + excludes.add('OverrideFrom23') + excludes.add('OverrideFrom23._Res') + + # import trickery in the dummy_threading module (stdlib) + excludes.add('_dummy_threading') + + try: + imp_find_module('poll') + except ImportError: + excludes.update([ + 'poll', + ]) + + +def find_needed_modules( + mf=None, scripts=(), includes=(), packages=(), warn=warnings.warn): + if mf is None: + mf = modulegraph.ModuleGraph() + # feed Modulefinder with everything, and return it. + + for path in scripts: + mf.run_script(path) + + for mod in includes: + try: + if mod[-2:] == '.*': + mf.import_hook(mod[:-2], None, ['*']) + else: + mf.import_hook(mod) + except ImportError: + warn("No module named %s" % (mod,)) + + for f in packages: + # If modulegraph has seen a reference to the package, then + # we prefer to believe that (imp_find_module doesn't seem to locate + # sub-packages) + m = mf.findNode(f) + if m is not None: + path = m.packagepath[0] + else: + # Find path of package + # TODO: use imp_find_module_or_importer + try: + path = imp_find_module(f, mf.path)[1] + except ImportError: + warn("No package named %s" % f) + continue + + # walk the path to find subdirs containing __init__.py files + # scan the results (directory of __init__.py files) + # first trim the path (of the head package), + # then convert directory name in package name, + # finally push into modulegraph. + # FIXME: + # 1) Needs to be adjusted for namespace packages in python 3.3 + # 2) Code is fairly dodgy and needs better tests + for (dirpath, dirnames, filenames) in os.walk(path): + if '__init__.py' in filenames and dirpath.startswith(path): + package = f + '.' + dirpath[len(path)+1:].replace(os.sep, '.') + if package.endswith('.'): + package = package[:-1] + m = mf.import_hook(package, None, ["*"]) + else: + # Exclude subtrees that aren't packages + dirnames[:] = [] + + return mf + +# +# resource constants +# + + +PY_SUFFIXES = ['.py', '.pyw', '.pyo', '.pyc'] +C_SUFFIXES = [ + _triple[0] for _triple in imp.get_suffixes() + if _triple[2] == imp.C_EXTENSION +] + + +# +# side-effects +# + + +def _replacePackages(): + REPLACEPACKAGES = { + '_xmlplus': 'xml', + } + for k, v in REPLACEPACKAGES.items(): + modulegraph.replacePackage(k, v) + + +_replacePackages() + + +def find_modules( + scripts=(), includes=(), packages=(), excludes=(), path=None, debug=0): + """ + High-level interface, takes iterables for: + scripts, includes, packages, excludes + + And returns a :class:`modulegraph.modulegraph.ModuleGraph` instance, + python_files, and extensions + + python_files is a list of pure python dependencies as modulegraph.Module + objects, extensions is a list of platform-specific C extension dependencies + as modulegraph.Module objects + """ + scripts = set(scripts) + includes = set(includes) + packages = set(packages) + excludes = set(excludes) + plat_prepare(includes, packages, excludes) + mf = modulegraph.ModuleGraph( + path=path, + excludes=(excludes - includes), + implies=get_implies(), + debug=debug, + ) + find_needed_modules(mf, scripts, includes, packages) + return mf diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/modulegraph.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/modulegraph.py new file mode 100644 index 0000000000000000000000000000000000000000..430eabe422e9844b8df0c2eb48afc116dcf2a5fb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/modulegraph.py @@ -0,0 +1,3293 @@ +""" +Find modules used by a script, using bytecode analysis. + +Based on the stdlib modulefinder by Thomas Heller and Just van Rossum, +but uses a graph data structure and 2.3 features + +XXX: Verify all calls to _import_hook (and variants) to ensure that +imports are done in the right way. +""" +#FIXME: To decrease the likelihood of ModuleGraph exceeding the recursion limit +#and hence unpredictably raising fatal exceptions, increase the recursion +#limit at PyInstaller startup (i.e., in the +#PyInstaller.building.build_main.build() function). For details, see: +# https://github.com/pyinstaller/pyinstaller/issues/1919#issuecomment-216016176 + +import pkg_resources + +import ast +import codecs +import imp +import marshal +import os +import pkgutil +import sys +import re +from collections import deque, namedtuple +import warnings + +from altgraph.ObjectGraph import ObjectGraph +from altgraph import GraphError + +from . import util +from . import zipio +from ._compat import BytesIO, StringIO, pathname2url, _READ_MODE + + +BOM = codecs.BOM_UTF8.decode('utf-8') + + +class BUILTIN_MODULE: + def is_package(fqname): + return False + + +class NAMESPACE_PACKAGE: + def __init__(self, namespace_dirs): + self.namespace_dirs = namespace_dirs + + def is_package(self, fqname): + return True + + +#FIXME: Leverage this rather than magic numbers below. +ABSOLUTE_OR_RELATIVE_IMPORT_LEVEL = -1 +""" +Constant instructing the builtin `__import__()` function to attempt both +absolute and relative imports. +""" + + +#FIXME: Leverage this rather than magic numbers below. +ABSOLUTE_IMPORT_LEVEL = 0 +""" +Constant instructing the builtin `__import__()` function to attempt only +absolute imports. +""" + + +#FIXME: Leverage this rather than magic numbers below. +DEFAULT_IMPORT_LEVEL = ( + ABSOLUTE_OR_RELATIVE_IMPORT_LEVEL if sys.version_info[0] == 2 else + ABSOLUTE_IMPORT_LEVEL) +""" +Constant instructing the builtin `__import__()` function to attempt the default +import style specific to the active Python interpreter. + +Specifically, under: + +* Python 2, this defaults to attempting both absolute and relative imports. +* Python 3, this defaults to attempting only absolute imports. +""" + +# TODO: Refactor all uses of explicit filetypes in this module *AND* of the +# imp.get_suffixes() function to use this dictionary instead. Unfortunately, +# tests for explicit filetypes (e.g., ".py") are non-portable. Under Windows, +# for example, both the ".py" *AND* ".pyw" filetypes signify valid uncompiled +# Python modules. +# TODO: The imp.get_suffixes() function (in fact, the entire "imp" package) has +# been deprecated as of Python 3.3 by the importlib.machinery.all_suffixes() +# function, which largely performs the same role. Unfortunately, the latter +# function was only introduced with Python 3.3. Since PyInstaller requires +# Python >= 3.3 when running under Python 3, refactor this as follows: +# +# * Under Python 2, continue calling imp.get_suffixes(). +# * Under Python 3, call importlib.machinery.all_suffixes() instead. +_IMPORTABLE_FILETYPE_TO_METADATA = { + filetype: (filetype, open_mode, imp_type) + for filetype, open_mode, imp_type in imp.get_suffixes() +} +# Reverse sort by length so when comparing filenames the longest match first +_IMPORTABLE_FILETYPE_EXTS = sorted(_IMPORTABLE_FILETYPE_TO_METADATA, + key=lambda p: len(p), reverse=True) +""" +Dictionary mapping the filetypes of importable files to the 3-tuple of metadata +describing such files returned by the `imp.get_suffixes()` function whose first +element is that filetype. + +This dictionary simplifies platform-portable importation of importable files, +including: + +* Uncompiled modules suffixed by `.py` (as well as `.pyw` under Windows). +* Compiled modules suffixed by either `.pyc` or `.pyo`. +* C extensions suffixed by the platform-specific shared library filetype (e.g., + `.so` under Linux, `.dll` under Windows). + +The keys of this dictionary are `.`-prefixed filetypes (e.g., `.py`, `.so`) or +`-`-prefixed filetypes (e.g., `-cpython-37m.dll`[1]); +the values of this dictionary are 3-tuples whose: + +1. First element is the same `.` or `-` prefixed filetype. +1. Second element is the mode to be passed to the `open()` built-in to open + files of that filetype under the current platform and Python interpreter + (e.g., `rU` for the `.py` filetype under Python 2, `r` for the same + filetype under Python 3). +1. Third element is a magic number specific to the `imp` module (e.g., + `imp.C_EXTENSION` for filetypes corresponding to C extensions). + +[1] For example of `-cpython-m37.dll` search on + https://packages.msys2.org/package/mingw-w64-x86_64-python3?repo=mingw64 +""" + + + +# Modulegraph does a good job at simulating Python's, but it can not +# handle packagepath modifications packages make at runtime. Therefore there +# is a mechanism whereby you can register extra paths in this map for a +# package, and it will be honored. +# +# Note this is a mapping is lists of paths. +_packagePathMap = {} + +# Prefix used in magic .pth files used by setuptools to create namespace +# packages without an __init__.py file. +# +# The value is a list of such prefixes as the prefix varies with versions of +# setuptools. +_SETUPTOOLS_NAMESPACEPKG_PTHs=( + # setuptools 31.0.0 + ("import sys, types, os;has_mfs = sys.version_info > (3, 5);" + "p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('"), + # distribute 0.6.10 + ("import sys,types,os; p = os.path.join(" + "sys._getframe(1).f_locals['sitedir'], *('"), + # setuptools 0.6c9, distribute 0.6.12 + ("import sys,new,os; p = os.path.join(sys._getframe(" + "1).f_locals['sitedir'], *('"), + # setuptools 28.1.0 + ("import sys, types, os;p = os.path.join(" + "sys._getframe(1).f_locals['sitedir'], *('"), + # setuptools 28.7.0 + ("import sys, types, os;pep420 = sys.version_info > (3, 3);" + "p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('"), +) + + +class InvalidRelativeImportError (ImportError): + pass + + +def _namespace_package_path(fqname, pathnames, path=None): + """ + Return the __path__ for the python package in *fqname*. + + This function uses setuptools metadata to extract information + about namespace packages from installed eggs. + """ + working_set = pkg_resources.WorkingSet(path) + + path = list(pathnames) + + for dist in working_set: + if dist.has_metadata('namespace_packages.txt'): + namespaces = dist.get_metadata( + 'namespace_packages.txt').splitlines() + if fqname in namespaces: + nspath = os.path.join(dist.location, *fqname.split('.')) + if nspath not in path: + path.append(nspath) + + return path + +_strs = re.compile(r'''^\s*["']([A-Za-z0-9_]+)["'],?\s*''') # "<- emacs happy + + +def _eval_str_tuple(value): + """ + Input is the repr of a tuple of strings, output + is that tuple. + + This only works with a tuple where the members are + python identifiers. + """ + if not (value.startswith('(') and value.endswith(')')): + raise ValueError(value) + + orig_value = value + value = value[1:-1] + + result = [] + while value: + m = _strs.match(value) + if m is None: + raise ValueError(orig_value) + + result.append(m.group(1)) + value = value[len(m.group(0)):] + + return tuple(result) + + +def _path_from_importerror(exc, default): + # This is a hack, but sadly enough the necessary information + # isn't available otherwise. + m = re.match(r'^No module named (\S+)$', str(exc)) + if m is not None: + return m.group(1) + + return default + + +def os_listdir(path): + """ + Deprecated name + """ + warnings.warn( + "Use zipio.listdir instead of os_listdir", + DeprecationWarning) + return zipio.listdir(path) + + +def _code_to_file(co): + """ Convert code object to a .pyc pseudo-file """ + if sys.version_info >= (3, 7): + header = imp.get_magic() + (b'\0' * 12) + elif sys.version_info >= (3, 4): + header = imp.get_magic() + (b'\0' * 8) + else: + header = imp.get_magic() + (b'\0' * 4) + return BytesIO(header + marshal.dumps(co)) + + +def moduleInfoForPath(path): + for (ext, readmode, typ) in imp.get_suffixes(): + if path.endswith(ext): + return os.path.basename(path)[:-len(ext)], readmode, typ + return None + + +def AddPackagePath(packagename, path): + warnings.warn( + "Use addPackagePath instead of AddPackagePath", + DeprecationWarning) + addPackagePath(packagename, path) + + +def addPackagePath(packagename, path): + paths = _packagePathMap.get(packagename, []) + paths.append(path) + _packagePathMap[packagename] = paths + + +_replacePackageMap = {} + + +# This ReplacePackage mechanism allows modulefinder to work around the +# way the _xmlplus package injects itself under the name "xml" into +# sys.modules at runtime by calling ReplacePackage("_xmlplus", "xml") +# before running ModuleGraph. +def ReplacePackage(oldname, newname): + warnings.warn("use replacePackage instead of ReplacePackage", + DeprecationWarning) + replacePackage(oldname, newname) + + +def replacePackage(oldname, newname): + _replacePackageMap[oldname] = newname + + +#FIXME: What is this? Do we actually need this? This appears to provide +#significantly more fine-grained metadata than PyInstaller will ever require. +#It consumes a great deal of space (slots or no slots), since we store an +#instance of this class for each edge of the graph. +class DependencyInfo (namedtuple("DependencyInfo", + ["conditional", "function", "tryexcept", "fromlist"])): + __slots__ = () + + def _merged(self, other): + if (not self.conditional and not self.function and not self.tryexcept) \ + or (not other.conditional and not other.function and not other.tryexcept): + return DependencyInfo( + conditional=False, + function=False, + tryexcept=False, + fromlist=self.fromlist and other.fromlist) + + else: + return DependencyInfo( + conditional=self.conditional or other.conditional, + function=self.function or other.function, + tryexcept=self.tryexcept or other.tryexcept, + fromlist=self.fromlist and other.fromlist) + + +#FIXME: Shift the following Node class hierarchy into a new +#"PyInstaller.lib.modulegraph.node" module. This module is much too long. +#FIXME: Refactor "_deferred_imports" from a tuple into a proper lightweight +#class leveraging "__slots__". If not for backward compatibility, we'd just +#leverage a named tuple -- but this should do just as well. +#FIXME: Move the "packagepath" attribute into the "Package" class. Only +#packages define the "__path__" special attribute. The codebase currently +#erroneously tests whether "module.packagepath is not None" to determine +#whether a node is a package or not. However, "isinstance(module, Package)" is +#a significantly more reliable test. Refactor the former into the latter. +class Node(object): + """ + Abstract base class (ABC) of all objects added to a `ModuleGraph`. + + Attributes + ---------- + code : codeobject + Code object of the pure-Python module corresponding to this graph node + if any _or_ `None` otherwise. + graphident : str + Synonym of `identifier` required by the `ObjectGraph` superclass of the + `ModuleGraph` class. For readability, the `identifier` attribute should + typically be used instead. + filename : str + Absolute path of this graph node's corresponding module, package, or C + extension if any _or_ `None` otherwise. + identifier : str + Fully-qualified name of this graph node's corresponding module, + package, or C extension. + packagepath : str + List of the absolute paths of all directories comprising this graph + node's corresponding package. If this is a: + * Non-namespace package, this list contains exactly one path. + * Namespace package, this list contains one or more paths. + _deferred_imports : list + List of all target modules imported by the source module corresponding + to this graph node whole importations have been deferred for subsequent + processing in between calls to the `_ModuleGraph._scan_code()` and + `_ModuleGraph._process_imports()` methods for this source module _or_ + `None` otherwise. Each element of this list is a 3-tuple + `(have_star, _safe_import_hook_args, _safe_import_hook_kwargs)` + collecting the importation of a target module from this source module + for subsequent processing, where: + * `have_star` is a boolean `True` only if this is a `from`-style star + import (e.g., resembling `from {target_module_name} import *`). + * `_safe_import_hook_args` is a (typically non-empty) sequence of all + positional arguments to be passed to the `_safe_import_hook()` method + to add this importation to the graph. + * `_safe_import_hook_kwargs` is a (typically empty) dictionary of all + keyword arguments to be passed to the `_safe_import_hook()` method + to add this importation to the graph. + Unlike functional languages, Python imposes a maximum depth on the + interpreter stack (and hence recursion). On breaching this depth, + Python raises a fatal `RuntimeError` exception. Since `ModuleGraph` + parses imports recursively rather than iteratively, this depth _was_ + commonly breached before the introduction of this list. Python + environments installing a large number of modules (e.g., Anaconda) were + particularly susceptible. Why? Because `ModuleGraph` concurrently + descended through both the abstract syntax trees (ASTs) of all source + modules being parsed _and_ the graph of all target modules imported by + these source modules being built. The stack thus consisted of + alternating layers of AST and graph traversal. To unwind such + alternation and effectively halve the stack depth, `ModuleGraph` now + descends through the abstract syntax tree (AST) of each source module + being parsed and adds all importations originating within this module + to this list _before_ descending into the graph of these importations. + See pyinstaller/pyinstaller/#1289 for further details. + _global_attr_names : set + Set of the unqualified names of all global attributes (e.g., classes, + variables) defined in the pure-Python module corresponding to this + graph node if any _or_ the empty set otherwise. This includes the names + of all attributes imported via `from`-style star imports from other + existing modules (e.g., `from {target_module_name} import *`). This + set is principally used to differentiate the non-ignorable importation + of non-existent submodules in a package from the ignorable importation + of existing global attributes defined in that package's pure-Python + `__init__` submodule in `from`-style imports (e.g., `bar` in + `from foo import bar`, which may be either a submodule or attribute of + `foo`), as such imports ambiguously allow both. This set is _not_ used + to differentiate submodules from attributes in `import`-style imports + (e.g., `bar` in `import foo.bar`, which _must_ be a submodule of + `foo`), as such imports unambiguously allow only submodules. + _starimported_ignored_module_names : set + Set of the fully-qualified names of all existing unparsable modules + that the existing parsable module corresponding to this graph node + attempted to perform one or more "star imports" from. If this module + either does _not_ exist or does but is unparsable, this is the empty + set. Equivalently, this set contains each fully-qualified name + `{trg_module_name}` for which: + * This module contains an import statement of the form + `from {trg_module_name} import *`. + * The module whose name is `{trg_module_name}` exists but is _not_ + parsable by `ModuleGraph` (e.g., due to _not_ being pure-Python). + **This set is currently defined but otherwise ignored.** + _submodule_basename_to_node : dict + Dictionary mapping from the unqualified name of each submodule + contained by the parent module corresponding to this graph node to that + submodule's graph node. If this dictionary is non-empty, this parent + module is typically but _not_ always a package (e.g., the non-package + `os` module containing the `os.path` submodule). + """ + + __slots__ = [ + 'code', + 'filename', + 'graphident', + 'identifier', + 'packagepath', + '_deferred_imports', + '_global_attr_names', + '_starimported_ignored_module_names', + '_submodule_basename_to_node', + ] + + def __init__(self, identifier): + """ + Initialize this graph node. + + Parameters + ---------- + identifier : str + Fully-qualified name of this graph node's corresponding module, + package, or C extension. + """ + + self.code = None + self.filename = None + self.graphident = identifier + self.identifier = identifier + self.packagepath = None + self._deferred_imports = None + self._global_attr_names = set() + self._starimported_ignored_module_names = set() + self._submodule_basename_to_node = dict() + + + def is_global_attr(self, attr_name): + """ + `True` only if the pure-Python module corresponding to this graph node + defines a global attribute (e.g., class, variable) with the passed + name. + + If this module is actually a package, this method instead returns + `True` only if this package's pure-Python `__init__` submodule defines + such a global attribute. In this case, note that this package may still + contain an importable submodule of the same name. Callers should + attempt to import this attribute as a submodule of this package + _before_ assuming this attribute to be an ignorable global. See + "Examples" below for further details. + + Parameters + ---------- + attr_name : str + Unqualified name of the attribute to be tested. + + Returns + ---------- + bool + `True` only if this module defines this global attribute. + + Examples + ---------- + Consider a hypothetical module `foo` containing submodules `bar` and + `__init__` where the latter assigns `bar` to be a global variable + (possibly star-exported via the special `__all__` global variable): + + >>> # In "foo.__init__": + >>> bar = 3.1415 + + Python 2 and 3 both permissively permit this. This method returns + `True` in this case (i.e., when called on the `foo` package's graph + node, passed the attribute name `bar`) despite the importability of the + `foo.bar` submodule. + """ + + return attr_name in self._global_attr_names + + + def is_submodule(self, submodule_basename): + """ + `True` only if the parent module corresponding to this graph node + contains the submodule with the passed name. + + If `True`, this parent module is typically but _not_ always a package + (e.g., the non-package `os` module containing the `os.path` submodule). + + Parameters + ---------- + submodule_basename : str + Unqualified name of the submodule to be tested. + + Returns + ---------- + bool + `True` only if this parent module contains this submodule. + """ + + return submodule_basename in self._submodule_basename_to_node + + + def add_global_attr(self, attr_name): + """ + Record the global attribute (e.g., class, variable) with the passed + name to be defined by the pure-Python module corresponding to this + graph node. + + If this module is actually a package, this method instead records this + attribute to be defined by this package's pure-Python `__init__` + submodule. + + Parameters + ---------- + attr_name : str + Unqualified name of the attribute to be added. + """ + + self._global_attr_names.add(attr_name) + + + def add_global_attrs_from_module(self, target_module): + """ + Record all global attributes (e.g., classes, variables) defined by the + target module corresponding to the passed graph node to also be defined + by the source module corresponding to this graph node. + + If the source module is actually a package, this method instead records + these attributes to be defined by this package's pure-Python `__init__` + submodule. + + Parameters + ---------- + target_module : Node + Graph node of the target module to import attributes from. + """ + + self._global_attr_names.update(target_module._global_attr_names) + + + def add_submodule(self, submodule_basename, submodule_node): + """ + Add the submodule with the passed name and previously imported graph + node to the parent module corresponding to this graph node. + + This parent module is typically but _not_ always a package (e.g., the + non-package `os` module containing the `os.path` submodule). + + Parameters + ---------- + submodule_basename : str + Unqualified name of the submodule to add to this parent module. + submodule_node : Node + Graph node of this submodule. + """ + + self._submodule_basename_to_node[submodule_basename] = submodule_node + + + def get_submodule(self, submodule_basename): + """ + Graph node of the submodule with the passed name in the parent module + corresponding to this graph node. + + If this parent module does _not_ contain this submodule, an exception + is raised. Else, this parent module is typically but _not_ always a + package (e.g., the non-package `os` module containing the `os.path` + submodule). + + Parameters + ---------- + module_basename : str + Unqualified name of the submodule to retrieve. + + Returns + ---------- + Node + Graph node of this submodule. + """ + + return self._submodule_basename_to_node[submodule_basename] + + + def get_submodule_or_none(self, submodule_basename): + """ + Graph node of the submodule with the passed unqualified name in the + parent module corresponding to this graph node if this module contains + this submodule _or_ `None`. + + This parent module is typically but _not_ always a package (e.g., the + non-package `os` module containing the `os.path` submodule). + + Parameters + ---------- + submodule_basename : str + Unqualified name of the submodule to retrieve. + + Returns + ---------- + Node + Graph node of this submodule if this parent module contains this + submodule _or_ `None`. + """ + + return self._submodule_basename_to_node.get(submodule_basename) + + + def remove_global_attr_if_found(self, attr_name): + """ + Record the global attribute (e.g., class, variable) with the passed + name if previously recorded as defined by the pure-Python module + corresponding to this graph node to be subsequently undefined by the + same module. + + If this module is actually a package, this method instead records this + attribute to be undefined by this package's pure-Python `__init__` + submodule. + + This method is intended to be called on globals previously defined by + this module that are subsequently undefined via the `del` built-in by + this module, thus "forgetting" or "undoing" these globals. + + For safety, there exists no corresponding `remove_global_attr()` + method. While defining this method is trivial, doing so would invite + `KeyError` exceptions on scanning valid Python that lexically deletes a + global in a scope under this module's top level (e.g., in a function) + _before_ defining this global at this top level. Since `ModuleGraph` + cannot and should not (re)implement a full-blown Python interpreter, + ignoring out-of-order deletions is the only sane policy. + + Parameters + ---------- + attr_name : str + Unqualified name of the attribute to be removed. + """ + + if self.is_global_attr(attr_name): + self._global_attr_names.remove(attr_name) + + + def __cmp__(self, other): + try: + otherIdent = getattr(other, 'graphident') + except AttributeError: + return NotImplemented + + return cmp(self.graphident, otherIdent) # noqa: F821 + + def __eq__(self, other): + try: + otherIdent = getattr(other, 'graphident') + except AttributeError: + return False + + return self.graphident == otherIdent + + def __ne__(self, other): + try: + otherIdent = getattr(other, 'graphident') + except AttributeError: + return True + + return self.graphident != otherIdent + + def __lt__(self, other): + try: + otherIdent = getattr(other, 'graphident') + except AttributeError: + return NotImplemented + + return self.graphident < otherIdent + + def __le__(self, other): + try: + otherIdent = getattr(other, 'graphident') + except AttributeError: + return NotImplemented + + return self.graphident <= otherIdent + + def __gt__(self, other): + try: + otherIdent = getattr(other, 'graphident') + except AttributeError: + return NotImplemented + + return self.graphident > otherIdent + + def __ge__(self, other): + try: + otherIdent = getattr(other, 'graphident') + except AttributeError: + return NotImplemented + + return self.graphident >= otherIdent + + def __hash__(self): + return hash(self.graphident) + + def infoTuple(self): + return (self.identifier,) + + def __repr__(self): + return '%s%r' % (type(self).__name__, self.infoTuple()) + + +# TODO: This indirection is, frankly, unnecessary. The +# ModuleGraph.alias_module() should directly add the desired AliasNode instance +# to the graph rather than indirectly adding an Alias instance to the +# "lazynodes" dictionary. +class Alias(str): + """ + Placeholder aliasing an existing source module to a non-existent target + module (i.e., the desired alias). + + For obscure reasons, this class subclasses `str`. Each instance of this + class is the fully-qualified name of the existing source module being + aliased. Unlike the related `AliasNode` class, instances of this class are + _not_ actual nodes and hence _not_ added to the graph; they only facilitate + communication between the `ModuleGraph.alias_module()` and + `ModuleGraph.findNode()` methods. + """ + + +class AliasNode(Node): + """ + Graph node representing the aliasing of an existing source module under a + non-existent target module name (i.e., the desired alias). + """ + + def __init__(self, name, node): + """ + Initialize this alias. + + Parameters + ---------- + name : str + Fully-qualified name of the non-existent target module to be + created (as an alias of the existing source module). + node : Node + Graph node of the existing source module being aliased. + """ + super(AliasNode, self).__init__(name) + + #FIXME: Why only some? Why not *EVERYTHING* except "graphident", which + #must remain equal to "name" for lookup purposes? This is, after all, + #an alias. The idea is for the two nodes to effectively be the same. + + # Copy some attributes from this source module into this target alias. + for attr_name in ( + 'identifier', 'packagepath', + '_global_attr_names', '_starimported_ignored_module_names', + '_submodule_basename_to_node'): + if hasattr(node, attr_name): + setattr(self, attr_name, getattr(node, attr_name)) + + + def infoTuple(self): + return (self.graphident, self.identifier) + + +class BadModule(Node): + pass + + +class ExcludedModule(BadModule): + pass + + +class MissingModule(BadModule): + pass + + +class InvalidRelativeImport (BadModule): + def __init__(self, relative_path, from_name): + identifier = relative_path + if relative_path.endswith('.'): + identifier += from_name + else: + identifier += '.' + from_name + super(InvalidRelativeImport, self).__init__(identifier) + self.relative_path = relative_path + self.from_name = from_name + + def infoTuple(self): + return (self.relative_path, self.from_name) + + +class Script(Node): + def __init__(self, filename): + super(Script, self).__init__(filename) + self.filename = filename + + def infoTuple(self): + return (self.filename,) + + +class BaseModule(Node): + def __init__(self, name, filename=None, path=None): + super(BaseModule, self).__init__(name) + self.filename = filename + self.packagepath = path + + def infoTuple(self): + return tuple(filter(None, (self.identifier, self.filename, self.packagepath))) + + +class BuiltinModule(BaseModule): + pass + + +class SourceModule(BaseModule): + pass + + +class InvalidSourceModule(SourceModule): + pass + + +class CompiledModule(BaseModule): + pass + + +class InvalidCompiledModule(BaseModule): + pass + + +class Extension(BaseModule): + pass + + +class Package(BaseModule): + """ + Graph node representing a non-namespace package. + """ + pass + + +class ExtensionPackage(Extension, Package): + """ + Graph node representing a package where the __init__ module is an extension + module. + """ + pass + + +class NamespacePackage(Package): + """ + Graph node representing a namespace package. + """ + pass + + +class RuntimeModule(BaseModule): + """ + Graph node representing a non-package Python module dynamically defined at + runtime. + + Most modules are statically defined on-disk as standard Python files. + Some modules, however, are dynamically defined in-memory at runtime + (e.g., `gi.repository.Gst`, dynamically defined by the statically + defined `gi.repository.__init__` module). + + This node represents such a runtime module. Since this is _not_ a package, + all attempts to import submodules from this module in `from`-style import + statements (e.g., the `queue` submodule in `from six.moves import queue`) + will be silently ignored. + + To ensure that the parent package of this module if any is also imported + and added to the graph, this node is typically added to the graph by + calling the `ModuleGraph.add_module()` method. + """ + pass + + +class RuntimePackage(Package): + """ + Graph node representing a non-namespace Python package dynamically defined + at runtime. + + Most packages are statically defined on-disk as standard subdirectories + containing `__init__.py` files. Some packages, however, are dynamically + defined in-memory at runtime (e.g., `six.moves`, dynamically defined by + the statically defined `six` module). + + This node represents such a runtime package. All attributes imported from + this package in `from`-style import statements that are submodules of this + package (e.g., the `queue` submodule in `from six.moves import queue`) will + be imported rather than ignored. + + To ensure that the parent package of this package if any is also imported + and added to the graph, this node is typically added to the graph by + calling the `ModuleGraph.add_module()` method. + """ + pass + + +#FIXME: Safely removable. We don't actually use this anywhere. After removing +#this class, remove the corresponding entry from "compat". +class FlatPackage(BaseModule): + def __init__(self, *args, **kwds): + warnings.warn( + "This class will be removed in a future version of modulegraph", + DeprecationWarning) + super(FlatPackage, *args, **kwds) + + +#FIXME: Safely removable. We don't actually use this anywhere. After removing +#this class, remove the corresponding entry from "compat". +class ArchiveModule(BaseModule): + def __init__(self, *args, **kwds): + warnings.warn( + "This class will be removed in a future version of modulegraph", + DeprecationWarning) + super(FlatPackage, *args, **kwds) + + +# HTML templates for ModuleGraph generator +header = """\ + + + + + %(TITLE)s + + + +

%(TITLE)s

""" +entry = """ +
+ + %(CONTENT)s +
""" +contpl = """%(NAME)s %(TYPE)s""" +contpl_linked = """\ +%(NAME)s +%(TYPE)s""" +imports = """\ +
+%(HEAD)s: + %(LINKS)s +
+""" +footer = """ + +""" + + +def _ast_names(names): + result = [] + for nm in names: + if isinstance(nm, ast.alias): + result.append(nm.name) + else: + result.append(nm) + + result = [r for r in result if r != '__main__'] + return result + + +def uniq(seq): + """Remove duplicates from a list, preserving order""" + # Taken from https://stackoverflow.com/questions/480214 + seen = set() + seen_add = seen.add + return [x for x in seq if not (x in seen or seen_add(x))] + + +if sys.version_info[0] == 2: + DEFAULT_IMPORT_LEVEL = -1 +else: + DEFAULT_IMPORT_LEVEL = 0 + + +class _Visitor(ast.NodeVisitor): + def __init__(self, graph, module): + self._graph = graph + self._module = module + self._level = DEFAULT_IMPORT_LEVEL + self._in_if = [False] + self._in_def = [False] + self._in_tryexcept = [False] + + @property + def in_if(self): + return self._in_if[-1] + + @property + def in_def(self): + return self._in_def[-1] + + @property + def in_tryexcept(self): + return self._in_tryexcept[-1] + + + def _collect_import(self, name, fromlist, level): + if sys.version_info[0] == 2: + if name == '__future__' and 'absolute_import' in (fromlist or ()): + self._level = 0 + + have_star = False + if fromlist is not None: + fromlist = uniq(fromlist) + if '*' in fromlist: + fromlist.remove('*') + have_star = True + + # Record this import as originating from this module for subsequent + # handling by the _process_imports() method. + self._module._deferred_imports.append( + (have_star, + (name, self._module, fromlist, level), + {'edge_attr': DependencyInfo( + conditional=self.in_if, + tryexcept=self.in_tryexcept, + function=self.in_def, + fromlist=False)})) + + + def visit_Import(self, node): + for nm in _ast_names(node.names): + self._collect_import(nm, None, self._level) + + def visit_ImportFrom(self, node): + level = node.level if node.level != 0 else self._level + self._collect_import(node.module or '', _ast_names(node.names), level) + + def visit_If(self, node): + self._in_if.append(True) + self.generic_visit(node) + self._in_if.pop() + + def visit_FunctionDef(self, node): + self._in_def.append(True) + self.generic_visit(node) + self._in_def.pop() + + visit_AsyncFunctionDef = visit_FunctionDef + + def visit_Try(self, node): + self._in_tryexcept.append(True) + self.generic_visit(node) + self._in_tryexcept.pop() + + def visit_TryExcept(self, node): + self._in_tryexcept.append(True) + self.generic_visit(node) + self._in_tryexcept.pop() + + def visit_Expression(self, node): + # Expression node's cannot contain import statements or + # other nodes that are relevant for us. + pass + + # Expression isn't actually used as such in AST trees, + # therefore define visitors for all kinds of expression nodes. + visit_BoolOp = visit_Expression + visit_BinOp = visit_Expression + visit_UnaryOp = visit_Expression + visit_Lambda = visit_Expression + visit_IfExp = visit_Expression + visit_Dict = visit_Expression + visit_Set = visit_Expression + visit_ListComp = visit_Expression + visit_SetComp = visit_Expression + visit_ListComp = visit_Expression + visit_GeneratorExp = visit_Expression + visit_Compare = visit_Expression + visit_Yield = visit_Expression + visit_YieldFrom = visit_Expression + visit_Await = visit_Expression + visit_Call = visit_Expression + visit_Await = visit_Expression + + +class ModuleGraph(ObjectGraph): + """ + Directed graph whose nodes represent modules and edges represent + dependencies between these modules. + """ + + + def createNode(self, cls, name, *args, **kw): + m = self.findNode(name) + + if m is None: + #assert m is None, m + m = super(ModuleGraph, self).createNode(cls, name, *args, **kw) + + return m + + + def __init__(self, path=None, excludes=(), replace_paths=(), implies=(), graph=None, debug=0): + super(ModuleGraph, self).__init__(graph=graph, debug=debug) + if path is None: + path = sys.path + self.path = path + self.lazynodes = {} + # excludes is stronger than implies + self.lazynodes.update(dict(implies)) + for m in excludes: + self.lazynodes[m] = None + self.replace_paths = replace_paths + + self.set_setuptools_nspackages() + # Maintain own list of package path mappings in the scope of Modulegraph + # object. + self._package_path_map = _packagePathMap + + def set_setuptools_nspackages(self): + # This is used when running in the test-suite + self.nspackages = self._calc_setuptools_nspackages() + + def _calc_setuptools_nspackages(self): + # Setuptools has some magic handling for namespace + # packages when using 'install --single-version-externally-managed' + # (used by system packagers and also by pip) + # + # When this option is used namespace packages are writting to + # disk *without* an __init__.py file, which means the regular + # import machinery will not find them. + # + # We therefore explicitly look for the hack used by + # setuptools to get this kind of namespace packages to work. + + pkgmap = {} + + try: + from pkgutil import ImpImporter + except ImportError: + try: + from _pkgutil import ImpImporter + except ImportError: + ImpImporter = pkg_resources.ImpWrapper + + if sys.version_info[:2] >= (3, 3): + import importlib.machinery + ImpImporter = importlib.machinery.FileFinder + + for entry in self.path: + importer = pkg_resources.get_importer(entry) + + if isinstance(importer, ImpImporter): + try: + ldir = os.listdir(entry) + except os.error: + continue + + for fn in ldir: + if fn.endswith('-nspkg.pth'): + with open(os.path.join(entry, fn), _READ_MODE) as fp: + for ln in fp: + for pfx in _SETUPTOOLS_NAMESPACEPKG_PTHs: + if ln.startswith(pfx): + try: + start = len(pfx)-2 + stop = ln.index(')', start)+1 + except ValueError: + continue + + pkg = _eval_str_tuple(ln[start:stop]) + identifier = ".".join(pkg) + subdir = os.path.join(entry, *pkg) + if os.path.exists(os.path.join(subdir, '__init__.py')): + # There is a real __init__.py, + # ignore the setuptools hack + continue + + if identifier in pkgmap: + pkgmap[identifier].append(subdir) + else: + pkgmap[identifier] = [subdir] + break + + return pkgmap + + def implyNodeReference(self, node, other, edge_data=None): + """ + Create a reference from the passed source node to the passed other node, + implying the former to depend upon the latter. + + While the source node _must_ be an existing graph node, the target node + may be either an existing graph node _or_ a fully-qualified module name. + In the latter case, the module with that name and all parent packages of + that module will be imported _without_ raising exceptions and for each + newly imported module or package: + + * A new graph node will be created for that module or package. + * A reference from the passed source node to that module or package will + be created. + + This method allows dependencies between Python objects _not_ importable + with standard techniques (e.g., module aliases, C extensions). + + Parameters + ---------- + node : str + Graph node for this reference's source module or package. + other : {Node, str} + Either a graph node _or_ fully-qualified name for this reference's + target module or package. + """ + + if isinstance(other, Node): + self._updateReference(node, other, edge_data) + else: + if isinstance(other, tuple): + raise ValueError(other) + others = self._safe_import_hook(other, node, None) + for other in others: + self._updateReference(node, other, edge_data) + + def getReferences(self, fromnode): + """ + Yield all nodes that `fromnode` dependes on (that is, + all modules that `fromnode` imports. + """ + + node = self.findNode(fromnode) + out_edges, _ = self.get_edges(node) + return out_edges + + def getReferers(self, tonode, collapse_missing_modules=True): + node = self.findNode(tonode) + _, in_edges = self.get_edges(node) + + if collapse_missing_modules: + for n in in_edges: + if isinstance(n, MissingModule): + for n in self.getReferers(n, False): + yield n + + else: + yield n + + else: + for n in in_edges: + yield n + + def hasEdge(self, fromnode, tonode): + """ Return True iff there is an edge from 'fromnode' to 'tonode' """ + fromnode = self.findNode(fromnode) + tonode = self.findNode(tonode) + + return self.graph.edge_by_node(fromnode, tonode) is not None + + def foldReferences(self, packagenode): + """ + Create edges to/from `packagenode` based on the edges to/from all + submodules of that package _and_ then hide the graph nodes + corresponding to those submodules. + """ + + pkg = self.findNode(packagenode) + + for n in self.nodes(): + if not n.identifier.startswith(pkg.identifier + '.'): + continue + + iter_out, iter_inc = self.get_edges(n) + for other in iter_out: + if other.identifier.startswith(pkg.identifier + '.'): + continue + + if not self.hasEdge(pkg, other): + # Ignore circular dependencies + self._updateReference(pkg, other, 'pkg-internal-import') + + for other in iter_inc: + if other.identifier.startswith(pkg.identifier + '.'): + # Ignore circular dependencies + continue + + if not self.hasEdge(other, pkg): + self._updateReference(other, pkg, 'pkg-import') + + self.graph.hide_node(n) + + # TODO: unfoldReferences(pkg) that restore the submodule nodes and + # removes 'pkg-import' and 'pkg-internal-import' edges. Care should + # be taken to ensure that references are correct if multiple packages + # are folded and then one of them in unfolded + + def _updateReference(self, fromnode, tonode, edge_data): + try: + ed = self.edgeData(fromnode, tonode) + except (KeyError, GraphError): # XXX: Why 'GraphError' + return self.createReference(fromnode, tonode, edge_data) + + if not (isinstance(ed, DependencyInfo) and isinstance(edge_data, DependencyInfo)): + self.updateEdgeData(fromnode, tonode, edge_data) + else: + self.updateEdgeData(fromnode, tonode, ed._merged(edge_data)) + + def createReference(self, fromnode, tonode, edge_data='direct'): + """ + Create a reference from fromnode to tonode + """ + return super(ModuleGraph, self).createReference(fromnode, tonode, edge_data=edge_data) + + def findNode(self, name, create_nspkg=True): + """ + Graph node uniquely identified by the passed fully-qualified module + name if this module has been added to the graph _or_ `None` otherwise. + + If (in order): + + . A namespace package with this identifier exists _and_ the passed + `create_nspkg` parameter is `True`, this package will be + instantiated and returned. + . A lazy node with this identifier and: + * No dependencies exists, this node will be instantiated and + returned. + * Dependencies exists, this node and all transitive dependencies of + this node be instantiated and this node returned. + . A non-lazy node with this identifier exists, this node will be + returned as is. + + Parameters + ---------- + name : str + Fully-qualified name of the module whose graph node is to be found. + create_nspkg : bool + Whether or not to implicitly instantiate namespace packages. If + `True` _and_ this name is that of a previously registered namespace + package (i.e., in `self.nspackages`) not already added to the + graph, this package will be added to the graph. Defaults to `True`. + + Returns + ---------- + Node + Graph node of this module if added to the graph _or_ `None` + otherwise. + """ + + data = super(ModuleGraph, self).findNode(name) + + if data is not None: + return data + + if name in self.lazynodes: + deps = self.lazynodes.pop(name) + + if deps is None: + # excluded module + m = self.createNode(ExcludedModule, name) + elif isinstance(deps, Alias): + other = self._safe_import_hook(deps, None, None).pop() + m = self.createNode(AliasNode, name, other) + self.implyNodeReference(m, other) + else: + m = self._safe_import_hook(name, None, None).pop() + for dep in deps: + self.implyNodeReference(m, dep) + + return m + + if name in self.nspackages and create_nspkg: + # name is a --single-version-externally-managed + # namespace package (setuptools/distribute) + pathnames = self.nspackages.pop(name) + m = self.createNode(NamespacePackage, name) + + # FIXME: The filename must be set to a string to ensure that py2app + # works, it is not clear yet why that is. Setting to None would be + # cleaner. + m.filename = '-' + m.packagepath = _namespace_package_path(name, pathnames, self.path) + + # As per comment at top of file, simulate runtime packagepath additions. + m.packagepath = m.packagepath + self._package_path_map.get(name, []) + return m + + return None + + + def run_script(self, pathname, caller=None): + """ + Create a node by path (not module name). It is expected to be a Python + source file, and will be scanned for dependencies. + """ + self.msg(2, "run_script", pathname) + + pathname = os.path.realpath(pathname) + m = self.findNode(pathname) + if m is not None: + return m + + if sys.version_info[0] != 2: + with open(pathname, 'rb') as fp: + encoding = util.guess_encoding(fp) + + with open(pathname, _READ_MODE, encoding=encoding) as fp: + contents = fp.read() + '\n' + if contents.startswith(BOM): + # Ignore BOM at start of input + contents = contents[1:] + + else: + with open(pathname, _READ_MODE) as fp: + contents = fp.read() + '\n' + + co_ast = compile(contents, pathname, 'exec', ast.PyCF_ONLY_AST, True) + co = compile(co_ast, pathname, 'exec', 0, True) + m = self.createNode(Script, pathname) + self._updateReference(caller, m, None) + n = self._scan_code(m, co, co_ast) + self._process_imports(n) + m.code = co + if self.replace_paths: + m.code = self._replace_paths_in_code(m.code) + return m + + + #FIXME: For safety, the "source_module" parameter should default to the + #root node of the current graph if unpassed. This parameter currently + #defaults to None, thus disconnected modules imported in this manner (e.g., + #hidden imports imported by depend.analysis.initialize_modgraph()). + def import_hook( + self, + target_module_partname, + source_module=None, + target_attr_names=None, + level=DEFAULT_IMPORT_LEVEL, + edge_attr=None, + ): + """ + Import the module with the passed name, all parent packages of this + module, _and_ all submodules and attributes in this module with the + passed names from the previously imported caller module signified by + the passed graph node. + + Unlike most import methods (e.g., `_safe_import_hook()`), this method + is designed to be publicly called by both external and internal + callers and hence is public. + + Parameters + ---------- + target_module_partname : str + Partially-qualified name of the target module to be imported. See + `_safe_import_hook()` for further details. + source_module : Node + Graph node for the previously imported **source module** (i.e., + module containing the `import` statement triggering the call to + this method) _or_ `None` if this module is to be imported in a + "disconnected" manner. **Passing `None` is _not_ recommended.** + Doing so produces a disconnected graph in which the graph node + created for the module to be imported will be disconnected and + hence unreachable from all other nodes -- which frequently causes + subtle issues in external callers (namely PyInstaller, which + silently ignores unreachable nodes). + target_attr_names : list + List of the unqualified names of all submodules and attributes to + be imported from the module to be imported if this is a "from"- + style import (e.g., `[encode_base64, encode_noop]` for the import + `from email.encoders import encode_base64, encode_noop`) _or_ + `None` otherwise. + level : int + Whether to perform an absolute or relative import. See + `_safe_import_hook()` for further details. + + Returns + ---------- + list + List of the graph nodes created for all modules explicitly imported + by this call, including the passed module and all submodules listed + in `target_attr_names` _but_ excluding all parent packages + implicitly imported by this call. If `target_attr_names` is `None` + or the empty list, this is guaranteed to be a list of one element: + the graph node created for the passed module. + + Raises + ---------- + ImportError + If the target module to be imported is unimportable. + """ + self.msg(3, "_import_hook", target_module_partname, source_module, source_module, level) + + source_package = self._determine_parent(source_module) + target_package, target_module_partname = self._find_head_package( + source_package, target_module_partname, level) + + self.msgin(4, "load_tail", target_package, target_module_partname) + + submodule = target_package + while target_module_partname: + i = target_module_partname.find('.') + if i < 0: + i = len(target_module_partname) + head, target_module_partname = target_module_partname[ + :i], target_module_partname[i+1:] + mname = "%s.%s" % (submodule.identifier, head) + submodule = self._safe_import_module(head, mname, submodule) + + if submodule is None: + # FIXME: Why do we no longer return a MissingModule instance? + # result = self.createNode(MissingModule, mname) + self.msgout(4, "raise ImportError: No module named", mname) + raise ImportError("No module named " + repr(mname)) + + self.msgout(4, "load_tail ->", submodule) + + target_module = submodule + target_modules = [target_module] + + # If this is a "from"-style import *AND* this target module is + # actually a package, import all submodules of this package specified + # by the "import" half of this import (e.g., the submodules "bar" and + # "car" of the target package "foo" in "from foo import bar, car"). + # + # If this target module is a non-package, it could still contain + # importable submodules (e.g., the non-package `os` module containing + # the `os.path` submodule). In this case, these submodules are already + # imported by this target module's pure-Python code. Since our import + # scanner already detects such imports, these submodules need *NOT* be + # reimported here. + if target_attr_names and isinstance(target_module, + (Package, AliasNode)): + for target_submodule in self._import_importable_package_submodules( + target_module, target_attr_names): + if target_submodule not in target_modules: + target_modules.append(target_submodule) + + # Add an edge from this source module to each target module. + for target_module in target_modules: + self._updateReference( + source_module, target_module, edge_data=edge_attr) + + return target_modules + + + def _determine_parent(self, caller): + """ + Determine the package containing a node. + """ + self.msgin(4, "determine_parent", caller) + + parent = None + if caller: + pname = caller.identifier + + if isinstance(caller, Package): + parent = caller + + elif '.' in pname: + pname = pname[:pname.rfind('.')] + parent = self.findNode(pname) + + elif caller.packagepath: + # XXX: I have no idea why this line + # is necessary. + parent = self.findNode(pname) + + self.msgout(4, "determine_parent ->", parent) + return parent + + + def _find_head_package( + self, + source_package, + target_module_partname, + level=DEFAULT_IMPORT_LEVEL): + """ + Import the target package providing the target module with the passed + name to be subsequently imported from the previously imported source + package corresponding to the passed graph node. + + Parameters + ---------- + source_package : Package + Graph node for the previously imported **source package** (i.e., + package containing the module containing the `import` statement + triggering the call to this method) _or_ `None` if this module is + to be imported in a "disconnected" manner. **Passing `None` is + _not_ recommended.** See the `_import_hook()` method for further + details. + target_module_partname : str + Partially-qualified name of the target module to be imported. See + `_safe_import_hook()` for further details. + level : int + Whether to perform absolute or relative imports. See the + `_safe_import_hook()` method for further details. + + Returns + ---------- + (target_package, target_module_tailname) + 2-tuple describing the imported target package, where: + * `target_package` is the graph node created for this package. + * `target_module_tailname` is the unqualified name of the target + module to be subsequently imported (e.g., `text` when passed a + `target_module_partname` of `email.mime.text`). + + Raises + ---------- + ImportError + If the package to be imported is unimportable. + """ + self.msgin(4, "find_head_package", source_package, target_module_partname, level) + + #FIXME: Rename all local variable names to something sensible. No, + #"p_fqdn" is not a sensible name. + + # If this target module is a submodule... + if '.' in target_module_partname: + target_module_headname, target_module_tailname = ( + target_module_partname.split('.', 1)) + # Else, this target module is a top-level module. + else: + target_module_headname = target_module_partname + target_module_tailname = '' + + # If attempting both absolute and relative imports... + if level == ABSOLUTE_OR_RELATIVE_IMPORT_LEVEL: + if source_package: + target_package_name = source_package.identifier + '.' + target_module_headname + else: + target_package_name = target_module_headname + # Else if attempting only absolute imports... + elif level == ABSOLUTE_IMPORT_LEVEL: + target_package_name = target_module_headname + + # Absolute import, ignore the parent + source_package = None + # Else if attempting only relative imports... + else: + if source_package is None: + self.msg(2, "Relative import outside of package") + raise InvalidRelativeImportError( + "Relative import outside of package (name=%r, parent=%r, level=%r)" % ( + target_module_partname, source_package, level)) + + for i in range(level - 1): + if '.' not in source_package.identifier: + self.msg(2, "Relative import outside of package") + raise InvalidRelativeImportError( + "Relative import outside of package (name=%r, parent=%r, level=%r)" % ( + target_module_partname, source_package, level)) + + p_fqdn = source_package.identifier.rsplit('.', 1)[0] + new_parent = self.findNode(p_fqdn) + if new_parent is None: + #FIXME: Repetition detected. Exterminate. Exterminate. + self.msg(2, "Relative import outside of package") + raise InvalidRelativeImportError( + "Relative import outside of package (name=%r, parent=%r, level=%r)" % ( + target_module_partname, source_package, level)) + + assert new_parent is not source_package, ( + new_parent, source_package) + source_package = new_parent + + if target_module_headname: + target_package_name = ( + source_package.identifier + '.' + target_module_headname) + else: + target_package_name = source_package.identifier + + # Graph node of this target package. + target_package = self._safe_import_module( + target_module_headname, target_package_name, source_package) + + #FIXME: Why exactly is this necessary again? This doesn't quite seem + #right but maybe it is. Shouldn't absolute imports only be performed if + #the passed "level" is either "ABSOLUTE_IMPORT_LEVEL" or + #"ABSOLUTE_OR_RELATIVE_IMPORT_LEVEL" -- or, more succinctly: + # + # if level < 1: + + # If this target package is *NOT* importable and a source package was + # passed, attempt to import this target package as an absolute import. + if target_package is None and source_package is not None: + target_package_name = target_module_headname + source_package = None + + # Graph node for the target package, again. + target_package = self._safe_import_module( + target_module_headname, target_package_name, source_package) + + # If this target package is importable, return this package. + if target_package is not None: + self.msgout(4, "find_head_package ->", (target_package, target_module_tailname)) + return target_package, target_module_tailname + + # Else, raise an exception. + self.msgout(4, "raise ImportError: No module named", target_package_name) + raise ImportError("No module named " + target_package_name) + + + + + #FIXME: Refactor from a generator yielding graph nodes into a non-generator + #returning a list or tuple of all yielded graph nodes. This method is only + #called once above and the return value of that call is only iterated over + #as a list or tuple. There's no demonstrable reason for this to be a + #generator. Generators are great for their intended purposes (e.g., as + #continuations). This isn't one of those purposes. + def _import_importable_package_submodules(self, package, attr_names): + """ + Generator importing and yielding each importable submodule (of the + previously imported package corresponding to the passed graph node) + whose unqualified name is in the passed list. + + Elements of this list that are _not_ importable submodules of this + package are either: + + * Ignorable attributes (e.g., classes, globals) defined at the top + level of this package's `__init__` submodule, which will be ignored. + * Else, unignorable unimportable submodules, in which case an + exception is raised. + + Parameters + ---------- + package : Package + Graph node of the previously imported package containing the + modules to be imported and yielded. + + attr_names : list + List of the unqualified names of all attributes of this package to + attempt to import as submodules. This list will be internally + converted into a set, safely ignoring any duplicates in this list + (e.g., reducing the "from"-style import + `from foo import bar, car, far, bar, car, far` to merely + `from foo import bar, car, far`). + + Yields + ---------- + Node + Graph node created for the currently imported submodule. + + Raises + ---------- + ImportError + If any attribute whose name is in `attr_names` is neither: + * An importable submodule of this package. + * An ignorable global attribute (e.g., class, variable) defined at + the top level of this package's `__init__` submodule. + In this case, this attribute _must_ be an unimportable submodule of + this package. + """ + + # Ignore duplicate submodule names in the passed list. + attr_names = set(attr_names) + self.msgin(4, "_import_importable_package_submodules", package, attr_names) + + #FIXME: This test *SHOULD* be superfluous and hence safely removable. + #The higher-level _scan_bytecode() and _collect_import() methods + #already guarantee "*" characters to be removed from fromlists. + if '*' in attr_names: + attr_names.update(self._find_all_submodules(package)) + attr_names.remove('*') + + # self.msg(4, '_import_importable_package_submodules (global attrs)', package.identifier, package._global_attr_names) + + # For the name of each attribute to be imported from this package... + for attr_name in attr_names: + # self.msg(4, '_import_importable_package_submodules (fromlist attr)', package.identifier, attr_name) + + # Graph node of this attribute if this attribute is a previously + # imported module or None otherwise. + submodule = package.get_submodule_or_none(attr_name) + + # If this attribute is *NOT* a previously imported module, attempt + # to import this attribute as a submodule of this package. + if submodule is None: + # Fully-qualified name of this submodule. + submodule_name = package.identifier + '.' + attr_name + + # Graph node of this submodule if importable or None otherwise. + submodule = self._safe_import_module( + attr_name, submodule_name, package) + + # If this submodule is unimportable... + if submodule is None: + # If this attribute is a global (e.g., class, variable) + # defined at the top level of this package's "__init__" + # submodule, this importation is safely ignorable. Do so + # and skip to the next attribute. + # + # This behaviour is non-conformant with Python behaviour, + # which is bad, but is required to sanely handle all + # possible edge cases, which is good. In Python, a global + # attribute defined at the top level of a package's + # "__init__" submodule shadows a submodule of the same name + # in that package. Attempting to import that submodule + # instead imports that attribute; thus, that submodule is + # effectively unimportable. In this method and elsewhere, + # that submodule is tested for first and hence shadows that + # attribute -- the opposite logic. Attempts to import that + # attribute are mistakenly seen as attempts to import that + # submodule! Why? + # + # Edge cases. PyInstaller (and by extension ModuleGraph) + # only cares about module imports. Global attribute imports + # are parsed only as the means to this ends and are + # otherwise ignorable. The cost of erroneously shadowing: + # + # * Submodules by attributes is significant. Doing so + # prevents such submodules from being frozen and hence + # imported at application runtime. + # * Attributes by submodules is insignificant. Doing so + # could erroneously freeze such submodules despite their + # never being imported at application runtime. However, + # ModuleGraph is incapable of determining with certainty + # that Python logic in another module other than the + # "__init__" submodule containing these attributes does + # *NOT* delete these attributes and hence unshadow these + # submodules, which would then become importable at + # runtime and require freezing. Hence, ModuleGraph *MUST* + # permissively assume submodules of the same name as + # attributes to be unshadowed elsewhere and require + # freezing -- even if they do not. + # + # It is practically difficult (albeit technically feasible) + # for ModuleGraph to determine whether or not the target + # attribute names of "from"-style import statements (e.g., + # "bar" and "car" in "from foo import bar, car") refer to + # non-ignorable submodules or ignorable non-module globals + # during opcode scanning. Distinguishing these two cases + # during opcode scanning would require a costly call to the + # _find_module() method, which would subsequently be + # repeated during import-graph construction. This could be + # ameliorated with caching, which itself would require + # costly space consumption and developer time. + # + # Since opcode scanning fails to distinguish these two + # cases, this and other methods subsequently called at + # import-graph construction time (e.g., + # _safe_import_hook()) must do so. Since submodules of the + # same name as attributes must assume to be unshadowed + # elsewhere and require freezing, the only solution is to + # attempt to import an attribute as a non-ignorable module + # *BEFORE* assuming an attribute to be an ignorable + # non-module. Which is what this and other methods do. + # + # See Package.is_global_attr() for similar discussion. + if package.is_global_attr(attr_name): + self.msg(4, '_import_importable_package_submodules: ignoring from-imported global', package.identifier, attr_name) + continue + # Else, this attribute is an unimportable submodule. Since + # this is *NOT* safely ignorable, raise an exception. + else: + raise ImportError("No module named " + submodule_name) + + # Yield this submodule's graph node to the caller. + yield submodule + + self.msgin(4, "_import_importable_package_submodules ->") + + + def _find_all_submodules(self, m): + if not m.packagepath: + return + # 'suffixes' used to be a list hardcoded to [".py", ".pyc", ".pyo"]. + # But we must also collect Python extension modules - although + # we cannot separate normal dlls from Python extensions. + for path in m.packagepath: + try: + names = zipio.listdir(path) + except (os.error, IOError): + self.msg(2, "can't list directory", path) + continue + for info in (moduleInfoForPath(p) for p in names): + if info is None: + continue + if info[0] != '__init__': + yield info[0] + + + def alias_module(self, src_module_name, trg_module_name): + """ + Alias the source module to the target module with the passed names. + + This method ensures that the next call to findNode() given the target + module name will resolve this alias. This includes importing and adding + a graph node for the source module if needed as well as adding a + reference from the target to source module. + + Parameters + ---------- + src_module_name : str + Fully-qualified name of the existing **source module** (i.e., the + module being aliased). + trg_module_name : str + Fully-qualified name of the non-existent **target module** (i.e., + the alias to be created). + """ + self.msg(3, 'alias_module "%s" -> "%s"' % (src_module_name, trg_module_name)) + # print('alias_module "%s" -> "%s"' % (src_module_name, trg_module_name)) + assert isinstance(src_module_name, str), '"%s" not a module name.' % str(src_module_name) + assert isinstance(trg_module_name, str), '"%s" not a module name.' % str(trg_module_name) + + # If the target module has already been added to the graph as either a + # non-alias or as a different alias, raise an exception. + trg_module = self.findNode(trg_module_name) + if trg_module is not None and not ( + isinstance(trg_module, AliasNode) and + trg_module.identifier == src_module_name): + raise ValueError( + 'Target module "%s" already imported as "%s".' % ( + trg_module_name, trg_module)) + + # See findNode() for details. + self.lazynodes[trg_module_name] = Alias(src_module_name) + + + def add_module(self, module): + """ + Add the passed module node to the graph if not already added. + + If that module has a parent module or package with a previously added + node, this method also adds a reference from this module node to its + parent node and adds this module node to its parent node's namespace. + + This high-level method wraps the low-level `addNode()` method, but is + typically _only_ called by graph hooks adding runtime module nodes. For + all other node types, the `import_module()` method should be called. + + Parameters + ---------- + module : BaseModule + Graph node of the module to be added. + """ + self.msg(3, 'add_module', module) + + # If no node exists for this module, add such a node. + module_added = self.findNode(module.identifier) + if module_added is None: + self.addNode(module) + else: + assert module == module_added, 'New module %r != previous %r.' % (module, module_added) + + # If this module has a previously added parent, reference this module to + # its parent and add this module to its parent's namespace. + parent_name, _, module_basename = module.identifier.rpartition('.') + if parent_name: + parent = self.findNode(parent_name) + if parent is None: + self.msg(4, 'add_module parent not found:', parent_name) + else: + self.createReference(module, parent) + parent.add_submodule(module_basename, module) + + + def append_package_path(self, package_name, directory): + """ + Modulegraph does a good job at simulating Python's, but it can not + handle packagepath '__path__' modifications packages make at runtime. + + Therefore there is a mechanism whereby you can register extra paths + in this map for a package, and it will be honored. + + NOTE: This method has to be called before a package is resolved by + modulegraph. + + Parameters + ---------- + module : str + Fully-qualified module name. + directory : str + Absolute or relative path of the directory to append to the + '__path__' attribute. + """ + + paths = self._package_path_map.setdefault(package_name, []) + paths.append(directory) + + + def _safe_import_module( + self, module_partname, module_name, parent_module): + """ + Create a new graph node for the module with the passed name under the + parent package signified by the passed graph node _without_ raising + `ImportError` exceptions. + + If this module has already been imported, this module's existing graph + node will be returned; else if this module is importable, a new graph + node will be added for this module and returned; else this module is + unimportable, in which case `None` will be returned. Like the + `_safe_import_hook()` method, this method does _not_ raise + `ImportError` exceptions when this module is unimportable. + + Parameters + ---------- + module_partname : str + Unqualified name of the module to be imported (e.g., `text`). + module_name : str + Fully-qualified name of this module (e.g., `email.mime.text`). + parent_module : Package + Graph node of the previously imported parent module containing this + submodule _or_ `None` if this is a top-level module (i.e., + `module_name` contains no `.` delimiters). This parent module is + typically but _not_ always a package (e.g., the `os.path` submodule + contained by the `os` module). + + Returns + ---------- + Node + Graph node created for this module _or_ `None` if this module is + unimportable. + """ + self.msgin(3, "safe_import_module", module_partname, module_name, parent_module) + + # If this module has *NOT* already been imported, do so. + module = self.findNode(module_name) + if module is None: + # List of the absolute paths of all directories to be searched for + # this module. This effectively defaults to "sys.path". + search_dirs = None + + # If this module has a parent package... + if parent_module is not None: + # ...with a list of the absolute paths of all directories + # comprising this package, prefer that to "sys.path". + if parent_module.packagepath is not None: + search_dirs = parent_module.packagepath + # Else, something is horribly wrong. Return emptiness. + else: + self.msgout(3, "safe_import_module -> None (parent_parent.packagepath is None)") + return None + + try: + pathname, loader = self._find_module( + module_partname, search_dirs, parent_module) + except ImportError as exc: + self.msgout(3, "safe_import_module -> None (%r)" % exc) + return None + + (module, co) = self._load_module(module_name, pathname, loader) + if co is not None: + try: + if isinstance(co, ast.AST): + co_ast = co + co = compile(co_ast, pathname, 'exec', 0, True) + else: + co_ast = None + n = self._scan_code(module, co, co_ast) + self._process_imports(n) + + if self.replace_paths: + co = self._replace_paths_in_code(co) + module.code = co + except SyntaxError: + self.msg( + 1, "safe_import_module: SyntaxError in ", pathname, + ) + cls = InvalidSourceModule + module = self.createNode(cls, module_name) + + # If this is a submodule rather than top-level module... + if parent_module is not None: + self.msg(4, "safe_import_module create reference", module, "->", parent_module) + + # Add an edge from this submodule to its parent module. + self._updateReference( + module, parent_module, edge_data=DependencyInfo( + conditional=False, + fromlist=False, + function=False, + tryexcept=False, + )) + + # Add this submodule to its parent module. + parent_module.add_submodule(module_partname, module) + + # Return this module. + self.msgout(3, "safe_import_module ->", module) + return module + + def _load_module(self, fqname, pathname, loader): + from importlib._bootstrap_external import ExtensionFileLoader + self.msgin(2, "load_module", fqname, pathname, + loader.__class__.__name__) + partname = fqname.rpartition(".")[-1] + + if loader.is_package(partname): + is_nspkg = isinstance(loader, NAMESPACE_PACKAGE) + if is_nspkg: + pkgpath = loader.namespace_dirs[:] # copy for safety + else: + pkgpath = [] + + newname = _replacePackageMap.get(fqname) + if newname: + fqname = newname + ns_pkgpath = _namespace_package_path( + fqname, pkgpath or [], self.path) + + if (ns_pkgpath or pkgpath) and is_nspkg: + # this is a PEP-420 namespace package + m = self.createNode(NamespacePackage, fqname) + m.filename = '-' + m.packagepath = ns_pkgpath + else: + if isinstance(loader, ExtensionFileLoader): + m = self.createNode(ExtensionPackage, fqname) + else: + m = self.createNode(Package, fqname) + m.filename = pathname + # PEP-302-compliant loaders return the pathname of the + # `__init__`-file, not the packge directory. + assert os.path.basename(pathname).startswith('__init__.') + m.packagepath = [os.path.dirname(pathname)] + ns_pkgpath + + # As per comment at top of file, simulate runtime packagepath + # additions + m.packagepath = m.packagepath + self._package_path_map.get( + fqname, []) + + if isinstance(m, NamespacePackage): + return (m, None) + + co = None + if loader is BUILTIN_MODULE: + cls = BuiltinModule + elif isinstance(loader, ExtensionFileLoader): + cls = Extension + else: + src = loader.get_source(partname) + if src is not None: + try: + co = compile(src, pathname, 'exec', ast.PyCF_ONLY_AST, + True) + cls = SourceModule + if sys.version_info[:2] == (3, 5): + # In Python 3.5 some syntax problems with async + # functions are only reported when compiling to + # bytecode + compile(co, '-', 'exec', 0, True) + except SyntaxError: + co = None + cls = InvalidSourceModule + except Exception as exc: # FIXME: more specific? + cls = InvalidSourceModule + self.msg(2, "load_module: InvalidSourceModule", pathname, + exc) + else: + # no src available + try: + co = loader.get_code(partname) + cls = (CompiledModule if co is not None + else InvalidCompiledModule) + except Exception as exc: # FIXME: more specific? + self.msg(2, "load_module: InvalidCompiledModule, " + "Cannot load code", pathname, exc) + cls = InvalidCompiledModule + + m = self.createNode(cls, fqname) + m.filename = pathname + + self.msgout(2, "load_module ->", m) + return (m, co) + + def _safe_import_hook( + self, target_module_partname, source_module, target_attr_names, + level=DEFAULT_IMPORT_LEVEL, edge_attr=None): + """ + Import the module with the passed name and all parent packages of this + module from the previously imported caller module signified by the + passed graph node _without_ raising `ImportError` exceptions. + + This method wraps the lowel-level `_import_hook()` method. On catching + an `ImportError` exception raised by that method, this method creates + and adds a `MissingNode` instance describing the unimportable module to + the graph instead. + + Parameters + ---------- + target_module_partname : str + Partially-qualified name of the module to be imported. If `level` + is: + * `ABSOLUTE_OR_RELATIVE_IMPORT_LEVEL` (e.g., the Python 2 default) + or a positive integer (e.g., an explicit relative import), the + fully-qualified name of this module is the concatenation of the + fully-qualified name of the caller module's package and this + parameter. + * `ABSOLUTE_IMPORT_LEVEL` (e.g., the Python 3 default), this name + is already fully-qualified. + * A non-negative integer (e.g., `1`), this name is typically the + empty string. In this case, this is a "from"-style relative + import (e.g., "from . import bar") and the fully-qualified name + of this module is dynamically resolved by import machinery. + source_module : Node + Graph node for the previously imported **caller module** (i.e., + module containing the `import` statement triggering the call to + this method) _or_ `None` if this module is to be imported in a + "disconnected" manner. **Passing `None` is _not_ recommended.** + Doing so produces a disconnected graph in which the graph node + created for the module to be imported will be disconnected and + hence unreachable from all other nodes -- which frequently causes + subtle issues in external callers (e.g., PyInstaller, which + silently ignores unreachable nodes). + target_attr_names : list + List of the unqualified names of all submodules and attributes to + be imported via a `from`-style import statement from this target + module if any (e.g., the list `[encode_base64, encode_noop]` for + the import `from email.encoders import encode_base64, encode_noop`) + _or_ `None` otherwise. Ignored unless `source_module` is the graph + node of a package (i.e., is an instance of the `Package` class). + Why? Because: + * Consistency. The `_import_importable_package_submodules()` + method accepts a similar list applicable only to packages. + * Efficiency. Unlike packages, modules cannot physically contain + submodules. Hence, any target module imported via a `from`-style + import statement as an attribute from another target parent + module must itself have been imported in that target parent + module. The import statement responsible for that import must + already have been previously parsed by `ModuleGraph`, in which + case that target module will already be frozen by PyInstaller. + These imports are safely ignorable here. + level : int + Whether to perform an absolute or relative import. This parameter + corresponds exactly to the parameter of the same name accepted by + the `__import__()` built-in: "The default is -1 which indicates + both absolute and relative imports will be attempted. 0 means only + perform absolute imports. Positive values for level indicate the + number of parent directories to search relative to the directory of + the module calling `__import__()`." Defaults to -1 under Python 2 + and 0 under Python 3. Since this default depends on the major + version of the current Python interpreter, depending on this + default can result in unpredictable and non-portable behaviour. + Callers are strongly recommended to explicitly pass this parameter + rather than implicitly accept this default. + + Returns + ---------- + list + List of the graph nodes created for all modules explicitly imported + by this call, including the passed module and all submodules listed + in `target_attr_names` _but_ excluding all parent packages + implicitly imported by this call. If `target_attr_names` is either + `None` or the empty list, this is guaranteed to be a list of one + element: the graph node created for the passed module. As above, + `MissingNode` instances are created for all unimportable modules. + """ + self.msg(3, "_safe_import_hook", target_module_partname, source_module, target_attr_names, level) + + def is_swig_candidate(): + return (source_module is not None and + target_attr_names is None and + level == ABSOLUTE_IMPORT_LEVEL and + type(source_module) is SourceModule and + target_module_partname == + '_' + source_module.identifier.rpartition('.')[2] and + sys.version_info[0] == 3) + + def is_swig_wrapper(source_module): + # TODO Define a new function util.open_text_file() performing + # this logic, which is repeated numerous times in this module. + # FIXME: Actually, can't we just use the new compat.open() + # function to reliably open text files in a portable manner? + with open(source_module.filename, 'rb') as source_module_file: + encoding = util.guess_encoding(source_module_file) + with open(source_module.filename, _READ_MODE, encoding=encoding) \ + as source_module_file: + first_line = source_module_file.readline() + self.msg(5, 'SWIG wrapper candidate first line: %r' % (first_line)) + return "automatically generated by SWIG" in first_line + + + # List of the graph nodes created for all target modules both + # imported by and returned from this call, whose: + # + # * First element is the graph node for the core target module + # specified by the "target_module_partname" parameter. + # * Remaining elements are the graph nodes for all target submodules + # specified by the "target_attr_names" parameter. + target_modules = None + + # True if this is a Python 2-style implicit relative import of a + # SWIG-generated C extension. False if we checked and it is not SWIG. + # None if we haven't checked yet. + is_swig_import = None + + # Attempt to import this target module in the customary way. + try: + target_modules = self.import_hook( + target_module_partname, source_module, + target_attr_names=None, level=level, edge_attr=edge_attr) + # Failing that, defer to custom module importers handling non-standard + # import schemes (namely, SWIG). + except InvalidRelativeImportError: + self.msgout(2, "Invalid relative import", level, + target_module_partname, target_attr_names) + result = [] + for sub in target_attr_names or '*': + m = self.createNode(InvalidRelativeImport, + '.' * level + target_module_partname, sub) + self._updateReference(source_module, m, edge_data=edge_attr) + result.append(m) + return result + except ImportError as msg: + # If this is an absolute top-level import under Python 3 and if the + # name to be imported is the caller's name prefixed by "_", this + # could be a SWIG-generated Python 2-style implicit relative import. + # SWIG-generated files contain functions named swig_import_helper() + # importing dynamic libraries residing in the same directory. For + # example, a SWIG-generated caller module "csr.py" might resemble: + # + # # This file was automatically generated by SWIG (http://www.swig.org). + # ... + # def swig_import_helper(): + # ... + # try: + # fp, pathname, description = imp.find_module('_csr', + # [dirname(__file__)]) + # except ImportError: + # import _csr + # return _csr + # + # While there exists no reasonable means for modulegraph to parse + # the call to imp.find_module(), the subsequent implicit relative + # import is trivially parsable. This import is prohibited under + # Python 3, however, and thus parsed only if the caller's file is + # parsable plaintext (as indicated by a filetype of ".py") and the + # first line of this file is the above SWIG header comment. + # + # The constraint that this library's name be the caller's name + # prefixed by '_' is explicitly mandated by SWIG and thus a + # reliable indicator of "SWIG-ness". The SWIG documentation states: + # "When linking the module, the name of the output file has to match + # the name of the module prefixed by an underscore." + # + # Only source modules (e.g., ".py"-suffixed files) are SWIG import + # candidates. All other node types are safely ignorable. + if is_swig_candidate(): + self.msg( + 4, + 'SWIG import candidate (name=%r, caller=%r, level=%r)' % ( + target_module_partname, source_module, level)) + is_swig_import = is_swig_wrapper(source_module) + if is_swig_import: + # Convert this Python 2-compliant implicit relative + # import prohibited by Python 3 into a Python + # 3-compliant explicit relative "from"-style import for + # the duration of this function call by overwriting the + # original parameters passed to this call. + target_attr_names = [target_module_partname] + target_module_partname = '' + level = 1 + self.msg(2, + 'SWIG import (caller=%r, fromlist=%r, level=%r)' + % (source_module, target_attr_names, level)) + # Import this target SWIG C extension's package. + try: + target_modules = self.import_hook( + target_module_partname, source_module, + target_attr_names=None, + level=level, + edge_attr=edge_attr) + except ImportError as msg: + self.msg(2, "SWIG ImportError:", str(msg)) + + # If this module remains unimportable... + if target_modules is None: + self.msg(2, "ImportError:", str(msg)) + + # Add this module as a MissingModule node. + target_module = self.createNode( + MissingModule, + _path_from_importerror(msg, target_module_partname)) + self._updateReference( + source_module, target_module, edge_data=edge_attr) + + # Initialize this list to this node. + target_modules = [target_module] + + # Ensure that the above logic imported exactly one target module. + assert len(target_modules) == 1, ( + 'Expected import_hook() to' + 'return only one module but received: {}'.format(target_modules)) + + # Target module imported above. + target_module = target_modules[0] + + if isinstance(target_module, MissingModule) \ + and is_swig_import is None and is_swig_candidate() \ + and is_swig_wrapper(source_module): + # if this possible swig C module was previously imported from + # a python module other than its corresponding swig python + # module, then it may have been considered a MissingModule. + # Try to reimport it now. For details see pull-request #2578 + # and issue #1522. + # + # If this module was takes as a SWIG candidate above, but failed + # to import, this would be a MissingModule, too. Thus check if + # this was the case (is_swig_import would be not None) to avoid + # recursion error. If `is_swig_import` is None and we are still a + # swig candidate then that means we haven't properly imported this + # swig module yet so do that below. + # + # Remove the MissingModule node from the graph so that we can + # attempt a reimport and avoid collisions. This node should be + # fine to remove because the proper module will be imported and + # added to the graph in the next line (call to _safe_import_hook). + self.removeNode(target_module) + # Reimport the SWIG C module relative to the wrapper + target_modules = self._safe_import_hook( + target_module_partname, source_module, + target_attr_names=None, level=1, edge_attr=edge_attr) + # return the output regardless because it would just be + # duplicating the processing below + return target_modules + + if isinstance(edge_attr, DependencyInfo): + edge_attr = edge_attr._replace(fromlist=True) + + # If this is a "from"-style import *AND* this target module is a + # package, import all attributes listed by the "import" clause of this + # import that are submodules of this package. If this target module is + # *NOT* a package, these attributes are always ignorable globals (e.g., + # classes, variables) defined at the top level of this module. + # + # If this target module is a non-package, it could still contain + # importable submodules (e.g., the non-package `os` module containing + # the `os.path` submodule). In this case, these submodules are already + # imported by this target module's pure-Python code. Since our import + # scanner already detects these imports, these submodules need *NOT* be + # reimported here. (Doing so would be harmless but inefficient.) + if target_attr_names and isinstance(target_module, + (Package, AliasNode)): + # For the name of each attribute imported from this target package + # into this source module... + for target_submodule_partname in target_attr_names: + #FIXME: Is this optimization *REALLY* an optimization or at all + #necessary? The findNode() method called below should already + #be heavily optimized, in which case this optimization here is + #premature, senseless, and should be eliminated. + + # If this attribute is a previously imported submodule of this + # target module, optimize this edge case. + if target_module.is_submodule(target_submodule_partname): + # Graph node for this submodule. + target_submodule = target_module.get_submodule( + target_submodule_partname) + + #FIXME: What? Shouldn't "target_submodule" *ALWAYS* be + #non-None here? Assert this to be non-None instead. + if target_submodule is not None: + #FIXME: Why does duplication matter? List searches are + #mildly expensive. + + # If this submodule has not already been added to the + # list of submodules to be returned, do so. + if target_submodule not in target_modules: + self._updateReference( + source_module, + target_submodule, + edge_data=edge_attr) + target_modules.append(target_submodule) + continue + + # Fully-qualified name of this submodule. + target_submodule_name = ( + target_module.identifier + '.' + target_submodule_partname) + + # Graph node of this submodule if previously imported or None. + target_submodule = self.findNode(target_submodule_name) + + # If this submodule has not been imported, do so as if this + # submodule were the only attribute listed by the "import" + # clause of this import (e.g., as "from foo import bar" rather + # than "from foo import car, far, bar"). + if target_submodule is None: + # Attempt to import this submodule. + try: + # Ignore the list of graph nodes returned by this + # method. If both this submodule's package and this + # submodule are importable, this method returns a + # 2-element list whose second element is this + # submodule's graph node. However, if this submodule's + # package is importable but this submodule is not, + # this submodule is either: + # + # * An ignorable global attribute defined at the top + # level of this package's "__init__" submodule. In + # this case, this method returns a 1-element list + # without raising an exception. + # * A non-ignorable unimportable submodule. In this + # case, this method raises an "ImportError". + # + # While the first two cases are disambiguatable by the + # length of this list, doing so would render this code + # dependent on import_hook() details subject to change. + # Instead, call findNode() to decide the truthiness. + self.import_hook( + target_module_partname, source_module, + target_attr_names=[target_submodule_partname], + level=level, + edge_attr=edge_attr) + + # Graph node of this submodule imported by the prior + # call if importable or None otherwise. + target_submodule = self.findNode(target_submodule_name) + + # If this submodule does not exist, this *MUST* be an + # ignorable global attribute defined at the top level + # of this package's "__init__" submodule. + if target_submodule is None: + # Assert this to actually be the case. + assert target_module.is_global_attr( + target_submodule_partname), ( + 'No global named {} in {}.__init__'.format( + target_submodule_partname, + target_module.identifier)) + + # Skip this safely ignorable importation to the + # next attribute. See similar logic in the body of + # _import_importable_package_submodules(). + self.msg(4, '_safe_import_hook', 'ignoring imported non-module global', target_module.identifier, target_submodule_partname) + continue + + # If this is a SWIG C extension, instruct PyInstaller + # to freeze this extension under its unqualified rather + # than qualified name (e.g., as "_csr" rather than + # "scipy.sparse.sparsetools._csr"), permitting the + # implicit relative import in its parent SWIG module to + # successfully find this extension. + if is_swig_import: + # If a graph node with this name already exists, + # avoid collisions by emitting an error instead. + if self.findNode(target_submodule_partname): + self.msg( + 2, + 'SWIG import error: %r basename %r ' + 'already exists' % ( + target_submodule_name, + target_submodule_partname)) + else: + self.msg( + 4, + 'SWIG import renamed from %r to %r' % ( + target_submodule_name, + target_submodule_partname)) + target_submodule.identifier = ( + target_submodule_partname) + # If this submodule is unimportable, add a MissingModule. + except ImportError as msg: + self.msg(2, "ImportError:", str(msg)) + target_submodule = self.createNode( + MissingModule, target_submodule_name) + + # Add this submodule to its package. + target_module.add_submodule( + target_submodule_partname, target_submodule) + if target_submodule is not None: + self._updateReference( + target_module, target_submodule, edge_data=edge_attr) + self._updateReference( + source_module, target_submodule, edge_data=edge_attr) + + if target_submodule not in target_modules: + target_modules.append(target_submodule) + + # Return the list of all target modules imported by this call. + return target_modules + + + def _scan_code( + self, + module, + module_code_object, + module_code_object_ast=None): + """ + Parse and add all import statements from the passed code object of the + passed source module to this graph, recursively. + + **This method is at the root of all `ModuleGraph` recursion.** + Recursion begins here and ends when all import statements in all code + objects of all modules transitively imported by the source module + passed to the first call to this method have been added to the graph. + Specifically, this method: + + 1. If the passed `module_code_object_ast` parameter is non-`None`, + parses all import statements from this object. + 2. Else, parses all import statements from the passed + `module_code_object` parameter. + 1. For each such import statement: + 1. Adds to this `ModuleGraph` instance: + 1. Nodes for all target modules of these imports. + 1. Directed edges from this source module to these target + modules. + 2. Recursively calls this method with these target modules. + + Parameters + ---------- + module : Node + Graph node of the module to be parsed. + module_code_object : PyCodeObject + Code object providing this module's disassembled Python bytecode. + Ignored unless `module_code_object_ast` is `None`. + module_code_object_ast : optional[ast.AST] + Optional abstract syntax tree (AST) of this module if any or `None` + otherwise. Defaults to `None`, in which case the passed + `module_code_object` is parsed instead. + Returns + ---------- + module : Node + Graph node of the module to be parsed. + """ + + # For safety, guard against multiple scans of the same module by + # resetting this module's list of deferred target imports. While + # uncommon, this edge case can occur due to: + # + # * Dynamic package replacement via the replacePackage() function. For + # example, the real "_xmlplus" package dynamically replaces itself + # with the fake "xml" package into the "sys.modules" cache of all + # currently loaded modules at runtime. + module._deferred_imports = [] + + # Parse all imports from this module *BEFORE* adding these imports to + # the graph. If an AST is provided, parse that rather than this + # module's code object. + if module_code_object_ast is not None: + # Parse this module's AST for imports. + self._scan_ast(module, module_code_object_ast) + + # Parse this module's code object for all relevant non-imports + # (e.g., global variable declarations and undeclarations). + self._scan_bytecode( + module, module_code_object, is_scanning_imports=False) + # Else, parse this module's code object for imports. + else: + self._scan_bytecode( + module, module_code_object, is_scanning_imports=True) + + return module + + def _scan_ast(self, module, module_code_object_ast): + """ + Parse and add all import statements from the passed abstract syntax + tree (AST) of the passed source module to this graph, non-recursively. + + Parameters + ---------- + module : Node + Graph node of the module to be parsed. + module_code_object_ast : ast.AST + Abstract syntax tree (AST) of this module to be parsed. + """ + + visitor = _Visitor(self, module) + visitor.visit(module_code_object_ast) + + #FIXME: Optimize. Global attributes added by this method are tested by + #other methods *ONLY* for packages, implying this method should scan and + #handle opcodes pertaining to global attributes (e.g., + #"STORE_NAME", "DELETE_GLOBAL") only if the passed "module" + #object is an instance of the "Package" class. For all other module types, + #these opcodes should simply be ignored. + # + #After doing so, the "Node._global_attr_names" attribute and all methods + #using this attribute (e.g., Node.is_global()) should be moved from the + #"Node" superclass to the "Package" subclass. + def _scan_bytecode( + self, module, module_code_object, is_scanning_imports): + """ + Parse and add all import statements from the passed code object of the + passed source module to this graph, non-recursively. + + This method parses all reasonably parsable operations (i.e., operations + that are both syntactically and semantically parsable _without_ + requiring Turing-complete interpretation) directly or indirectly + involving module importation from this code object. This includes: + + * `IMPORT_NAME`, denoting an import statement. Ignored unless + the passed `is_scanning_imports` parameter is `True`. + * `STORE_NAME` and `STORE_GLOBAL`, denoting the + declaration of a global attribute (e.g., class, variable) in this + module. This method stores each such declaration for subsequent + lookup. While global attributes are usually irrelevant to import + parsing, they remain the only means of distinguishing erroneous + non-ignorable attempts to import non-existent submodules of a package + from successful ignorable attempts to import existing global + attributes of a package's `__init__` submodule (e.g., the `bar` in + `from foo import bar`, which is either a non-ignorable submodule of + `foo` or an ignorable global attribute of `foo.__init__`). + * `DELETE_NAME` and `DELETE_GLOBAL`, denoting the + undeclaration of a previously declared global attribute in this + module. + + Since `ModuleGraph` is _not_ intended to replicate the behaviour of a + full-featured Turing-complete Python interpreter, this method ignores + operations that are _not_ reasonably parsable from this code object -- + even those directly or indirectly involving module importation. This + includes: + + * `STORE_ATTR(namei)`, implementing `TOS.name = TOS1`. If `TOS` is the + name of a target module currently imported into the namespace of the + passed source module, this opcode would ideally be parsed to add that + global attribute to that target module. Since this addition only + conditionally occurs on the importation of this source module and + execution of the code branch in this module performing this addition, + however, that global _cannot_ be unconditionally added to that target + module. In short, only Turing-complete behaviour suffices. + * `DELETE_ATTR(namei)`, implementing `del TOS.name`. If `TOS` is the + name of a target module currently imported into the namespace of the + passed source module, this opcode would ideally be parsed to remove + that global attribute from that target module. Again, however, only + Turing-complete behaviour suffices. + + Parameters + ---------- + module : Node + Graph node of the module to be parsed. + module_code_object : PyCodeObject + Code object of the module to be parsed. + is_scanning_imports : bool + `True` only if this method is parsing import statements from + `IMPORT_NAME` opcodes. If `False`, no import statements will be + parsed. This parameter is typically: + * `True` when parsing this module's code object for such imports. + * `False` when parsing this module's abstract syntax tree (AST) + (rather than code object) for such imports. In this case, that + parsing will have already parsed import statements, which this + parsing must avoid repeating. + """ + level = None + fromlist = None + + # 'deque' is a list-like container with fast appends, pops on + # either end, and automatically discarding elements too much. + prev_insts = deque(maxlen=2) + for inst in util.iterate_instructions(module_code_object): + if not inst: + continue + # If this is an import statement originating from this module, + # parse this import. + # + # Note that the related "IMPORT_FROM" opcode need *NOT* be parsed. + # "IMPORT_NAME" suffices. For further details, see + # http://probablyprogramming.com/2008/04/14/python-import_name + if inst.opname == 'IMPORT_NAME': + # If this method is ignoring import statements, skip to the + # next opcode. + if not is_scanning_imports: + continue + + assert prev_insts[-2].opname == 'LOAD_CONST' + assert prev_insts[-1].opname == 'LOAD_CONST' + + # Python >=2.5: LOAD_CONST flags, LOAD_CONST names, IMPORT_NAME name + level = prev_insts[-2].argval + fromlist = prev_insts[-1].argval + + assert fromlist is None or type(fromlist) is tuple + target_module_partname = inst.argval + + #FIXME: The exact same logic appears in _collect_import(), + #which isn't particularly helpful. Instead, defer this logic + #until later by: + # + #* Refactor the "_deferred_imports" list to contain 2-tuples + # "(_safe_import_hook_args, _safe_import_hook_kwargs)" rather + # than 3-tuples "(have_star, _safe_import_hook_args, + # _safe_import_hook_kwargs)". + #* Stop prepending these tuples by a "have_star" boolean both + # here, in _collect_import(), and in _process_imports(). + #* Shift the logic below to _process_imports(). + #* Remove the same logic from _collect_import(). + have_star = False + if fromlist is not None: + fromlist = uniq(fromlist) + if '*' in fromlist: + fromlist.remove('*') + have_star = True + + # Record this import as originating from this module for + # subsequent handling by the _process_imports() method. + module._deferred_imports.append(( + have_star, + (target_module_partname, module, fromlist, level), + {} + )) + + elif inst.opname in ('STORE_NAME', 'STORE_GLOBAL'): + # If this is the declaration of a global attribute (e.g., + # class, variable) in this module, store this declaration for + # subsequent lookup. See method docstring for further details. + # + # Global attributes are usually irrelevant to import parsing, but + # remain the only means of distinguishing erroneous non-ignorable + # attempts to import non-existent submodules of a package from + # successful ignorable attempts to import existing global + # attributes of a package's "__init__" submodule (e.g., the "bar" + # in "from foo import bar", which is either a non-ignorable + # submodule of "foo" or an ignorable global attribute of + # "foo.__init__"). + name = inst.argval + module.add_global_attr(name) + + elif inst.opname in ('DELETE_NAME', 'DELETE_GLOBAL'): + # If this is the undeclaration of a previously declared global + # attribute (e.g., class, variable) in this module, remove that + # declaration to prevent subsequent lookup. See method docstring + # for further details. + name = inst.argval + module.remove_global_attr_if_found(name) + + prev_insts.append(inst) + + + def _process_imports(self, source_module): + """ + Graph all target modules whose importations were previously parsed from + the passed source module by a prior call to the `_scan_code()` method + and methods call by that method (e.g., `_scan_ast()`, + `_scan_bytecode()`, `_scan_bytecode_stores()`). + + Parameters + ---------- + source_module : Node + Graph node of the source module to graph target imports for. + """ + + # If this source module imported no target modules, noop. + if not source_module._deferred_imports: + return + + # For each target module imported by this source module... + for have_star, import_info, kwargs in source_module._deferred_imports: + # Graph node of the target module specified by the "from" portion + # of this "from"-style star import (e.g., an import resembling + # "from {target_module_name} import *") or ignored otherwise. + target_module = self._safe_import_hook(*import_info, **kwargs)[0] + + # If this is a "from"-style star import, process this import. + if have_star: + #FIXME: Sadly, the current approach to importing attributes + #from "from"-style star imports is... simplistic. This should + #be revised as follows. If this target module is: + # + #* A package: + # * Whose "__init__" submodule defines the "__all__" global + # attribute, only attributes listed by this attribute should + # be imported. + # * Else, *NO* attributes should be imported. + #* A non-package: + # * Defining the "__all__" global attribute, only attributes + # listed by this attribute should be imported. + # * Else, only public attributes whose names are *NOT* + # prefixed by "_" should be imported. + source_module.add_global_attrs_from_module(target_module) + + source_module._starimported_ignored_module_names.update( + target_module._starimported_ignored_module_names) + + # If this target module has no code object and hence is + # unparsable, record its name for posterity. + if target_module.code is None: + target_module_name = import_info[0] + source_module._starimported_ignored_module_names.add( + target_module_name) + + # For safety, prevent these imports from being reprocessed. + source_module._deferred_imports = None + + + def _find_module(self, name, path, parent=None): + """ + 3-tuple describing the physical location of the module with the passed + name if this module is physically findable _or_ raise `ImportError`. + + This high-level method wraps the low-level `modulegraph.find_module()` + function with additional support for graph-based module caching. + + Parameters + ---------- + name : str + Fully-qualified name of the Python module to be found. + path : list + List of the absolute paths of all directories to search for this + module _or_ `None` if the default path list `self.path` is to be + searched. + parent : Node + Package containing this module if this module is a submodule of a + package _or_ `None` if this is a top-level module. + + Returns + ---------- + (filename, loader) + See `modulegraph._find_module()` for details. + + Raises + ---------- + ImportError + If this module is _not_ found. + """ + + if parent is not None: + # assert path is not None + fullname = parent.identifier + '.' + name + else: + fullname = name + + node = self.findNode(fullname) + if node is not None: + self.msg(3, "find_module: already included?", node) + raise ImportError(name) + + if path is None: + if name in sys.builtin_module_names: + return (None, BUILTIN_MODULE) + + path = self.path + + return self._find_module_path(fullname, name, path) + + + def _find_module_path(self, fullname, module_name, search_dirs): + """ + 3-tuple describing the physical location of the module with the passed + name if this module is physically findable _or_ raise `ImportError`. + + This low-level function is a variant on the standard `imp.find_module()` + function with additional support for: + + * Multiple search paths. The passed list of absolute paths will be + iteratively searched for the first directory containing a file + corresponding to this module. + * Compressed (e.g., zipped) packages. + + For efficiency, the higher level `ModuleGraph._find_module()` method + wraps this function with support for module caching. + + Parameters + ---------- + module_name : str + Fully-qualified name of the module to be found. + search_dirs : list + List of the absolute paths of all directories to search for this + module (in order). Searching will halt at the first directory + containing this module. + + Returns + ---------- + (filename, loader) + 2-tuple describing the physical location of this module, where: + * `filename` is the absolute path of this file. + * `loader` is the import loader. + In case of a namespace package, this is a NAMESPACE_PACKAGE + instance + + Raises + ---------- + ImportError + If this module is _not_ found. + """ + self.msgin(4, "_find_module_path <-", fullname, search_dirs) + + # Top-level 2-tuple to be returned. + path_data = None + + # List of the absolute paths of all directories comprising the + # namespace package to which this module belongs if any. + namespace_dirs = [] + + try: + for search_dir in search_dirs: + # PEP 302-compliant importer making loaders for this directory. + importer = pkgutil.get_importer(search_dir) + + # If this directory is not importable, continue. + if importer is None: + # self.msg(4, "_find_module_path importer not found", search_dir) + continue + + # Get the PEP 302-compliant loader object loading this module. + # + # If this importer defines the PEP 302-compliant find_loader() + # method, prefer that. + if hasattr(importer, 'find_loader'): + loader, loader_namespace_dirs = importer.find_loader( + module_name) + namespace_dirs.extend(loader_namespace_dirs) + # Else if this importer defines the Python 2-specific + # find_module() method, fall back to that. Despite the method + # name, this method returns a loader rather than a module. + elif hasattr(importer, 'find_module'): + loader = importer.find_module(module_name) + # Else, raise an exception. + else: + raise ImportError( + "Module %r importer %r loader unobtainable" % (module_name, importer)) + + # If this module is not loadable from this directory, continue. + if loader is None: + # self.msg(4, "_find_module_path loader not found", search_dir) + continue + + # Absolute path of this module. If this module resides in a + # compressed archive, this is the absolute path of this module + # after extracting this module from that archive and hence + # should not exist; else, this path should typically exist. + pathname = None + + # If this loader defines the PEP 302-compliant get_filename() + # method, preferably call that method first. Most if not all + # loaders (including zipimporter objects) define this method. + if hasattr(loader, 'get_filename'): + pathname = loader.get_filename(module_name) + # Else if this loader provides a "path" attribute, defer to that. + elif hasattr(loader, 'path'): + pathname = loader.path + # Else, raise an exception. + else: + raise ImportError( + "Module %r loader %r path unobtainable" % (module_name, loader)) + + # If no path was found, this is probably a namespace package. In + # such case, continue collecting namespace directories. + if pathname is None: + self.msg(4, "_find_module_path path not found", pathname) + continue + + # Return such metadata. + path_data = (pathname, loader) + break + # Else if this is a namespace package, return such metadata. + else: + if namespace_dirs: + path_data = (namespace_dirs[0], + NAMESPACE_PACKAGE(namespace_dirs)) + except UnicodeDecodeError as exc: + self.msgout(1, "_find_module_path -> unicode error", exc) + # Ensure that exceptions are logged, as this function is typically + # called by the import_module() method which squelches ImportErrors. + except Exception as exc: + self.msgout(4, "_find_module_path -> exception", exc) + raise + + # If this module was not found, raise an exception. + self.msgout(4, "_find_module_path ->", path_data) + if path_data is None: + raise ImportError("No module named " + repr(module_name)) + + return path_data + + + def create_xref(self, out=None): + global header, footer, entry, contpl, contpl_linked, imports + if out is None: + out = sys.stdout + scripts = [] + mods = [] + for mod in self.flatten(): + name = os.path.basename(mod.identifier) + if isinstance(mod, Script): + scripts.append((name, mod)) + else: + mods.append((name, mod)) + scripts.sort() + mods.sort() + scriptnames = [sn for sn, m in scripts] + scripts.extend(mods) + mods = scripts + + title = "modulegraph cross reference for " + ', '.join(scriptnames) + print(header % {"TITLE": title}, file=out) + + def sorted_namelist(mods): + lst = [os.path.basename(mod.identifier) for mod in mods if mod] + lst.sort() + return lst + for name, m in mods: + content = "" + if isinstance(m, BuiltinModule): + content = contpl % {"NAME": name, + "TYPE": "(builtin module)"} + elif isinstance(m, Extension): + content = contpl % {"NAME": name, + "TYPE": "%s" % m.filename} + else: + url = pathname2url(m.filename or "") + content = contpl_linked % {"NAME": name, "URL": url, + 'TYPE': m.__class__.__name__} + oute, ince = map(sorted_namelist, self.get_edges(m)) + if oute: + links = [] + for n in oute: + links.append(""" %s\n""" % (n, n)) + # #8226 = bullet-point; can't use html-entities since the + # test-suite uses xml.etree.ElementTree.XMLParser, which + # does't supprot them. + links = " • ".join(links) + content += imports % {"HEAD": "imports", "LINKS": links} + if ince: + links = [] + for n in ince: + links.append(""" %s\n""" % (n, n)) + # #8226 = bullet-point; can't use html-entities since the + # test-suite uses xml.etree.ElementTree.XMLParser, which + # does't supprot them. + links = " • ".join(links) + content += imports % {"HEAD": "imported by", "LINKS": links} + print(entry % {"NAME": name, "CONTENT": content}, file=out) + print(footer, file=out) + + def itergraphreport(self, name='G', flatpackages=()): + # XXX: Can this be implemented using Dot()? + nodes = list(map(self.graph.describe_node, self.graph.iterdfs(self))) + describe_edge = self.graph.describe_edge + edges = deque() + packagenodes = set() + packageidents = {} + nodetoident = {} + inpackages = {} + mainedges = set() + + # XXX - implement + flatpackages = dict(flatpackages) + + def nodevisitor(node, data, outgoing, incoming): + if not isinstance(data, Node): + return {'label': str(node)} + #if isinstance(d, (ExcludedModule, MissingModule, BadModule)): + # return None + s = ' ' + type(data).__name__ + for i, v in enumerate(data.infoTuple()[:1], 1): + s += '| %s' % (i, v) + return {'label': s, 'shape': 'record'} + + + def edgevisitor(edge, data, head, tail): + # XXX: This method nonsense, the edge + # data is never initialized. + if data == 'orphan': + return {'style': 'dashed'} + elif data == 'pkgref': + return {'style': 'dotted'} + return {} + + yield 'digraph %s {\ncharset="UTF-8";\n' % (name,) + attr = dict(rankdir='LR', concentrate='true') + cpatt = '%s="%s"' + for item in attr.items(): + yield '\t%s;\n' % (cpatt % item,) + + # find all packages (subgraphs) + for (node, data, outgoing, incoming) in nodes: + nodetoident[node] = getattr(data, 'identifier', None) + if isinstance(data, Package): + packageidents[data.identifier] = node + inpackages[node] = set([node]) + packagenodes.add(node) + + # create sets for subgraph, write out descriptions + for (node, data, outgoing, incoming) in nodes: + # update edges + for edge in (describe_edge(e) for e in outgoing): + edges.append(edge) + + # describe node + yield '\t"%s" [%s];\n' % ( + node, + ','.join([ + (cpatt % item) for item in + nodevisitor(node, data, outgoing, incoming).items() + ]), + ) + + inside = inpackages.get(node) + if inside is None: + inside = inpackages[node] = set() + ident = nodetoident[node] + if ident is None: + continue + pkgnode = packageidents.get(ident[:ident.rfind('.')]) + if pkgnode is not None: + inside.add(pkgnode) + + graph = [] + subgraphs = {} + for key in packagenodes: + subgraphs[key] = [] + + while edges: + edge, data, head, tail = edges.popleft() + if ((head, tail)) in mainedges: + continue + mainedges.add((head, tail)) + tailpkgs = inpackages[tail] + common = inpackages[head] & tailpkgs + if not common and tailpkgs: + usepkgs = sorted(tailpkgs) + if len(usepkgs) != 1 or usepkgs[0] != tail: + edges.append((edge, data, head, usepkgs[0])) + edges.append((edge, 'pkgref', usepkgs[-1], tail)) + continue + if common: + common = common.pop() + if tail == common: + edges.append((edge, data, tail, head)) + elif head == common: + subgraphs[common].append((edge, 'pkgref', head, tail)) + else: + edges.append((edge, data, common, head)) + edges.append((edge, data, common, tail)) + + else: + graph.append((edge, data, head, tail)) + + def do_graph(edges, tabs): + edgestr = tabs + '"%s" -> "%s" [%s];\n' + # describe edge + for (edge, data, head, tail) in edges: + attribs = edgevisitor(edge, data, head, tail) + yield edgestr % ( + head, + tail, + ','.join([(cpatt % item) for item in attribs.items()]), + ) + + for g, edges in subgraphs.items(): + yield '\tsubgraph "cluster_%s" {\n' % (g,) + yield '\t\tlabel="%s";\n' % (nodetoident[g],) + for s in do_graph(edges, '\t\t'): + yield s + yield '\t}\n' + + for s in do_graph(graph, '\t'): + yield s + + yield '}\n' + + def graphreport(self, fileobj=None, flatpackages=()): + if fileobj is None: + fileobj = sys.stdout + fileobj.writelines(self.itergraphreport(flatpackages=flatpackages)) + + def report(self): + """Print a report to stdout, listing the found modules with their + paths, as well as modules that are missing, or seem to be missing. + """ + print() + print("%-15s %-25s %s" % ("Class", "Name", "File")) + print("%-15s %-25s %s" % ("-----", "----", "----")) + for m in sorted(self.flatten(), key=lambda n: n.identifier): + print("%-15s %-25s %s" % (type(m).__name__, m.identifier, m.filename or "")) + + def _replace_paths_in_code(self, co): + new_filename = original_filename = os.path.normpath(co.co_filename) + for f, r in self.replace_paths: + f = os.path.join(f, '') + r = os.path.join(r, '') + if original_filename.startswith(f): + new_filename = r + original_filename[len(f):] + break + + else: + return co + + consts = list(co.co_consts) + for i in range(len(consts)): + if isinstance(consts[i], type(co)): + consts[i] = self._replace_paths_in_code(consts[i]) + + code_func = type(co) + + if hasattr(co, 'replace'): # is_py38 + return co.replace(co_consts=tuple(consts), + co_filename=new_filename) + elif hasattr(co, 'co_kwonlyargcount'): + return code_func( + co.co_argcount, co.co_kwonlyargcount, co.co_nlocals, + co.co_stacksize, co.co_flags, co.co_code, + tuple(consts), co.co_names, co.co_varnames, + new_filename, co.co_name, co.co_firstlineno, + co.co_lnotab, co.co_freevars, co.co_cellvars) + else: + return code_func( + co.co_argcount, co.co_nlocals, co.co_stacksize, + co.co_flags, co.co_code, tuple(consts), co.co_names, + co.co_varnames, new_filename, co.co_name, + co.co_firstlineno, co.co_lnotab, + co.co_freevars, co.co_cellvars) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/util.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/util.py new file mode 100644 index 0000000000000000000000000000000000000000..0faf49304b2e45d3aa8481173ba30ef3422c8989 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/util.py @@ -0,0 +1,147 @@ + +# Filter DeprecationWarnings until the code has been revised +import warnings +warnings.filterwarnings("ignore", "the imp module is deprecated in") +warnings.filterwarnings("ignore", "imp_walk will be removed in a future") + +import os +import imp +import sys +import re +import marshal +import warnings +import inspect + +try: + unicode +except NameError: + unicode = str + +from ._compat import StringIO, BytesIO, get_instructions, _READ_MODE + + +def imp_find_module(name, path=None): + """ + same as imp.find_module, but handles dotted names + """ + names = name.split('.') + if path is not None: + if isinstance(path, (str, unicode)): + path = [os.path.realpath(path)] + for name in names: + result = imp.find_module(name, path) + if result[0] is not None: + result[0].close() + path = [result[1]] + return result + + +def _check_importer_for_path(name, path_item): + try: + importer = sys.path_importer_cache[path_item] + except KeyError: + for path_hook in sys.path_hooks: + try: + importer = path_hook(path_item) + break + except ImportError: + pass + else: + importer = None + sys.path_importer_cache.setdefault(path_item, importer) + + if importer is None: + try: + return imp.find_module(name, [path_item]) + except ImportError: + return None + return importer.find_module(name) + + +def imp_walk(name): + """ + yields namepart, tuple_or_importer for each path item + + raise ImportError if a name can not be found. + """ + warnings.warn( + "imp_walk will be removed in a future version", + DeprecationWarning) + + if name in sys.builtin_module_names: + yield name, (None, None, ("", "", imp.C_BUILTIN)) + return + paths = sys.path + res = None + for namepart in name.split('.'): + for path_item in paths: + res = _check_importer_for_path(namepart, path_item) + if hasattr(res, 'load_module'): + if res.path.endswith('.py') or res.path.endswith('.pyw'): + fp = StringIO(res.get_source(namepart)) + res = (fp, res.path, ('.py', _READ_MODE, imp.PY_SOURCE)) + elif res.path.endswith('.pyc') or res.path.endswith('.pyo'): + co = res.get_code(namepart) + fp = BytesIO( + imp.get_magic() + b'\0\0\0\0' + marshal.dumps(co)) + res = (fp, res.path, ('.pyc', 'rb', imp.PY_COMPILED)) + + else: + res = ( + None, + res.path, + ( + os.path.splitext(res.path)[-1], + 'rb', + imp.C_EXTENSION + ) + ) + + break + elif isinstance(res, tuple): + break + else: + break + + yield namepart, res + paths = [os.path.join(path_item, namepart)] + else: + return + + raise ImportError('No module named %s' % (name,)) + + +cookie_re = re.compile(br"coding[:=]\s*([-\w.]+)") +if sys.version_info[0] == 2: + default_encoding = 'ascii' +else: + default_encoding = 'utf-8' + + +def guess_encoding(fp): + + for i in range(2): + ln = fp.readline() + + m = cookie_re.search(ln) + if m is not None: + return m.group(1).decode('ascii') + + return default_encoding + + +def iterate_instructions(code_object): + """Delivers the byte-code instructions as a continuous stream. + + Yields `dis.Instruction`. After each code-block (`co_code`), `None` is + yielded to mark the end of the block and to interrupt the steam. + """ + yield from get_instructions(code_object) + + yield None + + # For each constant in this code object that is itself a code object, + # parse this constant in the same manner. + for constant in code_object.co_consts: + if inspect.iscode(constant): + yield from iterate_instructions(constant) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/zipio.py b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/zipio.py new file mode 100644 index 0000000000000000000000000000000000000000..732bc604a039ca8c7f48d1ab78e85833448b1f38 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/lib/modulegraph/zipio.py @@ -0,0 +1,418 @@ +""" +A helper module that can work with paths +that can refer to data inside a zipfile + +XXX: Need to determine if isdir("zipfile.zip") +should return True or False. Currently returns +True, but that might do the wrong thing with +data-files that are zipfiles. +""" +import os as _os +import zipfile as _zipfile +import errno as _errno +import time as _time +import sys as _sys +import stat as _stat + +_DFLT_DIR_MODE = ( + _stat.S_IXOTH + | _stat.S_IXGRP + | _stat.S_IXUSR + | _stat.S_IROTH + | _stat.S_IRGRP + | _stat.S_IRUSR) + +_DFLT_FILE_MODE = ( + _stat.S_IROTH + | _stat.S_IRGRP + | _stat.S_IRUSR) + + +if _sys.version_info[0] == 2: + from StringIO import StringIO as _BaseStringIO + from StringIO import StringIO as _BaseBytesIO + + class _StringIO (_BaseStringIO): + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + return False + + class _BytesIO (_BaseBytesIO): + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.close() + return False + +else: + from io import StringIO as _StringIO + from io import BytesIO as _BytesIO + + +def _locate(path): + full_path = path + if _os.path.exists(path): + return path, None + + else: + rest = [] + root = _os.path.splitdrive(path) + while path and path != root: + path, bn = _os.path.split(path) + rest.append(bn) + if _os.path.exists(path): + break + + if path == root: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + if not _os.path.isfile(path): + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + rest.reverse() + return path, '/'.join(rest).strip('/') + + +_open = open + + +def open(path, mode='r'): + if 'w' in mode or 'a' in mode: + raise IOError( + _errno.EINVAL, path, "Write access not supported") + elif 'r+' in mode: + raise IOError( + _errno.EINVAL, path, "Write access not supported") + + full_path = path + path, rest = _locate(path) + if not rest: + return _open(path, mode) + + else: + try: + zf = _zipfile.ZipFile(path, 'r') + + except _zipfile.error: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + try: + data = zf.read(rest) + except (_zipfile.error, KeyError): + zf.close() + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + zf.close() + + if mode == 'rb': + return _BytesIO(data) + + else: + if _sys.version_info[0] == 3: + data = data.decode('ascii') + + return _StringIO(data) + + +def listdir(path): + full_path = path + path, rest = _locate(path) + if not rest and not _os.path.isfile(path): + return _os.listdir(path) + + else: + try: + zf = _zipfile.ZipFile(path, 'r') + + except _zipfile.error: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + result = set() + seen = False + try: + for nm in zf.namelist(): + if rest is None: + seen = True + value = nm.split('/')[0] + if value: + result.add(value) + + elif nm.startswith(rest): + if nm == rest: + seen = True + value = '' + pass + elif nm[len(rest)] == '/': + seen = True + value = nm[len(rest)+1:].split('/')[0] + else: + value = None + + if value: + result.add(value) + except _zipfile.error: + zf.close() + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + zf.close() + + if not seen: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + return list(result) + + +def isfile(path): + full_path = path + path, rest = _locate(path) + if not rest: + ok = _os.path.isfile(path) + if ok: + try: + zf = _zipfile.ZipFile(path, 'r') + return False + except (_zipfile.error, IOError): + return True + return False + + zf = None + try: + zf = _zipfile.ZipFile(path, 'r') + zf.getinfo(rest) + zf.close() + return True + except (KeyError, _zipfile.error): + if zf is not None: + zf.close() + + # Check if this is a directory + try: + zf.getinfo(rest + '/') + except KeyError: + pass + else: + return False + + rest = rest + '/' + for nm in zf.namelist(): + if nm.startswith(rest): + # Directory + return False + + # No trace in zipfile + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + +def isdir(path): + full_path = path + path, rest = _locate(path) + if not rest: + ok = _os.path.isdir(path) + if not ok: + try: + zf = _zipfile.ZipFile(path, 'r') + except (_zipfile.error, IOError): + return False + return True + return True + + zf = None + try: + try: + zf = _zipfile.ZipFile(path) + except _zipfile.error: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + try: + zf.getinfo(rest) + except KeyError: + pass + else: + # File found + return False + + rest = rest + '/' + try: + zf.getinfo(rest) + except KeyError: + pass + else: + # Directory entry found + return True + + for nm in zf.namelist(): + if nm.startswith(rest): + return True + + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + finally: + if zf is not None: + zf.close() + + +def islink(path): + full_path = path + path, rest = _locate(path) + if not rest: + return _os.path.islink(path) + + try: + zf = _zipfile.ZipFile(path) + except _zipfile.error: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + try: + try: + zf.getinfo(rest) + except KeyError: + pass + else: + # File + return False + + rest += '/' + try: + zf.getinfo(rest) + except KeyError: + pass + else: + # Directory + return False + + for nm in zf.namelist(): + if nm.startswith(rest): + # Directory without listing + return False + + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + finally: + zf.close() + + +def readlink(path): + full_path = path + path, rest = _locate(path) + if rest: + # No symlinks inside zipfiles + raise OSError( + _errno.ENOENT, full_path, + "No such file or directory") + + return _os.readlink(path) + + +def getmode(path): + full_path = path + path, rest = _locate(path) + if not rest: + return _stat.S_IMODE(_os.stat(path).st_mode) + + zf = None + try: + zf = _zipfile.ZipFile(path) + info = None + + try: + info = zf.getinfo(rest) + except KeyError: + pass + + if info is None: + try: + info = zf.getinfo(rest + '/') + except KeyError: + pass + + if info is None: + rest = rest + '/' + for nm in zf.namelist(): + if nm.startswith(rest): + break + else: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + # Directory exists, but has no entry of its own. + return _DFLT_DIR_MODE + + # The mode is stored without file-type in external_attr. + if (info.external_attr >> 16) != 0: + return _stat.S_IMODE(info.external_attr >> 16) + else: + return _DFLT_FILE_MODE + + finally: + if zf is not None: + zf.close() + + +def getmtime(path): + full_path = path + path, rest = _locate(path) + if not rest: + return _os.path.getmtime(path) + + zf = None + try: + zf = _zipfile.ZipFile(path) + info = None + + try: + info = zf.getinfo(rest) + except KeyError: + pass + + if info is None: + try: + info = zf.getinfo(rest + '/') + except KeyError: + pass + + if info is None: + rest = rest + '/' + for nm in zf.namelist(): + if nm.startswith(rest): + break + else: + raise IOError( + _errno.ENOENT, full_path, + "No such file or directory") + + # Directory exists, but has no entry of its + # own, fake mtime by using the timestamp of + # the zipfile itself. + return _os.path.getmtime(path) + + return _time.mktime(info.date_time + (0, 0, -1)) + + finally: + if zf is not None: + zf.close() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/loader/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/loader/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/loader/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyiboot01_bootstrap.py b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyiboot01_bootstrap.py new file mode 100644 index 0000000000000000000000000000000000000000..0cd6d089799ac24b7a015d722e017a1d3899f49b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyiboot01_bootstrap.py @@ -0,0 +1,211 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +### Start bootstrap process +# Only python built-in modules can be used. + +import sys +import pyimod03_importers + +# Extend Python import machinery by adding PEP302 importers to sys.meta_path. +pyimod03_importers.install() + + +### Bootstrap process is complete. +# We can use other python modules (e.g. os) + + +import os + + +# Let other python modules know that the code is running in frozen mode. +if not hasattr(sys, 'frozen'): + sys.frozen = True + +# sys._MEIPASS is now set in the bootloader. Hooray. + + +# Python 3 C-API function Py_SetPath() resets sys.prefix to empty string. +# Python 2 was using PYTHONHOME for sys.prefix. Let's do the same for Python 3. +sys.prefix = sys._MEIPASS +sys.exec_prefix = sys.prefix + + +# Python 3.3+ defines also sys.base_prefix. Let's set them too. +sys.base_prefix = sys.prefix +sys.base_exec_prefix = sys.exec_prefix + + +# Some packages behaves differently when running inside virtual environment. +# E.g. IPython tries to append path VIRTUAL_ENV to sys.path. +# For the frozen app we want to prevent this behavior. +VIRTENV = 'VIRTUAL_ENV' +if VIRTENV in os.environ: + # On some platforms (e.g. AIX) 'os.unsetenv()' is not available and then + # deleting the var from os.environ does not delete it from the environment. + os.environ[VIRTENV] = '' + del os.environ[VIRTENV] + + +# Ensure sys.path contains absolute paths. Otherwise import of other python +# modules will fail when current working directory is changed by frozen +# application. +python_path = [] +for pth in sys.path: + python_path.append(os.path.abspath(pth)) + sys.path = python_path + + +# Implement workaround for prints in non-console mode. In non-console mode +# (with "pythonw"), print randomly fails with "[errno 9] Bad file descriptor" +# when the printed text is flushed (eg: buffer full); this is because the +# sys.stdout object is bound to an invalid file descriptor. +# Python 3000 has a fix for it (http://bugs.python.org/issue1415), but we +# feel that a workaround in PyInstaller is a good thing since most people +# found this problem for the first time with PyInstaller as they don't +# usually run their code with "pythonw" (and it's hard to debug anyway). +class NullWriter: + softspace = 0 + encoding = 'UTF-8' + + def write(*args): + pass + + def flush(*args): + pass + + # Some packages are checking if stdout/stderr is available. + # e.g. youtube-dl for details see #1883 + def isatty(self): + return False + + +# sys.stdout/err is None in GUI mode on Windows. +if sys.stdout is None: + sys.stdout = NullWriter() +if sys.stderr is None: + sys.stderr = NullWriter() + + +# At least on Windows, Python seems to hook up the codecs on this +# import, so it's not enough to just package up all the encodings. +# +# It was also reported that without 'encodings' module the frozen executable +# will fail to load in some configurations: +# +# http://www.pyinstaller.org/ticket/651 +# +# Import 'encodings' module in a run-time hook is not enough since some +# run-time hooks require this module and the order of running code from +# from run-time hooks is not defined. +try: + import encodings +except ImportError: + pass + + +# In the Python interpreter 'warnings' module is imported when 'sys.warnoptions' +# is not empty. Mimic this behavior in PyInstaller. +if sys.warnoptions: + import warnings + +try: + import ctypes + import os + from ctypes import LibraryLoader, DEFAULT_MODE + + def _frozen_name(name): + if name: + frozen_name = os.path.join(sys._MEIPASS, os.path.basename(name)) + if os.path.exists(frozen_name) and not os.path.isdir(frozen_name): + name = frozen_name + return name + + class PyInstallerImportError(OSError): + def __init__(self, name): + self.msg = ("Failed to load dynlib/dll %r. " + "Most probably this dynlib/dll was not found " + "when the application was frozen.") % name + self.args = (self.msg,) + + class PyInstallerCDLL(ctypes.CDLL): + def __init__(self, name, *args, **kwargs): + name = _frozen_name(name) + try: + super(PyInstallerCDLL, self).__init__(name, *args, **kwargs) + except Exception as base_error: + raise PyInstallerImportError(name) from base_error + + ctypes.CDLL = PyInstallerCDLL + ctypes.cdll = LibraryLoader(PyInstallerCDLL) + + class PyInstallerPyDLL(ctypes.PyDLL): + def __init__(self, name, *args, **kwargs): + name = _frozen_name(name) + try: + super(PyInstallerPyDLL, self).__init__(name, *args, **kwargs) + except Exception as base_error: + raise PyInstallerImportError(name) from base_error + + ctypes.PyDLL = PyInstallerPyDLL + ctypes.pydll = LibraryLoader(PyInstallerPyDLL) + + if sys.platform.startswith('win'): + class PyInstallerWinDLL(ctypes.WinDLL): + def __init__(self, name,*args, **kwargs): + name = _frozen_name(name) + try: + super(PyInstallerWinDLL, self).__init__(name, *args, **kwargs) + except Exception as base_error: + raise PyInstallerImportError(name) from base_error + + ctypes.WinDLL = PyInstallerWinDLL + ctypes.windll = LibraryLoader(PyInstallerWinDLL) + + class PyInstallerOleDLL(ctypes.OleDLL): + def __init__(self, name,*args, **kwargs): + name = _frozen_name(name) + try: + super(PyInstallerOleDLL, self).__init__(name, *args, **kwargs) + except Exception as base_error: + raise PyInstallerImportError(name) from base_error + + ctypes.OleDLL = PyInstallerOleDLL + ctypes.oledll = LibraryLoader(PyInstallerOleDLL) +except ImportError: + pass + +# On Mac OS X insert sys._MEIPASS in the first position of the list of paths +# that ctypes uses to search for libraries. +# +# Note: 'ctypes' module will NOT be bundled with every app because code in this +# module is not scanned for module dependencies. It is safe to wrap +# 'ctypes' module into 'try/except ImportError' block. +if sys.platform.startswith('darwin'): + try: + from ctypes.macholib import dyld + dyld.DEFAULT_LIBRARY_FALLBACK.insert(0, sys._MEIPASS) + except ImportError: + # Do nothing when module 'ctypes' is not available. + pass + + +# Make .eggs and zipfiles available at runtime +d = "eggs" +d = os.path.join(sys._MEIPASS, d) +# Test if the 'eggs' directory exists. This allows to +# opportunistically including this script into the packaged exe, even +# if no eggs as found when packaging the program. (Which may be a +# use-case, see issue #653. +if os.path.isdir(d): + for fn in os.listdir(d): + sys.path.append(os.path.join(d, fn)) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod01_os_path.py b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod01_os_path.py new file mode 100644 index 0000000000000000000000000000000000000000..3e74c6d9d3206b9dbf0dfe493e18c8aeb6a65571 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod01_os_path.py @@ -0,0 +1,109 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +### **NOTE** This module is used during bootstrap. +### Import *ONLY* builtin modules. +### List of built-in modules: sys.builtin_module_names + + +""" +Set up 'os' and 'os.path' module replacement functions for use during import +bootstrap. +""" + + +import sys + + +_builtin_names = sys.builtin_module_names +_mindirlen = 0 + + +# Wrap os.environ, os.listdir(), os.sep + +# We cannot cache the content of os.listdir(). It was found to cause problems +# with programs that dynamically add python modules to be reimported by that +# same program (i.e., plugins), because the cache is only built once +# at the beginning, and never updated. So, we must really list the directory +# again. + +if 'posix' in _builtin_names: # For Linux, Unix, Mac OS X + from posix import environ as os_environ + from posix import listdir as os_listdir + os_sep = '/' + _mindirlen = 1 +elif 'nt' in _builtin_names: # For Windows + from nt import environ as os_environ + from nt import listdir as os_listdir + os_sep = '\\' + _mindirlen = 3 +else: + raise ImportError('No os specific module found') + + +# Wrap os.path.join() +def os_path_join(a, b, sep=os_sep): + if a == '': + return b + lastchar = a[-1:] + if lastchar == '/' or lastchar == sep: + return a + b + return a + sep + b + + +# Wrap os.path.dirname() +def os_path_dirname(a, sep=os_sep, mindirlen=_mindirlen): + for i in range(len(a) - 1, -1, -1): + c = a[i] + if c == '/' or c == sep: + if i < mindirlen: + return a[:i + 1] + return a[:i] + return '' + + +# Wrap os.path.basename() +if sys.platform.startswith('win'): + # Implementation from ntpath.py module + # from standard Python 2.7 Library. + def os_path_basename(pth): + ## Implementation of os.path.splitdrive() + if pth[1:2] == ':': + d = pth[0:2] + p = pth[2:] + else: + d = '' + p = pth + ## Implementation of os.path.split() + # set i to index beyond p's last slash + i = len(p) + while i and p[i - 1] not in '/\\': + i = i - 1 + head, tail = p[:i], p[i:] # now tail has no slashes + # Windows implementation is based on split(). We need + # to return only tail. + return tail +else: + # Implementation from ntpath.py module + # from standard Python 2.7 Library. + def os_path_basename(pth): + i = pth.rfind('/') + 1 + return pth[i:] + + +if 'PYTHONCASEOK' not in os_environ: + def caseOk(filename): + files = os_listdir(os_path_dirname(filename)) + return os_path_basename(filename) in files +else: + def caseOk(filename): + return True diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod02_archive.py b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod02_archive.py new file mode 100644 index 0000000000000000000000000000000000000000..e62df8f9e4113acafe45eca14a8a660afffbb83e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod02_archive.py @@ -0,0 +1,338 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# TODO clean up this module + +# Subclasses may not need marshal or struct, but since they're +# builtin, importing is safe. +# +# While an Archive is really an abstraction for any "filesystem +# within a file", it is tuned for use with imputil.FuncImporter. +# This assumes it contains python code objects, indexed by the +# the internal name (ie, no '.py'). + +# See pyi_carchive.py for a more general archive (contains anything) +# that can be understood by a C program. + + +### **NOTE** This module is used during bootstrap. +### Import *ONLY* builtin modules. + +import marshal +import struct +import sys +import zlib +import _thread as thread + + +# For decrypting Python modules. +CRYPT_BLOCK_SIZE = 16 + + +# content types for PYZ +PYZ_TYPE_MODULE = 0 +PYZ_TYPE_PKG = 1 +PYZ_TYPE_DATA = 2 +PYZ_TYPE_NSPKG = 3 # PEP-420 namespace package + +class FilePos(object): + """ + This class keeps track of the file object representing and current position + in a file. + """ + def __init__(self): + # The file object representing this file. + self.file = None + # The position in the file when it was last closed. + self.pos = 0 + + +class ArchiveFile(object): + """ + File class support auto open when access member from file object + This class is use to avoid file locking on windows + """ + + def __init__(self, *args, **kwargs): + self.args = args + self.kwargs = kwargs + self._filePos = {} + + def local(self): + """ + Return an instance of FilePos for the current thread. This is a crude + # re-implementation of threading.local, which isn't a built-in module + # and therefore isn't available. + """ + ti = thread.get_ident() + if ti not in self._filePos: + self._filePos[ti] = FilePos() + return self._filePos[ti] + + def __getattr__(self, name): + """ + Make this class act like a file, by invoking most methods on its + underlying file object. + """ + file = self.local().file + assert file + return getattr(file, name) + + def __enter__(self): + """ + Open file and seek to pos record from last close. + """ + # The file shouldn't be open yet. + fp = self.local() + assert not fp.file + # Open the file and seek to the last position. + fp.file = open(*self.args, **self.kwargs) + fp.file.seek(fp.pos) + + def __exit__(self, type, value, traceback): + """ + Close file and record pos. + """ + # The file should still be open. + fp = self.local() + assert fp.file + + # Close the file and record its position. + fp.pos = fp.file.tell() + fp.file.close() + fp.file = None + + +class ArchiveReadError(RuntimeError): + pass + + +class ArchiveReader(object): + """ + A base class for a repository of python code objects. + The extract method is used by imputil.ArchiveImporter + to get code objects by name (fully qualified name), so + an enduser "import a.b" would become + extract('a.__init__') + extract('a.b') + """ + MAGIC = b'PYL\0' + HDRLEN = 12 # default is MAGIC followed by python's magic, int pos of toc + TOCPOS = 8 + os = None + _bincache = None + + def __init__(self, path=None, start=0): + """ + Initialize an Archive. If path is omitted, it will be an empty Archive. + """ + self.toc = None + self.path = path + self.start = start + + # In Python 3 module 'imp' is no longer built-in and we cannot use it. + # There is for Python 3 another way how to obtain magic value. + # We cannot use at this bootstrap stage importlib directly + # but its frozen variant. + import _frozen_importlib + self.pymagic = _frozen_importlib._bootstrap_external.MAGIC_NUMBER + + if path is not None: + self.lib = ArchiveFile(self.path, 'rb') + with self.lib: + self.checkmagic() + self.loadtoc() + + + def loadtoc(self): + """ + Overridable. + Default: After magic comes an int (4 byte native) giving the + position of the TOC within self.lib. + Default: The TOC is a marshal-able string. + """ + self.lib.seek(self.start + self.TOCPOS) + (offset,) = struct.unpack('!i', self.lib.read(4)) + self.lib.seek(self.start + offset) + # Use marshal.loads() since load() arg must be a file object + # Convert the read list into a dict for faster access + self.toc = dict(marshal.loads(self.lib.read())) + + ######## This is what is called by FuncImporter ####### + ## Since an Archive is flat, we ignore parent and modname. + #XXX obsolete - imputil only code + ## def get_code(self, parent, modname, fqname): + ## pass + + def is_package(self, name): + ispkg, pos = self.toc.get(name, (0, None)) + if pos is None: + return None + return bool(ispkg) + + ####### Core method - Override as needed ######### + def extract(self, name): + """ + Get the object corresponding to name, or None. + For use with imputil ArchiveImporter, object is a python code object. + 'name' is the name as specified in an 'import name'. + 'import a.b' will become: + extract('a') (return None because 'a' is not a code object) + extract('a.__init__') (return a code object) + extract('a.b') (return a code object) + Default implementation: + self.toc is a dict + self.toc[name] is pos + self.lib has the code object marshal-ed at pos + """ + ispkg, pos = self.toc.get(name, (0, None)) + if pos is None: + return None + with self.lib: + self.lib.seek(self.start + pos) + # use marshal.loads() sind load() arg must be a file object + obj = marshal.loads(self.lib.read()) + return ispkg, obj + + ######################################################################## + # Informational methods + + def contents(self): + """ + Return a list of the contents + Default implementation assumes self.toc is a dict like object. + Not required by ArchiveImporter. + """ + return list(self.toc.keys()) + + def checkmagic(self): + """ + Overridable. + Check to see if the file object self.lib actually has a file + we understand. + """ + self.lib.seek(self.start) # default - magic is at start of file + + if self.lib.read(len(self.MAGIC)) != self.MAGIC: + raise ArchiveReadError("%s is not a valid %s archive file" + % (self.path, self.__class__.__name__)) + + if self.lib.read(len(self.pymagic)) != self.pymagic: + raise ArchiveReadError("%s has version mismatch to dll" % + (self.path)) + + self.lib.read(4) + + +class Cipher(object): + """ + This class is used only to decrypt Python modules. + """ + def __init__(self): + # At build-type the key is given to us from inside the spec file, at + # bootstrap-time, we must look for it ourselves by trying to import + # the generated 'pyi_crypto_key' module. + import pyimod00_crypto_key + key = pyimod00_crypto_key.key + + assert type(key) is str + if len(key) > CRYPT_BLOCK_SIZE: + self.key = key[0:CRYPT_BLOCK_SIZE] + else: + self.key = key.zfill(CRYPT_BLOCK_SIZE) + assert len(self.key) == CRYPT_BLOCK_SIZE + + import tinyaes + self._aesmod = tinyaes + # Issue #1663: Remove the AES module from sys.modules list. Otherwise + # it interferes with using 'tinyaes' module in users' code. + del sys.modules['tinyaes'] + + def __create_cipher(self, iv): + # The 'AES' class is stateful, this factory method is used to + # re-initialize the block cipher class with each call to xcrypt(). + return self._aesmod.AES(self.key.encode(), iv) + + def decrypt(self, data): + cipher = self.__create_cipher(data[:CRYPT_BLOCK_SIZE]) + return cipher.CTR_xcrypt_buffer(data[CRYPT_BLOCK_SIZE:]) + + +class ZlibArchiveReader(ArchiveReader): + """ + ZlibArchive - an archive with compressed entries. Archive is read + from the executable created by PyInstaller. + + This archive is used for bundling python modules inside the executable. + + NOTE: The whole ZlibArchive (PYZ) is compressed so it is not necessary + to compress single modules with zlib. + """ + MAGIC = b'PYZ\0' + TOCPOS = 8 + HDRLEN = ArchiveReader.HDRLEN + 5 + + def __init__(self, path=None, offset=None): + if path is None: + offset = 0 + elif offset is None: + for i in range(len(path) - 1, - 1, - 1): + if path[i] == '?': + try: + offset = int(path[i + 1:]) + except ValueError: + # Just ignore any spurious "?" in the path + # (like in Windows UNC \\?\). + continue + path = path[:i] + break + else: + offset = 0 + + super(ZlibArchiveReader, self).__init__(path, offset) + + # Try to import the key module. If the key module is not available + # then it means that encryption is disabled. + try: + import pyimod00_crypto_key + self.cipher = Cipher() + except ImportError: + self.cipher = None + + def is_package(self, name): + (typ, pos, length) = self.toc.get(name, (0, None, 0)) + if pos is None: + return None + return typ in (PYZ_TYPE_PKG, PYZ_TYPE_NSPKG) + + def is_pep420_namespace_package(self, name): + (typ, pos, length) = self.toc.get(name, (0, None, 0)) + if pos is None: + return None + return typ == PYZ_TYPE_NSPKG + + def extract(self, name): + (typ, pos, length) = self.toc.get(name, (0, None, 0)) + if pos is None: + return None + with self.lib: + self.lib.seek(self.start + pos) + obj = self.lib.read(length) + try: + if self.cipher: + obj = self.cipher.decrypt(obj) + obj = zlib.decompress(obj) + if typ in (PYZ_TYPE_MODULE, PYZ_TYPE_PKG, PYZ_TYPE_NSPKG): + obj = marshal.loads(obj) + except EOFError as e: + raise ImportError("PYZ entry '%s' failed to unmarshal" % + name) from e + return typ, obj diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod03_importers.py b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod03_importers.py new file mode 100644 index 0000000000000000000000000000000000000000..01dc81e71fe897609ef4dc65d04ac5f90855a9de --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/loader/pyimod03_importers.py @@ -0,0 +1,586 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +PEP-302 and PEP-451 importers for frozen applications. +""" + + +### **NOTE** This module is used during bootstrap. +### Import *ONLY* builtin modules. +### List of built-in modules: sys.builtin_module_names + + +import sys +import _frozen_importlib + +import pyimod01_os_path as pyi_os_path +from pyimod02_archive import ArchiveReadError, ZlibArchiveReader + +SYS_PREFIX = sys._MEIPASS + pyi_os_path.os_sep +SYS_PREFIXLEN = len(SYS_PREFIX) + +# In Python 3 it is recommended to use class 'types.ModuleType' to create a new module. +# However, 'types' module is not a built-in module. The 'types' module uses this trick +# with using type() function: +imp_new_module = type(sys) + +if sys.flags.verbose and sys.stderr: + def trace(msg, *a): + sys.stderr.write(msg % a) + sys.stderr.write("\n") +else: + def trace(msg, *a): + pass + +class FrozenPackageImporter(object): + """ + Wrapper class for FrozenImporter that imports one specific fullname from + a module named by an alternate fullname. The alternate fullname is derived from the + __path__ of the package module containing that module. + + This is called by FrozenImporter.find_module whenever a module is found as a result + of searching module.__path__ + """ + def __init__(self, importer, entry_name): + self._entry_name = entry_name + self._importer = importer + + def load_module(self, fullname): + # Deprecated in Python 3.4, see PEP-451 + return self._importer.load_module(fullname, self._entry_name) + + +class FrozenImporter(object): + """ + Load bytecode of Python modules from the executable created by PyInstaller. + + Python bytecode is zipped and appended to the executable. + + NOTE: PYZ format cannot be replaced by zipimport module. + + The problem is that we have no control over zipimport; for instance, + it doesn't work if the zip file is embedded into a PKG appended + to an executable, like we create in one-file. + + This is PEP-302 finder and loader class for the ``sys.meta_path`` hook. + A PEP-302 finder requires method find_module() to return loader + class with method load_module(). Both these methods are implemented + in one class. + + This is also a PEP-451 finder and loader class for the ModuleSpec type + import system. A PEP-451 finder requires method find_spec(), a PEP-451 + loader requires methods exec_module(), load_module(9 and (optionally) + create_module(). All these methods are implemented in this one class. + + To use this class just call + + FrozenImporter.install() + """ + + def __init__(self): + """ + Load, unzip and initialize the Zip archive bundled with the executable. + """ + # Examine all items in sys.path and the one like /path/executable_name?117568 + # is the correct executable with bundled zip archive. Use this value + # for the ZlibArchiveReader class and remove this item from sys.path. + # It was needed only for FrozenImporter class. Wrong path from sys.path + # Raises ArchiveReadError exception. + for pyz_filepath in sys.path: + try: + # Unzip zip archive bundled with the executable. + self._pyz_archive = ZlibArchiveReader(pyz_filepath) + # Verify the integrity of the zip archive with Python modules. + # This is already done when creating the ZlibArchiveReader instance. + #self._pyz_archive.checkmagic() + + # End this method since no Exception was raised we can assume + # ZlibArchiveReader was successfully loaded. Let's remove 'pyz_filepath' + # from sys.path. + sys.path.remove(pyz_filepath) + # Some runtime hook might need access to the list of available + # frozen module. Let's make them accessible as a set(). + self.toc = set(self._pyz_archive.toc.keys()) + # Return - no error was raised. + trace("# PyInstaller: FrozenImporter(%s)", pyz_filepath) + return + except IOError: + # Item from sys.path is not ZlibArchiveReader let's try next. + continue + except ArchiveReadError: + # Item from sys.path is not ZlibArchiveReader let's try next. + continue + # sys.path does not contain filename of executable with bundled zip archive. + # Raise import error. + raise ImportError("Can't load frozen modules.") + + # Private helper + def _is_pep420_namespace_package(self, fullname): + if fullname in self.toc: + try: + return self._pyz_archive.is_pep420_namespace_package(fullname) + except Exception as e: + raise ImportError( + 'Loader FrozenImporter cannot handle module ' + fullname + ) from e + else: + raise ImportError( + 'Loader FrozenImporter cannot handle module ' + fullname + ) + + def find_module(self, fullname, path=None): + # Deprecated in Python 3.4, see PEP-451 + """ + PEP-302 finder.find_module() method for the ``sys.meta_path`` hook. + + fullname fully qualified name of the module + path None for a top-level module, or package.__path__ + for submodules or subpackages. + + Return a loader object if the module was found, or None if it wasn't. + If find_module() raises an exception, it will be propagated to the + caller, aborting the import. + """ + module_loader = None # None means - no module found in this importer. + + if fullname in self.toc: + # Tell the import machinery to use self.load_module() to load the module. + module_loader = self + trace("import %s # PyInstaller PYZ", fullname) + elif path is not None: + # Try to handle module.__path__ modifications by the modules themselves + # Reverse the fake __path__ we added to the package module to a + # dotted module name and add the tail module from fullname onto that + # to synthesize a new fullname + modname = fullname.split('.')[-1] + + for p in path: + if not p.startswith(SYS_PREFIX): + continue + p = p[SYS_PREFIXLEN:] + parts = p.split(pyi_os_path.os_sep) + if not parts: continue + if not parts[0]: + parts = parts[1:] + parts.append(modname) + entry_name = ".".join(parts) + if entry_name in self.toc: + module_loader = FrozenPackageImporter(self, entry_name) + trace("import %s as %s # PyInstaller PYZ (__path__ override: %s)", + entry_name, fullname, p) + break + # Release the interpreter's import lock. + if module_loader is None: + trace("# %s not found in PYZ", fullname) + return module_loader + + def load_module(self, fullname, entry_name=None): + # Deprecated in Python 3.4, see PEP-451 + """ + PEP-302 loader.load_module() method for the ``sys.meta_path`` hook. + + Return the loaded module (instance of imp_new_module()) or raises + an exception, preferably ImportError if an existing exception + is not being propagated. + + When called from FrozenPackageImporter, `entry_name` is the name of the + module as it is stored in the archive. This module will be loaded and installed + into sys.modules using `fullname` as its name + """ + # Acquire the interpreter's import lock. + module = None + if entry_name is None: + entry_name = fullname + try: + # PEP302 If there is an existing module object named 'fullname' + # in sys.modules, the loader must use that existing module. + module = sys.modules.get(fullname) + + # Module not in sys.modules - load it and it to sys.modules. + if module is None: + # Load code object from the bundled ZIP archive. + is_pkg, bytecode = self._pyz_archive.extract(entry_name) + # Create new empty 'module' object. + module = imp_new_module(fullname) + + # TODO Replace bytecode.co_filename by something more meaningful: + # e.g. /absolute/path/frozen_executable/path/to/module/module_name.pyc + # Paths from developer machine are masked. + + # Set __file__ attribute of a module relative to the + # executable so that data files can be found. + module.__file__ = self.get_filename(entry_name) + + ### Set __path__ if 'fullname' is a package. + # Python has modules and packages. A Python package is container + # for several modules or packages. + if is_pkg: + + # If a module has a __path__ attribute, the import mechanism + # will treat it as a package. + # + # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to the + # correct path where PyInstaller should find bundled dynamic + # libraries. In one-file mode it points to the tmp directory where + # bundled files are extracted at execution time. + # + # __path__ cannot be empty list because 'wx' module prepends something to it. + # It cannot contain value 'sys.prefix' because 'xml.etree.cElementTree' fails + # Otherwise. + # + # Set __path__ to point to 'sys.prefix/package/subpackage'. + module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)] + + ### Set __loader__ + # The attribute __loader__ improves support for module 'pkg_resources' and + # with the frozen apps the following functions are working: + # pkg_resources.resource_string(), pkg_resources.resource_stream(). + module.__loader__ = self + + ### Set __package__ + # Accoring to PEP302 this attribute must be set. + # When it is present, relative imports will be based on this + # attribute rather than the module __name__ attribute. + # More details can be found in PEP366. + # For ordinary modules this is set like: + # 'aa.bb.cc.dd' -> 'aa.bb.cc' + if is_pkg: + module.__package__ = fullname + else: + module.__package__ = fullname.rsplit('.', 1)[0] + + ### Set __spec__ + # In Python 3.4 was introduced module attribute __spec__ to + # consolidate all module attributes. + module.__spec__ = _frozen_importlib.ModuleSpec( + entry_name, self, is_package=is_pkg) + + ### Add module object to sys.modules dictionary. + # Module object must be in sys.modules before the loader + # executes the module code. This is crucial because the module + # code may (directly or indirectly) import itself; adding it + # to sys.modules beforehand prevents unbounded recursion in the + # worst case and multiple loading in the best. + sys.modules[fullname] = module + + # Run the module code. + exec(bytecode, module.__dict__) + # Reread the module from sys.modules in case it's changed itself + module = sys.modules[fullname] + + except Exception: + # Remove 'fullname' from sys.modules if it was appended there. + if fullname in sys.modules: + sys.modules.pop(fullname) + # TODO Do we need to raise different types of Exceptions for better debugging? + # PEP302 requires to raise ImportError exception. + #raise ImportError("Can't load frozen module: %s" % fullname) + + raise + + + # Module returned only in case of no exception. + return module + + ### Optional Extensions to the PEP-302 Importer Protocol + + def is_package(self, fullname): + if fullname in self.toc: + try: + return self._pyz_archive.is_package(fullname) + except Exception as e: + raise ImportError( + 'Loader FrozenImporter cannot handle module ' + fullname + ) from e + else: + raise ImportError('Loader FrozenImporter cannot handle module ' + fullname) + + def get_code(self, fullname): + """ + Get the code object associated with the module. + + ImportError should be raised if module not found. + """ + try: + # extract() returns None if fullname not in the archive, thus the + # next line will raise an execpion which will be catched just + # below and raise the ImportError. + return self._pyz_archive.extract(fullname)[1] + except Exception as e: + raise ImportError( + 'Loader FrozenImporter cannot handle module ' + fullname + ) from e + + def get_source(self, fullname): + """ + Method should return the source code for the module as a string. + But frozen modules does not contain source code. + + Return None. + """ + if fullname in self.toc: + # Try loading .py file from the filesystem + filename = pyi_os_path.os_path_join( + SYS_PREFIX, + fullname.replace('.', pyi_os_path.os_sep) + '.py') + try: + with open(filename, 'r') as fp: + return fp.read() + except FileNotFoundError: + pass + return None + else: + # ImportError should be raised if module not found. + raise ImportError('No module named ' + fullname) + + def get_data(self, path): + """ + This returns the data as a string, or raise IOError if the "file" + wasn't found. The data is always returned as if "binary" mode was used. + + This method is useful getting resources with 'pkg_resources' that are + bundled with Python modules in the PYZ archive. + + The 'path' argument is a path that can be constructed by munging + module.__file__ (or pkg.__path__ items) + """ + assert path.startswith(SYS_PREFIX) + fullname = path[SYS_PREFIXLEN:] + if fullname in self.toc: + # If the file is in the archive, return this + return self._pyz_archive.extract(fullname)[1] + else: + # Otherwise try to fetch it from the filesystem. Since + # __file__ attribute works properly just try to open and + # read it. + with open(path, 'rb') as fp: + return fp.read() + + def get_filename(self, fullname): + """ + This method should return the value that __file__ would be set to + if the named module was loaded. If the module is not found, then + ImportError should be raised. + """ + # The absolute absolute path to the executable is taken from + # sys.prefix. In onefile mode it points to the temp directory where + # files are unpacked by PyInstaller. Then, append the appropriate + # suffix (__init__.pyc for a package, or just .pyc for a module). + # Method is_package() will raise ImportError if module not found. + if self.is_package(fullname): + filename = pyi_os_path.os_path_join(pyi_os_path.os_path_join(SYS_PREFIX, + fullname.replace('.', pyi_os_path.os_sep)), '__init__.pyc') + else: + filename = pyi_os_path.os_path_join(SYS_PREFIX, + fullname.replace('.', pyi_os_path.os_sep) + '.pyc') + return filename + + def find_spec(self, fullname, path=None, target=None): + """ + PEP-451 finder.find_spec() method for the ``sys.meta_path`` hook. + + fullname fully qualified name of the module + path None for a top-level module, or package.__path__ for + submodules or subpackages. + target unused by this Finder + + Finders are still responsible for identifying, and typically creating, + the loader that should be used to load a module. That loader will now + be stored in the module spec returned by find_spec() rather than + returned directly. As is currently the case without the PEP-452, if a + loader would be costly to create, that loader can be designed to defer + the cost until later. + + Finders must return ModuleSpec objects when find_spec() is called. + This new method replaces find_module() and find_loader() (in the + PathEntryFinder case). If a loader does not have find_spec(), + find_module() and find_loader() are used instead, for + backward-compatibility. + """ + entry_name = None # None means - no module found in this importer. + + if fullname in self.toc: + entry_name = fullname + trace("import %s # PyInstaller PYZ", fullname) + elif path is not None: + # Try to handle module.__path__ modifications by the modules themselves + # Reverse the fake __path__ we added to the package module to a + # dotted module name and add the tail module from fullname onto that + # to synthesize a new fullname + modname = fullname.rsplit('.')[-1] + + for p in path: + if not p.startswith(SYS_PREFIX): + continue + p = p[SYS_PREFIXLEN:] + parts = p.split(pyi_os_path.os_sep) + if not parts: continue + if not parts[0]: + parts = parts[1:] + parts.append(modname) + entry_name = ".".join(parts) + if entry_name in self.toc: + trace("import %s as %s # PyInstaller PYZ (__path__ override: %s)", + entry_name, fullname, p) + break + else: + entry_name = None + + if entry_name is None: + trace("# %s not found in PYZ", fullname) + return None + + if self._is_pep420_namespace_package(entry_name): + # PEP-420 namespace package; as per PEP 451, we need to + # return a spec with "loader" set to None (a.k.a. not set) + spec = _frozen_importlib.ModuleSpec( + fullname, None, + is_package=True) + # Set submodule_search_locations, which seems to fill the + # __path__ attribute. + spec.submodule_search_locations = [ + pyi_os_path.os_path_dirname(self.get_filename(entry_name)) + ] + return spec + + # origin has to be the filename + origin = self.get_filename(entry_name) + is_pkg = self.is_package(entry_name) + + spec = _frozen_importlib.ModuleSpec( + fullname, self, + is_package=is_pkg, origin=origin, + # Provide the entry_name for the loader to use during loading + loader_state = entry_name) + + # Make the import machinery set __file__. + # PEP 451 says: "has_location" is true if the module is locatable. In + # that case the spec's origin is used as the location and __file__ is + # set to spec.origin. If additional location information is required + # (e.g. zipimport), that information may be stored in + # spec.loader_state. + spec.has_location = True + + # Set submodule_search_locations for packages. Seems to be + # required for importlib_resources from 3.2.0 - see issue #5395. + if is_pkg: + spec.submodule_search_locations = [ + pyi_os_path.os_path_dirname(self.get_filename(entry_name)) + ] + + return spec + + def create_module(self, spec): + """ + PEP-451 loader.create_module() method for the ``sys.meta_path`` hook. + + Loaders may also implement create_module() that will return a new + module to exec. It may return None to indicate that the default module + creation code should be used. One use case, though atypical, for + create_module() is to provide a module that is a subclass of the + builtin module type. Most loaders will not need to implement + create_module(), + + create_module() should properly handle the case where it is called + more than once for the same spec/module. This may include returning + None or raising ImportError. + """ + # Opposed to what is defined in PEP-451, this method is not optional. + # We want the default results, so we simply return None (which is + # handled for su my the import machinery). See + # https://bugs.python.org/issue23014 for more information. + return None + + def exec_module(self, module): + """ + PEP-451 loader.exec_module() method for the ``sys.meta_path`` hook. + + Loaders will have a new method, exec_module(). Its only job is to + "exec" the module and consequently populate the module's namespace. It + is not responsible for creating or preparing the module object, nor + for any cleanup afterward. It has no return value. exec_module() will + be used during both loading and reloading. + + exec_module() should properly handle the case where it is called more + than once. For some kinds of modules this may mean raising ImportError + every time after the first time the method is called. This is + particularly relevant for reloading, where some kinds of modules do + not support in-place reloading. + """ + spec = module.__spec__ + bytecode = self.get_code(spec.loader_state) + + # Set by the import machinery + assert hasattr(module, '__file__') + + # If `submodule_search_locations` is not None, this is a package; + # set __path__. + if spec.submodule_search_locations is not None: + # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to + # the correct path where PyInstaller should find bundled dynamic + # libraries. In one-file mode it points to the tmp directory where + # bundled files are extracted at execution time. + # + # __path__ cannot be empty list because 'wx' module prepends + # something to it. It cannot contain value 'sys.prefix' because + # 'xml.etree.cElementTree' fails otherwise. + # + # Set __path__ to point to 'sys.prefix/package/subpackage'. + module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)] + + exec(bytecode, module.__dict__) + + +def install(): + """ + Install FrozenImporter class and other classes into the import machinery. + + This class method (static method) installs the FrozenImporter class into + the import machinery of the running process. The importer is added + to sys.meta_path. It could be added to sys.path_hooks but sys.meta_path + is processed by Python before looking at sys.path! + + The order of processing import hooks in sys.meta_path: + + 1. built-in modules + 2. modules from the bundled ZIP archive + 3. C extension modules + 4. Modules from sys.path + """ + # Ensure Python looks in the bundled zip archive for modules before any + # other places. + fimp = FrozenImporter() + sys.meta_path.append(fimp) + + # On Windows there is importer _frozen_importlib.WindowsRegistryFinder that + # looks for Python modules in Windows registry. The frozen executable should + # not look for anything in the Windows registry. Remove this importer from + # sys.meta_path. + for item in sys.meta_path: + if hasattr(item, '__name__') and item.__name__ == 'WindowsRegistryFinder': + sys.meta_path.remove(item) + break + # _frozen_importlib.PathFinder is also able to handle Python C + # extensions. However, PyInstaller needs its own importer since it + # uses extension names like 'module.submodle.so' (instead of paths). + # As of Python 3.7.0b2, there are several PathFinder instances (and + # duplicate ones) on sys.meta_path. This propobly is a bug, see + # https://bugs.python.org/issue33128. Thus we need to move all of them + # to the end, eliminating duplicates . + pathFinders = [] + for item in reversed(sys.meta_path): + if getattr(item, '__name__', None) == 'PathFinder': + sys.meta_path.remove(item) + if not item in pathFinders: + pathFinders.append(item) + sys.meta_path.extend(reversed(pathFinders)) + # TODO Do we need for Python 3 _frozen_importlib.FrozenImporter? Could it be also removed? diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/log.py b/3rdparty/pyinstaller-4.3/PyInstaller/log.py new file mode 100644 index 0000000000000000000000000000000000000000..b7577cbd06fbbb16aa9d2a9ee387e808cbade4e8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/log.py @@ -0,0 +1,48 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Logging module for PyInstaller +""" + +__all__ = ['getLogger', 'INFO', 'WARN', 'DEBUG', 'TRACE', 'ERROR', 'FATAL'] + +import logging +from logging import getLogger, INFO, WARN, DEBUG, ERROR, FATAL + +TRACE = logging.TRACE = DEBUG - 5 +logging.addLevelName(TRACE, 'TRACE') + +FORMAT = '%(relativeCreated)d %(levelname)s: %(message)s' +logging.basicConfig(format=FORMAT, level=logging.INFO) +logger = getLogger('PyInstaller') + + +def __add_options(parser): + levels = ('TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'CRITICAL') + parser.add_argument('--log-level', + choices=levels, metavar="LEVEL", + default='INFO', + dest='loglevel', + help=('Amount of detail in build-time console messages. ' + 'LEVEL may be one of %s (default: %%(default)s).' + % ', '.join(levels)) + ) + + +def __process_options(parser, opts): + try: + level = getattr(logging, opts.loglevel.upper()) + except AttributeError: + parser.error('Unknown log level `%s`' % opts.loglevel) + else: + logger.setLevel(level) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/_gitrevision.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/_gitrevision.py new file mode 100644 index 0000000000000000000000000000000000000000..772fd836c4379da3702d96f11d22407a48d43c12 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/_gitrevision.py @@ -0,0 +1,11 @@ +# +# The content of this file will be filled in with meaningful data +# when creating an archive using `git archive` or by downloading an +# archive from github, e.g. from github.com/.../archive/develop.zip +# +rev = "$Format:%h$" # abbreviated commit hash +commit = "$Format:%H$" # commit hash +date = "$Format:%ci$" # commit date +author = "$Format:%an$ <$Format:%ae$>" +ref_names = "$Format:%D$" # incl. current branch +commit_message = """$Format:%B$""" diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/archive_viewer.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/archive_viewer.py new file mode 100644 index 0000000000000000000000000000000000000000..ee6b3cac5fe6a2d080fd3f9237a55c7dabe07f69 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/archive_viewer.py @@ -0,0 +1,268 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Viewer for archives packaged by archive.py +""" + +import argparse +import os +import pprint +import sys +import tempfile +import zlib + +from PyInstaller.loader import pyimod02_archive +from PyInstaller.archive.readers import CArchiveReader, NotAnArchiveError +from PyInstaller.compat import stdin_input +import PyInstaller.log + +stack = [] +cleanup = [] + + +def main(name, brief, debug, rec_debug, **unused_options): + + global stack + + if not os.path.isfile(name): + print(name, "is an invalid file name!", file=sys.stderr) + return 1 + + arch = get_archive(name) + stack.append((name, arch)) + if debug or brief: + show_log(arch, rec_debug, brief) + raise SystemExit(0) + else: + show(name, arch) + + while 1: + try: + toks = stdin_input('? ').split(None, 1) + except EOFError: + # Ctrl-D + print(file=sys.stderr) # Clear line. + break + if not toks: + usage() + continue + if len(toks) == 1: + cmd = toks[0] + arg = '' + else: + cmd, arg = toks + cmd = cmd.upper() + if cmd == 'U': + if len(stack) > 1: + arch = stack[-1][1] + del stack[-1] + name, arch = stack[-1] + show(name, arch) + elif cmd == 'O': + if not arg: + arg = stdin_input('open name? ') + arg = arg.strip() + try: + arch = get_archive(arg) + except NotAnArchiveError as e: + print(e, file=sys.stderr) + continue + if arch is None: + print(arg, "not found", file=sys.stderr) + continue + stack.append((arg, arch)) + show(arg, arch) + elif cmd == 'X': + if not arg: + arg = stdin_input('extract name? ') + arg = arg.strip() + data = get_data(arg, arch) + if data is None: + print("Not found", file=sys.stderr) + continue + filename = stdin_input('to filename? ') + if not filename: + print(repr(data)) + else: + with open(filename, 'wb') as fp: + fp.write(data) + elif cmd == 'Q': + break + else: + usage() + do_cleanup() + + +def do_cleanup(): + global stack, cleanup + stack = [] + for filename in cleanup: + try: + os.remove(filename) + except Exception as e: + print("couldn't delete", filename, e.args, file=sys.stderr) + cleanup = [] + + +def usage(): + print("U: go Up one level", file=sys.stderr) + print("O : open embedded archive name", file=sys.stderr) + print("X : extract name", file=sys.stderr) + print("Q: quit", file=sys.stderr) + + +def get_archive(name): + if not stack: + if name[-4:].lower() == '.pyz': + return ZlibArchive(name) + return CArchiveReader(name) + parent = stack[-1][1] + try: + return parent.openEmbedded(name) + except KeyError: + return None + except (ValueError, RuntimeError): + ndx = parent.toc.find(name) + dpos, dlen, ulen, flag, typcd, name = parent.toc[ndx] + x, data = parent.extract(ndx) + tempfilename = tempfile.mktemp() + cleanup.append(tempfilename) + with open(tempfilename, 'wb') as fp: + fp.write(data) + if typcd == 'z': + return ZlibArchive(tempfilename) + else: + return CArchiveReader(tempfilename) + + +def get_data(name, arch): + if isinstance(arch.toc, dict): + (ispkg, pos, length) = arch.toc.get(name, (0, None, 0)) + if pos is None: + return None + with arch.lib: + arch.lib.seek(arch.start + pos) + return zlib.decompress(arch.lib.read(length)) + ndx = arch.toc.find(name) + dpos, dlen, ulen, flag, typcd, name = arch.toc[ndx] + x, data = arch.extract(ndx) + return data + + +def show(name, arch): + if isinstance(arch.toc, dict): + print(" Name: (ispkg, pos, len)") + toc = arch.toc + else: + print(" pos, length, uncompressed, iscompressed, type, name") + toc = arch.toc.data + pprint.pprint(toc) + + +def get_content(arch, recursive, brief, output): + if isinstance(arch.toc, dict): + toc = arch.toc + if brief: + for name, _ in toc.items(): + output.append(name) + else: + output.append(toc) + else: + toc = arch.toc.data + for el in toc: + if brief: + output.append(el[5]) + else: + output.append(el) + if recursive: + if el[4] in ('z', 'a'): + get_content(get_archive(el[5]), recursive, brief, output) + stack.pop() + + +def show_log(arch, recursive, brief): + output = [] + get_content(arch, recursive, brief, output) + # first print all TOCs + for out in output: + if isinstance(out, dict): + pprint.pprint(out) + # then print the other entries + pprint.pprint([out for out in output if not isinstance(out, dict)]) + + +def get_archive_content(filename): + """ + Get a list of the (recursive) content of archive `filename`. + + This function is primary meant to be used by runtests. + """ + archive = get_archive(filename) + stack.append((filename, archive)) + output = [] + get_content(archive, recursive=True, brief=True, output=output) + do_cleanup() + return output + + +class ZlibArchive(pyimod02_archive.ZlibArchiveReader): + + def checkmagic(self): + """ Overridable. + Check to see if the file object self.lib actually has a file + we understand. + """ + self.lib.seek(self.start) # default - magic is at start of file. + if self.lib.read(len(self.MAGIC)) != self.MAGIC: + raise RuntimeError("%s is not a valid %s archive file" + % (self.path, self.__class__.__name__)) + if self.lib.read(len(self.pymagic)) != self.pymagic: + print("Warning: pyz is from a different Python version", + file=sys.stderr) + self.lib.read(4) + + +def run(): + parser = argparse.ArgumentParser() + parser.add_argument('-l', '--log', + default=False, + action='store_true', + dest='debug', + help='Print an archive log (default: %(default)s)') + parser.add_argument('-r', '--recursive', + default=False, + action='store_true', + dest='rec_debug', + help='Recursively print an archive log (default: %(default)s). ' + 'Can be combined with -r') + parser.add_argument('-b', '--brief', + default=False, + action='store_true', + dest='brief', + help='Print only file name. (default: %(default)s). ' + 'Can be combined with -r') + PyInstaller.log.__add_options(parser) + parser.add_argument('name', metavar='pyi_archive', + help="pyinstaller archive to show content of") + + args = parser.parse_args() + PyInstaller.log.__process_options(parser, args) + + try: + raise SystemExit(main(**vars(args))) + except KeyboardInterrupt: + raise SystemExit("Aborted by user request.") + +if __name__ == '__main__': + run() + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/bindepend.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/bindepend.py new file mode 100644 index 0000000000000000000000000000000000000000..89487a9b45c879399891501412170bb59b593e7b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/bindepend.py @@ -0,0 +1,53 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Show dll dependencies of executable files or other dynamic libraries. +""" + +import glob +import argparse + + +import PyInstaller.depend.bindepend +from PyInstaller.compat import is_win +import PyInstaller.log + + +def run(): + parser = argparse.ArgumentParser() + PyInstaller.log.__add_options(parser) + parser.add_argument('filenames', nargs='+', + metavar='executable-or-dynamic-library', + help=("executables or dynamic libraries for which " + "the dependencies should be shown")) + + args = parser.parse_args() + PyInstaller.log.__process_options(parser, args) + + # Suppress all informative messages from the dependency code. + PyInstaller.log.getLogger('PyInstaller.build.bindepend').setLevel( + PyInstaller.log.WARN) + + try: + for a in args.filenames: + for fn in glob.glob(a): + imports = PyInstaller.depend.bindepend.getImports(fn) + if is_win: + assemblies = PyInstaller.depend.bindepend.getAssemblies(fn) + imports.update([a.getid() for a in assemblies]) + print(fn, imports) + except KeyboardInterrupt: + raise SystemExit("Aborted by user request.") + +if __name__ == '__main__': + run() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/grab_version.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/grab_version.py new file mode 100644 index 0000000000000000000000000000000000000000..6606417c17208d1577b15944faaf89edcc3e6b23 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/grab_version.py @@ -0,0 +1,43 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 codecs +import argparse + + +def run(): + parser = argparse.ArgumentParser( + epilog = ('The printed output may be saved to a file, edited and ' + 'used as the input for a version resource on any of the ' + 'executable targets in an Installer spec file.')) + parser.add_argument('exe_file', metavar='exe-file', + help="full pathname of a Windows executable") + parser.add_argument('out_filename', metavar='out-filename', nargs='?', + default='file_version_info.txt', + help=("filename where the grabbed version info " + "will be saved")) + + args = parser.parse_args() + + try: + import PyInstaller.utils.win32.versioninfo + vs = PyInstaller.utils.win32.versioninfo.decode(args.exe_file) + if not vs: + raise SystemExit("Error: VersionInfo resource not found in exe") + with codecs.open(args.out_filename, 'w', 'utf-8') as fp: + fp.write(u"%s" % (vs,)) + print('Version info written to: %s' % args.out_filename) + except KeyboardInterrupt: + raise SystemExit("Aborted by user request.") + +if __name__ == '__main__': + run() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/makespec.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/makespec.py new file mode 100644 index 0000000000000000000000000000000000000000..c78d29e8781368d950fd220b0854858688cfd594 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/makespec.py @@ -0,0 +1,47 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.building.makespec +import PyInstaller.log + + +def run(): + p = argparse.ArgumentParser() + PyInstaller.building.makespec.__add_options(p) + PyInstaller.log.__add_options(p) + p.add_argument('scriptname', nargs='+') + + args = p.parse_args() + PyInstaller.log.__process_options(p, args) + + # Split pathex by using the path separator + temppaths = args.pathex[:] + args.pathex = [] + for p in temppaths: + args.pathex.extend(p.split(os.pathsep)) + + try: + name = PyInstaller.building.makespec.main(args.scriptname, **vars(args)) + print('wrote %s' % name) + print('now run pyinstaller.py to build the executable') + except KeyboardInterrupt: + raise SystemExit("Aborted by user request.") + +if __name__ == '__main__': + run() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/set_version.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/set_version.py new file mode 100644 index 0000000000000000000000000000000000000000..784635ddbda64af3d1794e7a41415b20330bee0f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/cliutils/set_version.py @@ -0,0 +1,35 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 argparse + +def run(): + parser = argparse.ArgumentParser() + parser.add_argument('info_file', metavar='info-file', + help="text file containing version info") + parser.add_argument('exe_file', metavar='exe-file', + help="full pathname of a Windows executable") + args = parser.parse_args() + + info_file = os.path.abspath(args.info_file) + exe_file = os.path.abspath(args.exe_file) + + try: + import PyInstaller.utils.win32.versioninfo + vs = PyInstaller.utils.win32.versioninfo.SetVersion(exe_file, info_file) + print(('Version info set in: %s' % exe_file)) + except KeyboardInterrupt: + raise SystemExit("Aborted by user request.") + +if __name__ == '__main__': + run() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/conftest.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..5ef9b2d427831e78f47eabc3c33f4dc59dab0f7a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/conftest.py @@ -0,0 +1,606 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Imports +# ======= + +# Library imports +# --------------- +import copy +import glob +import os +import pytest +import re +import subprocess +import sys +import inspect +import textwrap +import io +import shutil +from contextlib import suppress + +# Third-party imports +# ------------------- +import py +import psutil # Manages subprocess timeout. + + +# Set a handler for the root-logger to inhibit 'basicConfig()' (called in +# PyInstaller.log) is setting up a stream handler writing to stderr. This +# avoids log messages to be written (and captured) twice: once on stderr and +# once by pytests's caplog. +import logging +logging.getLogger().addHandler(logging.NullHandler()) + + +# Local imports +# ------------- +# Expand sys.path with PyInstaller source. +_ROOT_DIR = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')) +sys.path.append(_ROOT_DIR) + +from PyInstaller import configure, config +from PyInstaller import __main__ as pyi_main +from PyInstaller.utils.tests import gen_sourcefile +from PyInstaller.utils.cliutils import archive_viewer +from PyInstaller.compat import is_darwin, is_win, safe_repr, \ + architecture, is_linux, text_read_mode +from PyInstaller.depend.analysis import initialize_modgraph +from PyInstaller.utils.win32 import winutils +from PyInstaller.utils.hooks.qt import pyqt5_library_info, pyside2_library_info + + + +# Globals +# ======= +# Timeout for running the executable. If executable does not exit in this time +# then it is interpreted as test failure. +_EXE_TIMEOUT = 30 # In sec. +# Number of retries we should attempt if the executable times out. +_MAX_RETRIES = 2 +# All currently supported platforms +SUPPORTED_OSES = {"darwin", "linux", "win32"} + + +# Code +# ==== +# Fixtures +# -------- + +@pytest.fixture +def SPEC_DIR(request): + """Return the directory where the test spec-files reside""" + return py.path.local(_get_spec_dir(request)) + + +@pytest.fixture +def SCRIPT_DIR(request): + """Return the directory where the test scripts reside""" + return py.path.local(_get_script_dir(request)) + + +def pytest_runtest_setup(item): + """Markers to skip tests based on the current platform. + https://pytest.org/en/stable/example/markers.html#marking-platform-specific-tests-with-pytest + + Available markers: see setup.cfg [tool:pytest] markers + - @pytest.mark.darwin (macOS) + - @pytest.mark.linux (GNU/Linux) + - @pytest.mark.win32 (Windows) + """ + supported_platforms = SUPPORTED_OSES.intersection( + mark.name for mark in item.iter_markers()) + plat = sys.platform + if supported_platforms and plat not in supported_platforms: + pytest.skip("does not run on %s" % plat) + + +@pytest.hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_makereport(item, call): + # execute all other hooks to obtain the report object + outcome = yield + rep = outcome.get_result() + + # set an report attribute for each phase of a call, which can + # be "setup", "call", "teardown" + + setattr(item, "rep_" + rep.when, rep) + + +# Return the base directory which contains the current test module. +def _get_base_dir(request): + return os.path.dirname(os.path.abspath(request.fspath.strpath)) + + +# Directory with Python scripts for functional tests. E.g. main scripts, etc. +def _get_script_dir(request): + return os.path.join(_get_base_dir(request), 'scripts') + + +# Directory with testing modules used in some tests. +def _get_modules_dir(request): + return os.path.join(_get_base_dir(request), 'modules') + + +# Directory with .toc log files. +def _get_logs_dir(request): + return os.path.join(_get_base_dir(request), 'logs') + + +# Return the directory where data for tests is located. +def _get_data_dir(request): + return os.path.join(_get_base_dir(request), 'data') + + +# Directory with .spec files used in some tests. +def _get_spec_dir(request): + return os.path.join(_get_base_dir(request), 'specs') + + +@pytest.fixture +def script_dir(request): + return py.path.local(_get_script_dir(request)) + + +# A helper function to copy from data/dir to tmpdir/data. +def _data_dir_copy( + # The pytest request object. + request, + # The name of the subdirectory located in data/name to copy. + subdir_name, + # The tmpdir object for this test. See + # https://pytest.org/latest/tmpdir.html. + tmpdir +): + + # Form the source and tmp paths. + source_data_dir = py.path.local(_get_data_dir(request)).join(subdir_name) + tmp_data_dir = tmpdir.join('data', subdir_name) + # Copy the data. + shutil.copytree(source_data_dir.strpath, tmp_data_dir.strpath) + # Return the temporary data directory, so that the copied data can now be + # used. + return tmp_data_dir + +# Define a fixure for the DataDir object. +@pytest.fixture +def data_dir( + # The request object for this test. See + # https://pytest.org/latest/builtin.html#_pytest.python.FixtureRequest + # and + # https://pytest.org/latest/fixture.html#fixtures-can-introspect-the-requesting-test-context. + request, + # The tmpdir object for this test. See + # https://pytest.org/latest/tmpdir.html. + tmpdir): + + # Strip the leading 'test_' from the test's name. + name = request.function.__name__[5:] + # Copy to tmpdir and return the path. + return _data_dir_copy(request, name, tmpdir) + +class AppBuilder(object): + + def __init__(self, tmpdir, request, bundle_mode): + self._tmpdir = tmpdir + self._request = request + self._mode = bundle_mode + self._specdir = str(tmpdir) + self._distdir = str(tmpdir / 'dist') + self._builddir = str(tmpdir /'build') + + + def test_spec(self, specfile, *args, **kwargs): + """ + Test a Python script that is referenced in the supplied .spec file. + """ + __tracebackhide__ = True + specfile = os.path.join(_get_spec_dir(self._request), specfile) + # 'test_script' should handle .spec properly as script. + return self.test_script(specfile, *args, **kwargs) + + + def test_source(self, source, *args, **kwargs): + """ + Test a Python script given as source code. + + The source will be written into a file named like the + test-function. This file will then be passed to `test_script`. + If you need other related file, e.g. as `.toc`-file for + testing the content, put it at at the normal place. Just mind + to take the basnename from the test-function's name. + + :param script: Source code to create executable from. This + will be saved into a temporary file which is + then passed on to `test_script`. + + :param test_id: Test-id for parametrized tests. If given, it + will be appended to the script filename, + separated by two underscores. + + All other arguments are passed straight on to `test_script`. + + Ensure that the caller of `test_source` is in a UTF-8 + encoded file with the correct '# -*- coding: utf-8 -*-' marker. + + """ + __tracebackhide__ = True + # For parametrized test append the test-id. + scriptfile = gen_sourcefile(self._tmpdir, source, + kwargs.setdefault('test_id')) + del kwargs['test_id'] + return self.test_script(str(scriptfile), *args, **kwargs) + + def test_script(self, script, pyi_args=None, app_name=None, + app_args=None, runtime=None, run_from_path=False, + **kwargs): + """ + Main method to wrap all phases of testing a Python script. + + :param script: Name of script to create executable from. + :param pyi_args: Additional arguments to pass to PyInstaller when creating executable. + :param app_name: Name of the executable. This is equivalent to argument --name=APPNAME. + :param app_args: Additional arguments to pass to + :param runtime: Time in seconds how long to keep executable running. + :param toc_log: List of modules that are expected to be bundled with the executable. + """ + __tracebackhide__ = True + + def marker(line): + # Print some marker to stdout and stderr to make it easier + # to distinguish the phases in the CI test output. + print('-------', line, '-------') + print('-------', line, '-------', file=sys.stderr) + + if pyi_args is None: + pyi_args = [] + if app_args is None: + app_args = [] + + if app_name: + pyi_args.extend(['--name', app_name]) + else: + # Derive name from script name. + app_name = os.path.splitext(os.path.basename(script))[0] + + # Relative path means that a script from _script_dir is referenced. + if not os.path.isabs(script): + script = os.path.join(_get_script_dir(self._request), script) + self.script = script + assert os.path.exists(self.script), 'Script %s not found.' % script + + marker('Starting build.') + if not self._test_building(args=pyi_args): + pytest.fail('Building of %s failed.' % script) + + marker('Build finshed, now running executable.') + self._test_executables(app_name, args=app_args, + runtime=runtime, run_from_path=run_from_path, + **kwargs) + marker('Running executable finished.') + + def _test_executables(self, name, args, runtime, run_from_path, **kwargs): + """ + Run created executable to make sure it works. + + Multipackage-tests generate more than one exe-file and all of + them have to be run. + + :param args: CLI options to pass to the created executable. + :param runtime: Time in seconds how long to keep the executable running. + + :return: Exit code of the executable. + """ + __tracebackhide__ = True + exes = self._find_executables(name) + # Empty list means that PyInstaller probably failed to create any executable. + assert exes != [], 'No executable file was found.' + for exe in exes: + # Try to find .toc log file. .toc log file has the same basename as exe file. + toc_log = os.path.join( + _get_logs_dir(self._request), + os.path.splitext(os.path.basename(exe))[0] + '.toc') + if os.path.exists(toc_log): + if not self._examine_executable(exe, toc_log): + pytest.fail('Matching .toc of %s failed.' % exe) + retcode = self._run_executable(exe, args, run_from_path, runtime) + if retcode != kwargs.get('retcode', 0): + pytest.fail('Running exe %s failed with return-code %s.' % + (exe, retcode)) + + def _find_executables(self, name): + """ + Search for all executables generated by the testcase. + + If the test-case is called e.g. 'test_multipackage1', this is + searching for each of 'test_multipackage1.exe' and + 'multipackage1_?.exe' in both one-file- and one-dir-mode. + + :param name: Name of the executable to look for. + + :return: List of executables + """ + exes = [] + onedir_pt = os.path.join(self._distdir, name, name) + onefile_pt = os.path.join(self._distdir, name) + patterns = [onedir_pt, onefile_pt, + # Multipackage one-dir + onedir_pt + '_?', + # Multipackage one-file + onefile_pt + '_?'] + # For Windows append .exe extension to patterns. + if is_win: + patterns = [pt + '.exe' for pt in patterns] + # For Mac OS X append pattern for .app bundles. + if is_darwin: + # e.g: ./dist/name.app/Contents/MacOS/name + pt = os.path.join(self._distdir, name + '.app', 'Contents', 'MacOS', name) + patterns.append(pt) + # Apply file patterns. + for pattern in patterns: + for prog in glob.glob(pattern): + if os.path.isfile(prog): + exes.append(prog) + return exes + + def _run_executable(self, prog, args, run_from_path, runtime): + """ + Run executable created by PyInstaller. + + :param args: CLI options to pass to the created executable. + """ + # Run the test in a clean environment to make sure they're really self-contained. + prog_env = copy.deepcopy(os.environ) + prog_env['PATH'] = '' + del prog_env['PATH'] + # For Windows we need to keep minimal PATH for successful running of some tests. + if is_win: + # Minimum Windows PATH is in most cases: C:\Windows\system32;C:\Windows + prog_env['PATH'] = os.pathsep.join(winutils.get_system_path()) + + exe_path = prog + if(run_from_path): + # Run executable in the temp directory + # Add the directory containing the executable to $PATH + # Basically, pretend we are a shell executing the program from $PATH. + prog_cwd = str(self._tmpdir) + prog_name = os.path.basename(prog) + prog_env['PATH'] = os.pathsep.join([prog_env.get('PATH', ''), os.path.dirname(prog)]) + + else: + # Run executable in the directory where it is. + prog_cwd = os.path.dirname(prog) + # The executable will be called with argv[0] as relative not absolute path. + prog_name = os.path.join(os.curdir, os.path.basename(prog)) + + args = [prog_name] + args + # Using sys.stdout/sys.stderr for subprocess fixes printing messages in + # Windows command prompt. Py.test is then able to collect stdout/sterr + # messages and display them if a test fails. + for _ in range(_MAX_RETRIES): + retcode = self.__run_executable(args, exe_path, prog_env, + prog_cwd, runtime) + if retcode != 1: # retcode == 1 means a timeout + break + return retcode + + + def __run_executable(self, args, exe_path, prog_env, prog_cwd, runtime): + process = psutil.Popen(args, executable=exe_path, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=prog_env, cwd=prog_cwd) + + def _msg(*text): + print('[' + str(process.pid) + '] ', *text) + + # Run executable. stderr is redirected to stdout. + _msg('RUNNING: ', safe_repr(exe_path), ', args: ', safe_repr(args)) + # 'psutil' allows to use timeout in waiting for a subprocess. + # If not timeout was specified then it is 'None' - no timeout, just waiting. + # Runtime is useful mostly for interactive tests. + try: + timeout = runtime if runtime else _EXE_TIMEOUT + stdout, stderr = process.communicate(timeout=timeout) + retcode = process.returncode + except (psutil.TimeoutExpired, subprocess.TimeoutExpired): + if runtime: + # When 'runtime' is set then expired timeout is a good sing + # that the executable was running successfully for a specified time. + # TODO Is there a better way return success than 'retcode = 0'? + retcode = 0 + else: + # Exe is running and it is not interactive. Fail the test. + retcode = 1 + _msg('TIMED OUT!') + # Kill the subprocess and its child processes. + for p in list(process.children(recursive=True)) + [process]: + with suppress(psutil.NoSuchProcess): + p.kill() + stdout, stderr = process.communicate() + + sys.stdout.buffer.write(stdout) + sys.stderr.buffer.write(stderr) + + return retcode + + def _test_building(self, args): + """ + Run building of test script. + + :param args: additional CLI options for PyInstaller. + + Return True if build succeded False otherwise. + """ + default_args = ['--debug=bootloader', '--noupx', + '--specpath', self._specdir, + '--distpath', self._distdir, + '--workpath', self._builddir, + '--path', _get_modules_dir(self._request), + '--log-level=INFO', + ] + + # Choose bundle mode. + if self._mode == 'onedir': + default_args.append('--onedir') + elif self._mode == 'onefile': + default_args.append('--onefile') + # if self._mode is None then just the spec file was supplied. + + pyi_args = [self.script] + default_args + args + # TODO fix return code in running PyInstaller programatically + PYI_CONFIG = configure.get_config(upx_dir=None) + # Override CACHEDIR for PyInstaller and put it into self.tmpdir + PYI_CONFIG['cachedir'] = str(self._tmpdir) + + pyi_main.run(pyi_args, PYI_CONFIG) + retcode = 0 + + return retcode == 0 + + def _examine_executable(self, exe, toc_log): + """ + Compare log files (now used mostly by multipackage test_name). + + :return: True if .toc files match + """ + print('EXECUTING MATCHING:', toc_log) + fname_list = archive_viewer.get_archive_content(exe) + fname_list = [fn for fn in fname_list] + with open(toc_log, text_read_mode) as f: + pattern_list = eval(f.read()) + # Alphabetical order of patterns. + pattern_list.sort() + missing = [] + for pattern in pattern_list: + for fname in fname_list: + if re.match(pattern, fname): + print('MATCH:', pattern, '-->', fname) + break + else: + # No matching entry found + missing.append(pattern) + print('MISSING:', pattern) + + # Not all modules matched. + # Stop comparing other .toc files and fail the test. + if missing: + for m in missing: + print('Missing', m, 'in', exe) + return False + # All patterns matched. + return True + + +# Scope 'session' should keep the object unchanged for whole tests. +# This fixture caches basic module graph dependencies that are same +# for every executable. +@pytest.fixture(scope='session') +def pyi_modgraph(): + # Explicitly set the log level since the plugin `pytest-catchlog` (un-) + # sets the root logger's level to NOTSET for the setup phase, which will + # lead to TRACE messages been written out. + import PyInstaller.log as logging + logging.logger.setLevel(logging.DEBUG) + initialize_modgraph() + + +# Run by default test as onedir and onefile. +@pytest.fixture(params=['onedir', 'onefile']) +def pyi_builder(tmpdir, monkeypatch, request, pyi_modgraph): + # Save/restore environment variable PATH. + monkeypatch.setenv('PATH', os.environ['PATH'], ) + # PyInstaller or a test case might manipulate 'sys.path'. + # Reset it for every test. + monkeypatch.syspath_prepend(None) + # Set current working directory to + monkeypatch.chdir(tmpdir) + # Clean up configuration and force PyInstaller to do a clean configuration + # for another app/test. + # The value is same as the original value. + monkeypatch.setattr('PyInstaller.config.CONF', {'pathex': []}) + + yield AppBuilder(tmpdir, request, request.param) + + if is_darwin or is_linux: + if request.node.rep_setup.passed: + if request.node.rep_call.passed: + if tmpdir.exists(): + tmpdir.remove(rec=1, ignore_errors=True) + # Clear any PyQt5 state. + try: + del pyqt5_library_info.version + del pyside2_library_info.version + except AttributeError: + pass + + +# Fixture for .spec based tests. +# With .spec it does not make sense to differentiate onefile/onedir mode. +@pytest.fixture +def pyi_builder_spec(tmpdir, request, monkeypatch, pyi_modgraph): + # Save/restore environment variable PATH. + monkeypatch.setenv('PATH', os.environ['PATH'], ) + # Set current working directory to + monkeypatch.chdir(tmpdir) + # PyInstaller or a test case might manipulate 'sys.path'. + # Reset it for every test. + monkeypatch.syspath_prepend(None) + # Clean up configuration and force PyInstaller to do a clean configuration + # for another app/test. + # The value is same as the original value. + monkeypatch.setattr('PyInstaller.config.CONF', {'pathex': []}) + + return AppBuilder(tmpdir, request, None) + +# Define a fixture which compiles the data/load_dll_using_ctypes/ctypes_dylib.c +# program in the tmpdir, returning the tmpdir object. +@pytest.fixture() +def compiled_dylib(tmpdir, request): + tmp_data_dir = _data_dir_copy(request, 'ctypes_dylib', tmpdir) + + # Compile the ctypes_dylib in the tmpdir: Make tmpdir/data the CWD. Don't + # use monkeypatch.chdir to change, then monkeypatch.undo() to restore the + # CWD, since this will undo ALL monkeypatches (such as the pyi_builder's + # additions to sys.path), breaking the test. + old_wd = tmp_data_dir.chdir() + try: + if is_win: + tmp_data_dir = tmp_data_dir.join('ctypes_dylib.dll') + # For Mingw-x64 we must pass '-m32' to build 32-bit binaries + march = '-m32' if architecture == '32bit' else '-m64' + ret = subprocess.call('gcc -shared ' + march + ' ctypes_dylib.c -o ctypes_dylib.dll', shell=True) + if ret != 0: + # Find path to cl.exe file. + from distutils.msvccompiler import MSVCCompiler + comp = MSVCCompiler() + comp.initialize() + cl_path = comp.cc + # Fallback to msvc. + ret = subprocess.call([cl_path, '/LD', 'ctypes_dylib.c'], shell=False) + elif is_darwin: + tmp_data_dir = tmp_data_dir.join('ctypes_dylib.dylib') + # On Mac OS X we need to detect architecture - 32 bit or 64 bit. + arch = 'i386' if architecture == '32bit' else 'x86_64' + cmd = ('gcc -arch ' + arch + ' -Wall -dynamiclib ' + 'ctypes_dylib.c -o ctypes_dylib.dylib -headerpad_max_install_names') + ret = subprocess.call(cmd, shell=True) + id_dylib = os.path.abspath('ctypes_dylib.dylib') + ret = subprocess.call('install_name_tool -id %s ctypes_dylib.dylib' % (id_dylib,), shell=True) + else: + tmp_data_dir = tmp_data_dir.join('ctypes_dylib.so') + ret = subprocess.call('gcc -fPIC -shared ctypes_dylib.c -o ctypes_dylib.so', shell=True) + assert ret == 0, 'Compile ctypes_dylib failed.' + finally: + # Reset the CWD directory. + old_wd.chdir() + + return tmp_data_dir diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/git.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/git.py new file mode 100644 index 0000000000000000000000000000000000000000..0457a478c02270a96e4cfdd50865e7b12e4b8e75 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/git.py @@ -0,0 +1,60 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 contains various helper functions for git DVCS +""" + +import os +from ..compat import exec_command, exec_command_rc + +try: + WindowsError +except NameError: + # Not running on Windows + WindowsError = FileNotFoundError + +def get_repo_revision(): + path = os.path # shortcut + gitdir = path.normpath(path.join(path.dirname(os.path.abspath(__file__)), '..', '..', '.git')) + cwd = os.path.dirname(gitdir) + if not path.exists(gitdir): + try: + from ._gitrevision import rev + if not rev.startswith('$'): + # the format specifier has been substituted + return '+' + rev + except ImportError: + pass + return '' + try: + # need to update index first to get reliable state + exec_command_rc('git', 'update-index', '-q', '--refresh', cwd=cwd) + recent = exec_command('git', 'describe', '--long', '--dirty', '--tag', + cwd=cwd).strip() + if recent.endswith('-dirty'): + tag, changes, rev, dirty = recent.rsplit('-', 3) + rev = rev + '.mod' + else: + tag, changes, rev = recent.rsplit('-', 2) + if changes == '0': + return '' + # According to pep440 local version identifier starts with '+'. + return '+' + rev + except (FileNotFoundError, WindowsError): + # Be silent when git command is not found. + pass + return '' + + +if __name__ == '__main__': + print(get_repo_revision()) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..74a802dd6bd4e202f89dee2c70f341bb0e8ef9ea --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/__init__.py @@ -0,0 +1,1155 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 copy +import glob +import os +import pkg_resources +import pkgutil +import sys +import textwrap +from pathlib import Path +from typing import Tuple + +from ...compat import base_prefix, exec_command_stdout, exec_python, \ + exec_python_rc, is_darwin, is_venv, string_types, open_file, \ + EXTENSION_SUFFIXES, ALL_SUFFIXES, is_conda, is_pure_conda +from ... import HOMEPATH +from ... import log as logging +from ...exceptions import ExecCommandFailed + +logger = logging.getLogger(__name__) + +# These extensions represent Python executables and should therefore be +# ignored when collecting data files. +# NOTE: .dylib files are not Python executable and should not be in this list. +PY_IGNORE_EXTENSIONS = set(ALL_SUFFIXES) + +# Some hooks need to save some values. This is the dict that can be used for +# that. +# +# When running tests this variable should be reset before every test. +# +# For example the 'wx' module needs variable 'wxpubsub'. This tells PyInstaller +# which protocol of the wx module should be bundled. +hook_variables = {} + + +def __exec_python_cmd(cmd, env=None, capture_stdout=True): + """ + Executes an externally spawned Python interpreter. If capture_stdout + is set to True, returns anything that was emitted in the standard + output as a single string. Otherwise, returns the exit code. + """ + # 'PyInstaller.config' cannot be imported as other top-level modules. + from ...config import CONF + if env is None: + env = {} + # Update environment. Defaults to 'os.environ' + pp_env = copy.deepcopy(os.environ) + pp_env.update(env) + # Prepend PYTHONPATH with pathex + # Some functions use some PyInstaller code in subprocess so add + # PyInstaller HOMEPATH to sys.path too. + pp = os.pathsep.join(CONF['pathex'] + [HOMEPATH]) + + # PYTHONPATH might be already defined in the 'env' argument or in + # the original 'os.environ'. Prepend it. + if 'PYTHONPATH' in pp_env: + pp = os.pathsep.join([pp_env.get('PYTHONPATH'), pp]) + pp_env['PYTHONPATH'] = pp + + if capture_stdout: + txt = exec_python(*cmd, env=pp_env) + return txt.strip() + else: + return exec_python_rc(*cmd, env=pp_env) + + +def __exec_statement(statement, capture_stdout=True): + statement = textwrap.dedent(statement) + cmd = ['-c', statement] + return __exec_python_cmd(cmd, capture_stdout=capture_stdout) + + +def exec_statement(statement): + """ + Executes a Python statement in an externally spawned interpreter, and + returns anything that was emitted in the standard output as a single string. + """ + return __exec_statement(statement, capture_stdout=True) + + +def exec_statement_rc(statement): + """ + Executes a Python statement in an externally spawned interpreter, and + returns the exit code. + """ + return __exec_statement(statement, capture_stdout=False) + + +def __exec_script(script_filename, *args, env=None, capture_stdout=True): + """ + Executes a Python script in an externally spawned interpreter. If + capture_stdout is set to True, returns anything that was emitted in + the standard output as a single string. Otherwise, returns the exit + code. + + To prevent misuse, the script passed to utils.hooks.exec_script + must be located in the `PyInstaller/utils/hooks/subproc` directory. + """ + script_filename = os.path.basename(script_filename) + script_filename = os.path.join(os.path.dirname(__file__), 'subproc', script_filename) + if not os.path.exists(script_filename): + raise SystemError("To prevent misuse, the script passed to " + "PyInstaller.utils.hooks.exec_script must be located " + "in the `PyInstaller/utils/hooks/subproc` directory.") + + cmd = [script_filename] + cmd.extend(args) + return __exec_python_cmd(cmd, env=env, capture_stdout=capture_stdout) + + +def exec_script(script_filename, *args, env=None): + """ + Executes a Python script in an externally spawned interpreter, and + returns anything that was emitted in the standard output as a + single string. + + To prevent misuse, the script passed to utils.hooks.exec_script + must be located in the `PyInstaller/utils/hooks/subproc` directory. + """ + return __exec_script(script_filename, *args, env=env, capture_stdout=True) + + +def exec_script_rc(script_filename, *args, env=None): + """ + Executes a Python script in an externally spawned interpreter, and + returns the exit code. + + To prevent misuse, the script passed to utils.hooks.exec_script + must be located in the `PyInstaller/utils/hooks/subproc` directory. + """ + return __exec_script(script_filename, *args, env=env, capture_stdout=False) + + +def eval_statement(statement): + txt = exec_statement(statement).strip() + if not txt: + # return an empty string which is "not true" but iterable + return '' + return eval(txt) + + +def eval_script(scriptfilename, *args, env=None): + txt = exec_script(scriptfilename, *args, env=env).strip() + if not txt: + # return an empty string which is "not true" but iterable + return '' + return eval(txt) + + +def get_pyextension_imports(modname): + """ + Return list of modules required by binary (C/C++) Python extension. + + Python extension files ends with .so (Unix) or .pyd (Windows). + It's almost impossible to analyze binary extension and its dependencies. + + Module cannot be imported directly. + + Let's at least try import it in a subprocess and get the difference + in module list from sys.modules. + + This function could be used for 'hiddenimports' in PyInstaller hooks files. + """ + + statement = """ + import sys + # Importing distutils filters common modules, especially in virtualenv. + import distutils + original_modlist = set(sys.modules.keys()) + # When importing this module - sys.modules gets updated. + import %(modname)s + all_modlist = set(sys.modules.keys()) + diff = all_modlist - original_modlist + # Module list contain original modname. We do not need it there. + diff.discard('%(modname)s') + # Print module list to stdout. + print(list(diff)) + """ % {'modname': modname} + module_imports = eval_statement(statement) + + if not module_imports: + logger.error('Cannot find imports for module %s' % modname) + return [] # Means no imports found or looking for imports failed. + # module_imports = filter(lambda x: not x.startswith('distutils'), module_imports) + return module_imports + + +def get_homebrew_path(formula=''): + """ + Return the homebrew path to the requested formula, or the global prefix when + called with no argument. Returns the path as a string or None if not found. + :param formula: + """ + import subprocess + brewcmd = ['brew', '--prefix'] + path = None + if formula: + brewcmd.append(formula) + dbgstr = 'homebrew formula "%s"' % formula + else: + dbgstr = 'homebrew prefix' + try: + path = subprocess.check_output(brewcmd).strip() + logger.debug('Found %s at "%s"' % (dbgstr, path)) + except OSError: + logger.debug('Detected homebrew not installed') + except subprocess.CalledProcessError: + logger.debug('homebrew formula "%s" not installed' % formula) + if path: + return path.decode('utf8') # OS X filenames are UTF-8 + else: + return None + + +def remove_prefix(string, prefix): + """ + This function removes the given prefix from a string, if the string does + indeed begin with the prefix; otherwise, it returns the string + unmodified. + """ + if string.startswith(prefix): + return string[len(prefix):] + else: + return string + + +def remove_suffix(string, suffix): + """ + This function removes the given suffix from a string, if the string + does indeed end with the prefix; otherwise, it returns the string + unmodified. + """ + # Special case: if suffix is empty, string[:0] returns ''. So, test + # for a non-empty suffix. + if suffix and string.endswith(suffix): + return string[:-len(suffix)] + else: + return string + + +# TODO: Do we really need a helper for this? This is pretty trivially obvious. +def remove_file_extension(filename): + """ + This function returns filename without its extension. + + For Python C modules it removes even whole '.cpython-34m.so' etc. + """ + for suff in EXTENSION_SUFFIXES: + if filename.endswith(suff): + return filename[0:filename.rfind(suff)] + # Fallback to ordinary 'splitext'. + return os.path.splitext(filename)[0] + + +def can_import_module(module_name): + """ + Check if the specified module can be imported. + + Intended as a silent module availability check, as it does not print + ModuleNotFoundError traceback to stderr when the module is unavailable. + + Parameters + ---------- + module_name : str + Fully-qualified name of the module. + + Returns + ---------- + bool + Boolean indicating whether the module can be imported or not. + """ + + rc = exec_statement_rc(""" + try: + import {0} + except ModuleNotFoundError: + raise SystemExit(1) + """.format(module_name) + ) + return rc == 0 + + +# TODO: Replace most calls to exec_statement() with calls to this function. +def get_module_attribute(module_name, attr_name): + """ + Get the string value of the passed attribute from the passed module if this + attribute is defined by this module _or_ raise `AttributeError` otherwise. + + Since modules cannot be directly imported during analysis, this function + spawns a subprocess importing this module and returning the string value of + this attribute in this module. + + Parameters + ---------- + module_name : str + Fully-qualified name of this module. + attr_name : str + Name of the attribute in this module to be retrieved. + + Returns + ---------- + str + String value of this attribute. + + Raises + ---------- + AttributeError + If this attribute is undefined. + """ + # Magic string to be printed and captured below if this attribute is + # undefined, which should be sufficiently obscure as to avoid collisions + # with actual attribute values. That's the hope, anyway. + attr_value_if_undefined = '!)ABadCafe@(D15ea5e#*DeadBeef$&Fee1Dead%^' + attr_value = exec_statement(""" + import %s as m + print(getattr(m, %r, %r)) + """ % (module_name, attr_name, attr_value_if_undefined)) + + if attr_value == attr_value_if_undefined: + raise AttributeError( + 'Module %r has no attribute %r' % (module_name, attr_name)) + else: + return attr_value + + +def get_module_file_attribute(package): + """ + Get the absolute path of the module with the passed name. + + Since modules *cannot* be directly imported during analysis, this function + spawns a subprocess importing this module and returning the value of this + module's `__file__` attribute. + + Parameters + ---------- + package : str + Fully-qualified name of this module. + + Returns + ---------- + str + Absolute path of this module. + """ + # First try to use 'pkgutil'. - fastest but doesn't work on + # certain modules in pywin32, which replace all module attributes + # with those of the .dll + try: + loader = pkgutil.find_loader(package) + attr = loader.get_filename(package) + # The built-in ``datetime`` module returns ``None``. Mark this as + # an ``ImportError``. + if not attr: + raise ImportError('Unable to load module attributes') + # Second try to import module in a subprocess. Might raise ImportError. + except (AttributeError, ImportError) as e: + # Statement to return __file__ attribute of a package. + __file__statement = """ + import %s as p + try: + print(p.__file__) + except: + # If p lacks a file attribute, hide the exception. + pass + """ + attr = exec_statement(__file__statement % package) + if not attr.strip(): + raise ImportError('Unable to load module attribute') from e + return attr + + +def is_module_satisfies(requirements, version=None, version_attr='__version__'): + """ + `True` if the module, package, or C extension described by the passed + requirements string both exists and satisfies these requirements. + + This function checks module versions and extras (i.e., optional install- + time features) via the same low-level algorithm leveraged by + `easy_install` and `pip`, and should _always_ be called in lieu of manual + checking. Attempting to manually check versions and extras invites subtle + issues, particularly when comparing versions lexicographically (e.g., + `'00.5' > '0.6'` is `True`, despite being semantically untrue). + + Requirements + ---------- + This function is typically used to compare the version of a currently + installed module with some desired version. To do so, a string of the form + `{module_name} {comparison_operator} {version}` (e.g., `sphinx >= 1.3`) is + passed as the `requirements` parameter, where: + + * `{module_name}` is the fully-qualified name of the module, package, or C + extension to be tested (e.g., `yaml`). This is _not_ a `setuptools`- + specific distribution name (e.g., `PyYAML`). + * `{comparison_operator}` is the numeric comparison to be performed. All + numeric Python comparisons are supported (e.g., `!=`, `==`, `<`, `>=`). + * `{version}` is the desired PEP 0440-compliant version (e.g., `3.14-rc5`) + to be compared against the current version of this module. + + This function may also be used to test multiple versions and/or extras. To + do so, a string formatted ala the `pkg_resources.Requirements.parse()` + class method (e.g., `idontevenknow<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1`) is + passed as the `requirements` parameter. (See URL below.) + + Implementation + ---------- + This function behaves as follows: + + * If one or more `setuptools` distributions exist for this module, this + module was installed via either `easy_install` or `pip`. In either case, + `setuptools` machinery is used to validate the passed requirements. + * Else, these requirements are manually validated. Since manually + validating extras is non-trivial, only versions are manually validated: + * If these requirements test only extras (e.g., `Norf [foo, bar]`), + `True` is unconditionally returned. + * Else, these requirements test one or more versions. Then: + 1. These requirements are converted into an instance of + `pkg_resources.Requirements`, thus parsing these requirements into + their constituent components. This is surprisingly non-trivial! + 1. The current version of the desired module is found as follows: + * If the passed `version` parameter is non-`None`, that is used. + * Else, a subprocess importing this module is spawned and the value + of this module's version attribute in that subprocess is used. The + name of this attribute defaults to `__version__` but may be + configured with the passed `version_attr` parameter. + 1. These requirements are validated against this version. + + Note that `setuptools` is generally considered to be the most robust means + of comparing version strings in Python. The alternative `LooseVersion()` + and `StrictVersion()` functions provided by the standard + `distutils.version` module fail for common edge cases: e.g., + + >>> from distutils.version import LooseVersion + >>> LooseVersion('1.5') >= LooseVersion('1.5-rc2') + False + >>> from pkg_resources import parse_version + >>> parse_version('1.5') >= parse_version('1.5-rc2') + True + + Parameters + ---------- + requirements : str + Requirements in `pkg_resources.Requirements.parse()` format. + version : str + Optional PEP 0440-compliant version (e.g., `3.14-rc5`) to be used + _instead_ of the current version of this module. If non-`None`, this + function ignores all `setuptools` distributions for this module and + instead compares this version against the version embedded in the + passed requirements. This ignores the module name embedded in the + passed requirements, permitting arbitrary versions to be compared in a + robust manner. (See examples below.) + version_attr : str + Optional name of the version attribute defined by this module, + defaulting to `__version__`. If a `setuptools` distribution exists for + this module (there usually does) _and_ the `version` parameter is + `None` (it usually is), this parameter is ignored. + + Returns + ---------- + bool + Boolean result of the desired validation. + + Raises + ---------- + AttributeError + If no `setuptools` distribution exists for this module _and_ this + module defines no attribute whose name is the passed + `version_attr` parameter. + ValueError + If the passed specification does _not_ comply with + `pkg_resources.Requirements` syntax. + + See Also + ---------- + https://pythonhosted.org/setuptools/pkg_resources.html#id12 + `pkg_resources.Requirements` syntax details. + + Examples + ---------- + # Assume PIL 2.9.0, Sphinx 1.3.1, and SQLAlchemy 0.6 are all installed. + >>> from PyInstaller.utils.hooks import is_module_satisfies + >>> is_module_satisfies('sphinx >= 1.3.1') + True + >>> is_module_satisfies('sqlalchemy != 0.6') + False + + # Compare two arbitrary versions. In this case, the module name + # "sqlalchemy" is simply ignored. + >>> is_module_satisfies('sqlalchemy != 0.6', version='0.5') + True + + # Since the "pillow" project providing PIL publishes its version via + # the custom "PILLOW_VERSION" attribute (rather than the standard + # "__version__" attribute), an attribute name is passed as a fallback + # to validate PIL when not installed by setuptools. As PIL is usually + # installed by setuptools, this optional parameter is usually ignored. + >>> is_module_satisfies('PIL == 2.9.0', version_attr='PILLOW_VERSION') + True + """ + # If no version was explicitly passed... + if version is None: + # If a setuptools distribution exists for this module, this validation + # is a simple one-liner. This approach supports non-version validation + # (e.g., of "["- and "]"-delimited extras) and is hence preferable. + try: + pkg_resources.get_distribution(requirements) + # If no such distribution exists, fallback to the logic below. + except pkg_resources.DistributionNotFound: + pass + # If all existing distributions violate these requirements, fail. + except (pkg_resources.UnknownExtra, pkg_resources.VersionConflict): + return False + # Else, an existing distribution satisfies these requirements. Win! + else: + return True + + # Either a module version was explicitly passed or no setuptools + # distribution exists for this module. First, parse a setuptools + # "Requirements" object from this requirements string. + requirements_parsed = pkg_resources.Requirement.parse(requirements) + + # If no version was explicitly passed, query this module for it. + if version is None: + module_name = requirements_parsed.project_name + version = get_module_attribute(module_name, version_attr) + + if not version: + # Module does not exist in the system. + return False + else: + # Compare this version against the one parsed from the requirements. + return version in requirements_parsed + + +def is_package(module_name): + """ + Check if a Python module is really a module or is a package containing + other modules. + + :param module_name: Module name to check. + :return: True if module is a package else otherwise. + """ + # This way determines if module is a package without importing the module. + try: + loader = pkgutil.find_loader(module_name) + except Exception: + # When it fails to find a module loader then it points probably to a class + # or function and module is not a package. Just return False. + return False + else: + if loader: + # A package must have a __path__ attribute. + return loader.is_package(module_name) + else: + # In case of None - modules is probably not a package. + return False + + +def get_package_paths(package): + """ + Given a package, return the path to packages stored on this machine + and also returns the path to this particular package. For example, + if pkg.subpkg lives in /abs/path/to/python/libs, then this function + returns (/abs/path/to/python/libs, + /abs/path/to/python/libs/pkg/subpkg). + """ + file_attr = get_module_file_attribute(package) + + # package.__file__ = /abs/path/to/package/subpackage/__init__.py. + # Search for Python files in /abs/path/to/package/subpackage; pkg_dir + # stores this path. + pkg_dir = os.path.dirname(file_attr) + # When found, remove /abs/path/to/ from the filename; pkg_base stores + # this path to be removed. + pkg_base = remove_suffix(pkg_dir, package.replace('.', os.sep)) + + return pkg_base, pkg_dir + + +def collect_submodules(package, filter=lambda name: True): + """ + :param package: A string which names the package which will be search for + submodules. + :param approve: A function to filter through the submodules found, + selecting which should be included in the returned list. It takes one + argument, a string, which gives the name of a submodule. Only if the + function returns true is the given submodule is added to the list of + returned modules. For example, ``filter=lambda name: 'test' not in + name`` will return modules that don't contain the word ``test``. + :return: A list of strings which specify all the modules in package. Its + results can be directly assigned to ``hiddenimports`` in a hook script; + see, for example, ``hook-sphinx.py``. + + This function is used only for hook scripts, but not by the body of + PyInstaller. + """ + # Accept only strings as packages. + if not isinstance(package, string_types): + raise TypeError('package must be a str') + + logger.debug('Collecting submodules for %s' % package) + # Skip a module which is not a package. + if not is_package(package): + logger.debug('collect_submodules - Module %s is not a package.' % package) + return [] + + # Determine the filesystem path to the specified package. + pkg_base, pkg_dir = get_package_paths(package) + + # Walk the package. Since this performs imports, do it in a separate + # process. Because module import may result in exta output to stdout, + # we enclose the output module names with special prefix and suffix. + names = exec_statement(""" + import sys + import pkgutil + import traceback + + # ``pkgutil.walk_packages`` doesn't walk subpackages of zipped files + # per https://bugs.python.org/issue14209. This is a workaround. + def walk_packages(path=None, prefix='', onerror=None): + def seen(p, m={{}}): + if p in m: + return True + m[p] = True + + for importer, name, ispkg in pkgutil.iter_modules(path, prefix): + if not name.startswith(prefix): ## Added + name = prefix + name ## Added + yield importer, name, ispkg + + if ispkg: + try: + __import__(name) + except ImportError: + if onerror is not None: + onerror(name) + except Exception: + if onerror is not None: + onerror(name) + else: + traceback.print_exc(file=sys.stderr) + print("collect_submodules: failed to import %r!" % + name, file=sys.stderr) + else: + path = getattr(sys.modules[name], '__path__', None) or [] + + # don't traverse path items we've seen before + path = [p for p in path if not seen(p)] + + ## Use Py2 code here. It still works in Py3. + for item in walk_packages(path, name+'.', onerror): + yield item + ## This is the original Py3 code. + #yield from walk_packages(path, name+'.', onerror) + + for module_loader, name, ispkg in walk_packages([{}], '{}.'): + print('\\n$_pyi:' + name + '*') + """.format( + # Use repr to escape Windows backslashes. + repr(pkg_dir), package)) + + # Include the package itself in the results. + mods = {package} + # Filter through the returend submodules. + for name in names.split(): + # Filter out extra output during module imports by checking + # for the special prefix and suffix + if name.startswith("$_pyi:") and name.endswith("*"): + name = name[6:-1] + else: + continue + + if filter(name): + mods.add(name) + + logger.debug("collect_submodules - Found submodules: %s", mods) + return list(mods) + + +def is_module_or_submodule(name, mod_or_submod): + """ + This helper function is designed for use in the ``filter`` argument of + ``collect_submodules``, by returning ``True`` if the given ``name`` is + a module or a submodule of ``mod_or_submod``. For example: + ``collect_submodules('foo', lambda name: not is_module_or_submodule(name, + 'foo.test'))`` excludes ``foo.test`` and ``foo.test.one`` but not + ``foo.testifier``. + """ + return name.startswith(mod_or_submod + '.') or name == mod_or_submod + + +# Patterns of dynamic library filenames that might be bundled with some +# installed Python packages. +PY_DYLIB_PATTERNS = [ + '*.dll', + '*.dylib', + 'lib*.so', +] + + +def collect_dynamic_libs(package, destdir=None): + """ + This routine produces a list of (source, dest) of dynamic library + files which reside in package. Its results can be directly assigned to + ``binaries`` in a hook script. The package parameter must be a string which + names the package. + + :param destdir: Relative path to ./dist/APPNAME where the libraries + should be put. + """ + # Accept only strings as packages. + if not isinstance(package, string_types): + raise TypeError('package must be a str') + + logger.debug('Collecting dynamic libraries for %s' % package) + pkg_base, pkg_dir = get_package_paths(package) + # Walk through all file in the given package, looking for dynamic libraries. + dylibs = [] + for dirpath, _, __ in os.walk(pkg_dir): + # Try all file patterns in a given directory. + for pattern in PY_DYLIB_PATTERNS: + files = glob.glob(os.path.join(dirpath, pattern)) + for source in files: + # Produce the tuple + # (/abs/path/to/source/mod/submod/file.pyd, + # mod/submod/file.pyd) + if destdir: + # Libraries will be put in the same directory. + dest = destdir + else: + # The directory hierarchy is preserved as in the original package. + dest = remove_prefix(dirpath, os.path.dirname(pkg_base) + os.sep) + logger.debug(' %s, %s' % (source, dest)) + dylibs.append((source, dest)) + return dylibs + + +def collect_data_files(package, include_py_files=False, subdir=None, + excludes=None, includes=None): + r""" + This routine produces a list of ``(source, dest)`` non-Python (i.e. data) + files which reside in ``package``. Its results can be directly assigned to + ``datas`` in a hook script; see, for example, ``hook-sphinx.py``. + Parameters: + + - The ``package`` parameter is a string which names the package. + - By default, all Python executable files (those ending in ``.py``, + ``.pyc``, and so on) will NOT be collected; setting the + ``include_py_files`` argument to ``True`` collects these files as well. + This is typically used with Python routines (such as those in + ``pkgutil``) that search a given directory for Python executable files + then load them as extensions or plugins. + - The ``subdir`` argument gives a subdirectory relative to ``package`` to + search, which is helpful when submodules are imported at run-time from a + directory lacking ``__init__.py``. + - The ``excludes`` argument contains a sequence of strings or Paths. These + provide a list of `globs `_ + to exclude from the collected data files; if a directory matches the + provided glob, all files it contains will be excluded as well. All + elements must be relative paths, which are relative to the provided + package's path (/ ``subdir`` if provided). + + Therefore, ``*.txt`` will exclude only ``.txt`` files in ``package``\ 's + path, while ``**/*.txt`` will exclude all ``.txt`` files in + ``package``\ 's path and all its subdirectories. Likewise, + ``**/__pycache__`` will exclude all files contained in any subdirectory + named ``__pycache__``. + - The ``includes`` function like ``excludes``, but only include matching + paths. ``excludes`` override ``includes``: a file or directory in both + lists will be excluded. + + This function does not work on zipped Python eggs. + + This function is used only for hook scripts, but not by the body of + PyInstaller. + """ + logger.debug('Collecting data files for %s' % package) + + # Accept only strings as packages. + if not isinstance(package, string_types): + raise TypeError('package must be a str') + + # Compute the root path for the provided patckage. + pkg_base, pkg_dir = get_package_paths(package) + if subdir: + pkg_dir = os.path.join(pkg_dir, subdir) + pkg_base = os.path.dirname(pkg_base) + # Ensure `pkg_base` ends with a single slash + # Subtle difference on Windows: In some cases `dirname` keeps the + # trailing slash, e.g. dirname("//aaa/bbb/"), see issue #4707. + if not pkg_base.endswith(os.sep): + pkg_base += os.sep + + # Make sure the excludes are a list; this also makes a copy, so we don't + # modify the original. + excludes = list(excludes) if excludes else [] + # These excludes may contain direcories which need to be searched. + excludes_len = len(excludes) + # Including py files means don't exclude them. This pattern will search any + # directories for containing files, so don't modify ``excludes_len``. + if not include_py_files: + excludes += ['**/*' + s for s in ALL_SUFFIXES] + + # Exclude .pyo files if include_py_files is False. + if not include_py_files and ".pyo" not in ALL_SUFFIXES: + excludes.append('**/*.pyo') + + # If not specified, include all files. Follow the same process as the + # excludes. + includes = list(includes) if includes else ["**/*"] + includes_len = len(includes) + + # Determine what source files to use. + sources = set() + + # A helper function to glob the in/ex "cludes", adding a wildcard to refer + # to all files under a subdirectory if a subdirectory is matched by the + # first ``clude_len`` patterns. Otherwise, it in/excludes the matched file. + # **This modifies** ``cludes``. + def clude_walker( + # A list of paths relative to ``pkg_dir`` to in/exclude. + cludes, + # The number of ``cludes`` for which matching directories should be + # searched for all files under them. + clude_len, + # True if the list is includes, False for excludes. + is_include + ): + for i, c in enumerate(cludes): + for g in Path(pkg_dir).glob(c): + if g.is_dir(): + # Only files are sources. Subdirectories are not. + if i < clude_len: + # In/exclude all files under a matching subdirectory. + cludes.append(str((g / "**/*").relative_to(pkg_dir))) + else: + # In/exclude a matching file. + sources.add(g) if is_include else sources.discard(g) + clude_walker(includes, includes_len, True) + clude_walker(excludes, excludes_len, False) + + # Tranform the sources into tuples for ``datas``. + datas = [(str(s), remove_prefix(str(s.parent), pkg_base)) for s in sources] + + logger.debug("collect_data_files - Found files: %s", datas) + return datas + + +def collect_system_data_files(path, destdir=None, include_py_files=False): + """ + This routine produces a list of (source, dest) non-Python (i.e. data) + files which reside somewhere on the system. Its results can be directly + assigned to ``datas`` in a hook script. + + This function is used only for hook scripts, but not by the body of + PyInstaller. + """ + # Accept only strings as paths. + if not isinstance(path, string_types): + raise TypeError('path must be a str') + # The call to ``remove_prefix`` below assumes a path separate of ``os.sep``, + # which may not be true on Windows; Windows allows Linux path separators in + # filenames. Fix this by normalizing the path. + path = os.path.normpath(path) + # Ensure `path` ends with a single slash + # Subtle difference on Windows: In some cases `dirname` keeps the + # trailing slash, e.g. dirname("//aaa/bbb/"), see issue #4707. + if not path.endswith(os.sep): + path += os.sep + + # Walk through all file in the given package, looking for data files. + datas = [] + for dirpath, dirnames, files in os.walk(path): + for f in files: + extension = os.path.splitext(f)[1] + if include_py_files or (extension not in PY_IGNORE_EXTENSIONS): + # Produce the tuple + # (/abs/path/to/source/mod/submod/file.dat, + # mod/submod/destdir) + source = os.path.join(dirpath, f) + dest = remove_prefix(dirpath, path) + if destdir is not None: + dest = os.path.join(destdir, dest) + datas.append((source, dest)) + + return datas + + +def copy_metadata(package_name): + """ + This function returns a list to be assigned to the ``datas`` global + variable. This list instructs PyInstaller to copy the metadata for the + given package to PyInstaller's data directory. + + Parameters + ---------- + package_name : str + Specifies the name of the package for which metadata should be copied. + + Returns + ---------- + list + This should be assigned to ``datas``. + + Examples + ---------- + >>> from PyInstaller.utils.hooks import copy_metadata + >>> copy_metadata('sphinx') + [('c:\\python27\\lib\\site-packages\\Sphinx-1.3.2.dist-info', + 'Sphinx-1.3.2.dist-info')] + """ + + # Some notes: to look at the metadata locations for all installed + # packages:: + # + # for key, value in pkg_resources.working_set.by_key.iteritems(): + # print('{}: {}'.format(key, value.egg_info)) + # + # Looking at this output, I see three general types of packages: + # + # 1. ``pypubsub: c:\python27\lib\site-packages\pypubsub-3.3.0-py2.7.egg\EGG-INFO`` + # 2. ``codechat: c:\users\bjones\documents\documentation\CodeChat.egg-info`` + # 3. ``zest.releaser: c:\python27\lib\site-packages\zest.releaser-6.2.dist-info`` + # 4. ``pyserial: None`` + # + # The first item shows that some metadata will be nested inside an egg. I + # assume we'll have to deal with zipped eggs, but I don't have any examples + # handy. The second and third items show different naming conventions for + # the metadata-containing directory. The fourth item shows a package with no + # metadata. + # + # So, in cases 1-3, copy the metadata directory. In case 4, emit an error + # -- there's no metadata to copy. + # See https://pythonhosted.org/setuptools/pkg_resources.html#getting-or-creating-distributions. + # Unfortunately, there's no documentation on the ``egg_info`` attribute; it + # was found through trial and error. + dist = pkg_resources.get_distribution(package_name) + metadata_dir = dist.egg_info + # Determine a destination directory based on the standardized egg name for + # this distribution. This avoids some problems discussed in + # https://github.com/pyinstaller/pyinstaller/issues/1888. + dest_dir = '{}.egg-info'.format(dist.egg_name()) + # Per https://github.com/pyinstaller/pyinstaller/issues/1888, ``egg_info`` + # isn't always defined. Try a workaround based on a suggestion by + # @benoit-pierre in that issue. + if metadata_dir is None: + # We assume that this is an egg, so guess a name based on `egg_name() + # `_. + metadata_dir = os.path.join(dist.location, dest_dir) + + assert os.path.exists(metadata_dir) + logger.debug('Package {} metadata found in {} belongs in {}'.format( + package_name, metadata_dir, dest_dir)) + + return [(metadata_dir, dest_dir)] + + +def get_installer(module): + """ + Try to find which package manager installed a module. + + :param module: Module to check + :return: Package manager or None + """ + file_name = get_module_file_attribute(module) + site_dir = file_name[:file_name.index('site-packages') + len('site-packages')] + # This is necessary for situations where the project name and module name don't match, i.e. + # Project name: pyenchant Module name: enchant + pkgs = pkg_resources.find_distributions(site_dir) + package = None + for pkg in pkgs: + if module.lower() in pkg.key: + package = pkg + break + metadata_dir, dest_dir = copy_metadata(package)[0] + # Check for an INSTALLER file in the metedata_dir and return the first line + # which should be the program that installed the module. + installer_file = os.path.join(metadata_dir, 'INSTALLER') + if os.path.isdir(metadata_dir) and os.path.exists(installer_file): + with open_file(installer_file, 'r') as installer_file_object: + lines = installer_file_object.readlines() + if lines[0] != '': + installer = lines[0].rstrip('\r\n') + logger.debug( + 'Found installer: \'{0}\' for module: \'{1}\' from package: \'{2}\''.format(installer, module, + package)) + return installer + if is_darwin: + try: + output = exec_command_stdout('port', 'provides', file_name) + if 'is provided by' in output: + logger.debug( + 'Found installer: \'macports\' for module: \'{0}\' from package: \'{1}\''.format(module, package)) + return 'macports' + except ExecCommandFailed: + pass + real_path = os.path.realpath(file_name) + if 'Cellar' in real_path: + logger.debug( + 'Found installer: \'homebrew\' for module: \'{0}\' from package: \'{1}\''.format(module, package)) + return 'homebrew' + return None + + +# ``_map_distribution_to_packages`` is expensive. Compute it when used, then +# return the memoized value. This is a simple alternative to +# ``functools.lru_cache``. +def _memoize(f): + memo = [] + + def helper(): + if not memo: + memo.append(f()) + return memo[0] + + return helper + + +# Walk through every package, determining which distribution it is in. +@_memoize +def _map_distribution_to_packages(): + logger.info('Determining a mapping of distributions to packages...') + dist_to_packages = {} + for p in sys.path: + # The path entry ``''`` refers to the current directory. + if not p: + p = '.' + # Ignore any entries in ``sys.path`` that don't exist. + try: + lds = os.listdir(p) + except Exception: + pass + else: + for ld in lds: + # Not all packages belong to a distribution. Skip these. + try: + dist = pkg_resources.get_distribution(ld) + except Exception: + pass + else: + dist_to_packages.setdefault(dist.key, []).append(ld) + + return dist_to_packages + + +# Given a ``package_name`` as a string, this function returns a list of packages +# needed to satisfy the requirements. This output can be assigned directly to +# ``hiddenimports``. +def requirements_for_package(package_name): + hiddenimports = [] + + dist_to_packages = _map_distribution_to_packages() + for requirement in pkg_resources.get_distribution(package_name).requires(): + if requirement.key in dist_to_packages: + required_packages = dist_to_packages[requirement.key] + hiddenimports.extend(required_packages) + else: + logger.warning('Unable to find package for requirement %s from ' + 'package %s.', + requirement.project_name, package_name) + + logger.info('Packages required by %s:\n%s', package_name, hiddenimports) + return hiddenimports + + +# Given a package name as a string, return a tuple of ``datas, binaries, +# hiddenimports`` containing all data files, binaries, and modules in the given +# package. The value of ``include_py_files`` is passed directly to +# ``collect_data_files``. +# +# Typical use: ``datas, binaries, hiddenimports = collect_all('my_module_name')``. +def collect_all( + package_name, include_py_files=True, filter_submodules=None, + exclude_datas=None, include_datas=None): + datas = [] + try: + datas += copy_metadata(package_name) + except Exception as e: + logger.warning('Unable to copy metadata for %s: %s', package_name, e) + datas += collect_data_files(package_name, include_py_files, + excludes=exclude_datas, includes=include_datas) + binaries = collect_dynamic_libs(package_name) + if filter_submodules: + hiddenimports = collect_submodules(package_name, + filter=filter_submodules) + else: + hiddenimports = collect_submodules(package_name) + try: + hiddenimports += requirements_for_package(package_name) + except Exception as e: + logger.warning('Unable to determine requirements for %s: %s', + package_name, e) + + return datas, binaries, hiddenimports + + +def collect_entry_point(name: str) -> Tuple[list, list]: + """Collect modules and metadata for all exporters of a given entry point. + + Args: + name: + The name of the entry point. Check the documentation for the + library which uses the entry point to find out its name. + Returns: + A ``(datas, hiddenimports)`` pair which should be assigned to the + ``datas`` and ``hiddenimports`` globals respectively. + + For libraries, such as ``pytest`` or ``keyring``, which rely on plugins to + extend their behaviour. + + Examples: + Pytest uses an entry point called ``'pytest11'`` for its extensions. + To collect all those extensions use:: + + datas, hiddenimports = collect_entry_point("pytest11") + + These values may be used in a hook or added to the ``datas`` and + ``hiddenimports`` arguments in the ``.spec`` file. See :ref:`using spec + files`. + + .. versionadded:: 5.0 + + """ + import pkg_resources + datas = [] + imports = [] + for dist in pkg_resources.iter_entry_points(name): + datas += copy_metadata(dist.dist.project_name) + imports.append(dist.module_name) + return datas, imports + + +if is_pure_conda: + from . import conda as conda_support # noqa: F401 +elif is_conda: + from .conda import CONDA_META_DIR as _tmp + logger.warning( + "Assuming this isn't an Anaconda environment or an additional venv/" + "pipenv/... environment manager is being used on top because the " + "conda-meta folder %s doesn't exist.", _tmp) + del _tmp + + +# These imports need to be here due to these modules recursively importing this module. +from .django import * +from .gi import * +from .qt import * +from .win32 import * diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/conda.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/conda.py new file mode 100644 index 0000000000000000000000000000000000000000..6669923defaeac65227fd16692367f7842c3acbb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/conda.py @@ -0,0 +1,401 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2020, 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) +#----------------------------------------------------------------------------- + +# language=rst +""" +Additional helper methods for working specifically with Anaconda distributions +are found at :attr:`PyInstaller.util.hooks.conda_support` which is designed to +mimic (albeit loosely) the `importlib.metadata`_ package. These functions find +and parse the distribution metadata from json files located in the +``conda-meta`` directory. + +.. versionadded:: 4.2.0 + +This module is available only if run inside a Conda environment. Usage of this +module should therefore be wrapped in a conditional clause:: + + from PyInstaller.utils.hooks import is_pure_conda + + if is_pure_conda: + from PyInstaller.utils.hooks import conda_support + + # Code goes here. e.g. + binaries = conda_support.collect_dynamic_libs("numpy") + ... + +Packages are all referenced by the *distribution name* you use to install it, +rather than the *package name* you import it with. i.e Use +``distribution("pillow")`` instead of ``distribution("PIL")`` or use +``package_distribution("PIL")``. +""" + +import sys +from pathlib import Path +import json +import fnmatch + +from PyInstaller import compat +from PyInstaller.log import logger + +if compat.is_py38: + from importlib.metadata import PackagePath as _PackagePath +else: + from importlib_metadata import PackagePath as _PackagePath + +# Conda virtual environments each get their own copy of `conda-meta` so the +# use of `sys.prefix` instead of `sys.base_prefix`, `sys.real_prefix` or +# anything from our `compat` module is intentional. +CONDA_ROOT = Path(sys.prefix) +CONDA_META_DIR = CONDA_ROOT / "conda-meta" + +# Find all paths in `sys.path` that are inside Conda root. +PYTHONPATH_PREFIXES = [] +for _path in sys.path: + _path = Path(_path) + try: + PYTHONPATH_PREFIXES.append(_path.relative_to(sys.prefix)) + except ValueError: + pass + +PYTHONPATH_PREFIXES.sort(key=lambda p: len(p.parts), reverse=True) + + +class Distribution(object): + """A bucket class representation of a Conda distribution. + + This bucket exports the following attributes: + + :ivar name: The distribution's name. + :ivar version: Its version. + :ivar files: All filenames as :meth:`PackagePath`\\ s included with this + distribution. + :ivar dependencies: Names of other distributions that this distribution + depends on (with version constraints removed). + :ivar packages: Names of importable packages included in this distribution. + + This class is not intended to be constructed directly by users. Rather use + :meth:`distribution` or :meth:`package_distribution` to provide one for + you. + """ + def __init__(self, json_path): + try: + self._json_path = Path(json_path) + assert self._json_path.exists() + except (TypeError, AssertionError): + raise TypeError( + "Distribution requires a path to a conda-meta json. Perhaps " + "you want `distribution({})` instead?".format(repr(json_path))) + + # Everything we need (including this distribution's name) is kept in + # the metadata json. + self.raw = json.loads(self._json_path.read_text()) + + # Unpack the more useful contents of the json. + self.name = self.raw["name"] + self.version = self.raw["version"] + self.files = [PackagePath(i) for i in self.raw["files"]] + self.dependencies = self._init_dependencies() + self.packages = self._init_package_names() + + def __repr__(self): + return "{}(name=\"{}\", packages={})".format( + type(self).__name__, self.name, self.packages) + + def _init_dependencies(self): + """ + Read dependencies from ``self.raw["depends"]``. + + :return: Dependent distribution names. + :rtype: list + + The names in ``self.raw["depends"]`` come with extra version + constraint information which must be stripped. + """ + dependencies = [] + # For each dependency: + for dependency in self.raw["depends"]: + # ``dependency`` is a string of the form: + # "[name] [version constraints]" + name, *version_constraints = dependency.split(maxsplit=1) + dependencies.append(name) + return dependencies + + def _init_package_names(self): + """ + Search ``self.files`` for package names shipped by this distribution. + + :return: Package names. + :rtype: list + + These are names you would ``import`` rather than names you would + install. + """ + packages = [] + for file in self.files: + package = _get_package_name(file) + if package is not None: + packages.append(package) + return packages + + @classmethod + def from_name(cls, name): + """Get distribution information for a given distribution **name** + (i.e. something you would ``conda install``). + + :rtype: :class:`Distribution` + """ + if name in distributions: + return distributions[name] + raise ModuleNotFoundError( + "Distribution {} is either not installed or was not installed " + "using Conda.".format(name)) + + @classmethod + def from_package_name(cls, name): + """Get distribution information for a **package** (i.e. something you'd + import). + + :rtype: :class:`Distribution` + + For example, the package ``pkg_resources`` belongs to the distribution + ``setuptools``, which contains three packages. + + >>> package_distribution("pkg_resources") + Distribution(name="setuptools", + packages=['easy_install', 'pkg_resources', 'setuptools']) + """ + if name in distributions_by_package: + return distributions_by_package[name] + raise ModuleNotFoundError( + "Package {} is either not installed or was not installed using " + "Conda.".format(name)) + + +distribution = Distribution.from_name +package_distribution = Distribution.from_package_name + + +class PackagePath(_PackagePath): + """ + A filename relative to Conda's root (``sys.prefix``). + + This class inherits from :class:`pathlib.PurePosixPath` even on non-Posix + OSs. To convert to a :class:`pathlib.Path` pointing to the real file use + the :meth:`locate` method. + """ + def locate(self): + """Return a path-like object for this path pointing to the file's + true location. + """ + return Path(sys.prefix) / self + + +def walk_dependency_tree(initial, excludes=None): + """ + Collect a :class:`Distribution` and all direct and indirect + dependencies of that distribution. + + :param initial: Distribution name to collect from. + :type initial: str + :param excludes: Distributions to exclude, defaults to ``None``. + :type excludes: iterable of str, optional + :return: A ``{name: distribution}`` dictionary where ``distribution`` + is the output of ``conda_support.distribution(name)``. + :rtype: dict + """ + if excludes is not None: + excludes = set(excludes) + + # Rather than use true recursion, mimic it with a to-do queue. + from collections import deque + done = {} + names_to_do = deque([initial]) + + while names_to_do: + # Grab a distribution name from the to-do list. + name = names_to_do.pop() + try: + # Collect and save it's metadata. + done[name] = distribution = Distribution.from_name(name) + logger.debug("Collected Conda distribution '%s', " + "a dependency of '%s'.", name, initial) + except ModuleNotFoundError: + logger.warning( + "Conda distribution '%s', dependency of '%s', was not found. " + "If you installed this distribution with pip then you may " + "ignore this warning.", name, initial) + continue + # For each dependency: + for _name in distribution.dependencies: + if _name in done: + # Skip anything already done. + continue + if _name == name: + # Avoid infinite recursion if a distribution depends on itself. + # This probably will ever happen but I certainly wouldn't + # chance it. + continue + if excludes is not None and _name in excludes: + # Don't recurse to excluded dependencies. + continue + names_to_do.append(_name) + return done + + +def _iter_distributions(name, dependencies, excludes): + if dependencies: + return walk_dependency_tree(name, excludes).values() + else: + return [Distribution.from_name(name)] + + +def requires(name, strip_versions=False): + """ + List requirements of a distribution. + + :param name: The name of the distribution. + :param strip_versions: List only their names, not their version + constraints. + :return: List of distribution names. + """ + if strip_versions: + return distribution(name).dependencies + return distribution(name).raw["depends"] + + +def files(name, dependencies=False, excludes=None): + """ + List all files belonging to a distribution. + + :param name: The name of the distribution. + :param dependencies: Recursively collect files of dependencies too. + :type dependencies: bool + :param excludes: Distributions to ignore if **dependencies** is true. + :return: List of :class:`PackagePath`\\ s. + + With ``dependencies=False``, this is just a shortcut for:: + + conda_support.distribution(name).files + """ + return [file + for dist in _iter_distributions(name, dependencies, excludes) + for file in dist.files] + + +if compat.is_win: + lib_dir = PackagePath("Library", "bin") +else: + lib_dir = PackagePath("lib") + + +def collect_dynamic_libs(name, dest=".", dependencies=True, excludes=None): + """ + Collect DLLs for distribution **name**. + + :param name: The distribution's project-name. + :type name: str + :param dest: Target destination, defaults to ``'.'``. + :type dest: str, optional + :param dependencies: Recursively collect libs for dependent distributions + (recommended). + :type dependencies: bool, optional + :param excludes: Dependent distributions to skip, defaults to ``None``. + :type excludes: iterable, optional + :return: List of DLLs in PyInstaller's ``(source, dest)`` format. + :rtype: list + + This collects libraries only from Conda's shared ``lib`` (Unix) or + ``Library/bin`` (Windows) folders. To collect from inside a distribution's + installation use the regular + :meth:`PyInstaller.utils.hooks.collect_collect_dynamic_libs`. + """ + _files = [] + for file in files(name, dependencies, excludes): + # A file is classified as a DLL if it lives inside the dedicated + # ``lib_dir`` DLL folder. + if file.parent == lib_dir: + _files.append((str(file.locate()), dest)) + return _files + + +# --- Map packages to distributions and vice-versa --- + + +def _get_package_name(file): + """Determine the package name of a Python file in ``sys.path``. + + :param file: A Python filename relative to Conda root (sys.prefix). + :type file: PackagePath + :return: Package name or None. + + This function only considers single file packages e.g. ``foo.py`` or + top level ``foo/__init__.py``\\ s. Anything else is ignored (returning + ``None``). + """ + file = Path(file) + # TODO: Handle PEP 420 namespace packages (which are missing `__init__` + # module). No such Conda PEP 420 namespace packages are known. + + # Get top-level folders by finding parents of `__init__.xyz`s + if file.stem == "__init__" and file.suffix in compat.ALL_SUFFIXES: + file = file.parent + elif file.suffix not in compat.ALL_SUFFIXES: + # Keep single-file packages but skip DLLs, data and junk files. + return + + # Check if this file/folder's parent is in ``sys.path`` i.e. it's directly + # importable. This intentionally excludes submodules which would cause + # confusion because ``sys.prefix`` is in ``sys.path``, meaning that + # every file in an Conda installation is a submodule. + for prefix in PYTHONPATH_PREFIXES: + if len(file.parts) != len(prefix.parts) + 1: + # This check is redundant but speeds it up quite a bit. + continue + # There are no wildcards involved here. The use of ``fnmatch`` is + # simply to handle the `if case-insensitive file system: use + # case-insensitive string matching.` + if fnmatch.fnmatch(str(file.parent), str(prefix)): + return file.stem + + +# All the information we want is organised the wrong way. + +# We want to look up distribution based on package names but we can only search +# for packages using distribution names. And we'd like to search for a +# distribution's json file but, due to the noisy filenames of the jsons, we can +# only find a json's distribution rather than a distribution's json. + +# So we have to read everything, then regroup distributions in the ways we want +# them grouped. This will likely be a spectacular bottleneck on full blown +# Conda (non miniconda) with 250+ packages by default at several GiBs. I +# suppose we could cache this on a per-json basis if it gets too much. + + +def _init_distributions(): + distributions = {} + for path in CONDA_META_DIR.glob("*.json"): + dist = Distribution(path) + distributions[dist.name] = dist + return distributions + + +distributions = _init_distributions() + + +def _init_packages(): + distributions_by_package = {} + for distribution in distributions.values(): + for package in distribution.packages: + distributions_by_package[package] = distribution + return distributions_by_package + + +distributions_by_package = _init_packages() diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/django.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/django.py new file mode 100644 index 0000000000000000000000000000000000000000..0448c3a93df2a871e5d9801fee283c765bcf495c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/django.py @@ -0,0 +1,79 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +from ..hooks import eval_script +from ...utils import misc + + +__all__ = [ + 'django_dottedstring_imports', 'django_find_root_dir' +] + + +def django_dottedstring_imports(django_root_dir): + """ + Get all the necessary Django modules specified in settings.py. + + In the settings.py the modules are specified in several variables + as strings. + """ + pths = [] + # Extend PYTHONPATH with parent dir of django_root_dir. + pths.append(misc.get_path_to_toplevel_modules(django_root_dir)) + # Extend PYTHONPATH with django_root_dir. + # Often, Django users do not specify absolute imports in the settings + # module. + pths.append(django_root_dir) + + default_settings_module = os.path.basename(django_root_dir) + '.settings' + settings_module = os.environ.get('DJANGO_SETTINGS_MODULE', default_settings_module) + env = {'DJANGO_SETTINGS_MODULE': settings_module, + 'PYTHONPATH': os.pathsep.join(pths)} + ret = eval_script('django_import_finder.py', env=env) + + return ret + + +def django_find_root_dir(): + """ + Return path to directory (top-level Python package) that contains main django + files. Return None if no directory was detected. + + Main Django project directory contain files like '__init__.py', 'settings.py' + and 'url.py'. + + In Django 1.4+ the script 'manage.py' is not in the directory with 'settings.py' + but usually one level up. We need to detect this special case too. + """ + # 'PyInstaller.config' cannot be imported as other top-level modules. + from ...config import CONF + # Get the directory with manage.py. Manage.py is supplied to PyInstaller as the + # first main executable script. + manage_py = CONF['main_script'] + manage_dir = os.path.dirname(os.path.abspath(manage_py)) + + # Get the Django root directory. The directory that contains settings.py and url.py. + # It could be the directory containing manage.py or any of its subdirectories. + settings_dir = None + files = set(os.listdir(manage_dir)) + if ('settings.py' in files or 'settings' in files) and 'urls.py' in files: + settings_dir = manage_dir + else: + for f in files: + if os.path.isdir(os.path.join(manage_dir, f)): + subfiles = os.listdir(os.path.join(manage_dir, f)) + # Subdirectory contains critical files. + if ('settings.py' in subfiles or 'settings' in subfiles) and 'urls.py' in subfiles: + settings_dir = os.path.join(manage_dir, f) + break # Find the first directory. + + return settings_dir diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/gi.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/gi.py new file mode 100644 index 0000000000000000000000000000000000000000..062d2ea8eca1ef36d89ce5a5529ecd158cc54e9b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/gi.py @@ -0,0 +1,261 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 re + +from ..hooks import collect_submodules, collect_system_data_files, eval_statement, exec_statement +from ... import log as logging +from ...compat import base_prefix, is_darwin, is_win, open_file, \ + text_read_mode +from ...depend.bindepend import findSystemLibrary + +logger = logging.getLogger(__name__) + + +__all__ = [ + 'get_gi_libdir', 'get_gi_typelibs', 'gir_library_path_fix', + 'get_glib_system_data_dirs', 'get_glib_sysconf_dirs', + 'collect_glib_share_files', 'collect_glib_etc_files', + 'collect_glib_translations' +] + + +def get_gi_libdir(module, version): + statement = """ + import gi + gi.require_version("GIRepository", "2.0") + from gi.repository import GIRepository + repo = GIRepository.Repository.get_default() + module, version = (%r, %r) + repo.require(module, version, + GIRepository.RepositoryLoadFlags.IREPOSITORY_LOAD_FLAG_LAZY) + print(repo.get_shared_library(module)) + """ + statement %= (module, version) + libs = exec_statement(statement).split(',') + for lib in libs: + path = findSystemLibrary(lib.strip()) + return os.path.normpath(os.path.dirname(path)) + + raise ValueError("Could not find libdir for %s-%s" % (module, version)) + + +def get_gi_typelibs(module, version): + """ + Return a tuple of (binaries, datas, hiddenimports) to be used by PyGObject + related hooks. Searches for and adds dependencies recursively. + + :param module: GI module name, as passed to 'gi.require_version()' + :param version: GI module version, as passed to 'gi.require_version()' + """ + datas = [] + binaries = [] + hiddenimports = [] + + statement = """ + import gi + gi.require_version("GIRepository", "2.0") + from gi.repository import GIRepository + repo = GIRepository.Repository.get_default() + module, version = (%r, %r) + repo.require(module, version, + GIRepository.RepositoryLoadFlags.IREPOSITORY_LOAD_FLAG_LAZY) + get_deps = getattr(repo, 'get_immediate_dependencies', None) + if not get_deps: + get_deps = repo.get_dependencies + print({'sharedlib': repo.get_shared_library(module), + 'typelib': repo.get_typelib_path(module), + 'deps': get_deps(module) or []}) + """ + statement %= (module, version) + typelibs_data = eval_statement(statement) + if not typelibs_data: + logger.error("gi repository 'GIRepository 2.0' not found. " + "Please make sure libgirepository-gir2.0 resp. " + "lib64girepository-gir2.0 is installed.") + # :todo: should we raise a SystemError here? + else: + logger.debug("Adding files for %s %s", module, version) + + if typelibs_data['sharedlib']: + for lib in typelibs_data['sharedlib'].split(','): + path = findSystemLibrary(lib.strip()) + if path: + logger.debug('Found shared library %s at %s', lib, path) + binaries.append((path, '.')) + + d = gir_library_path_fix(typelibs_data['typelib']) + if d: + logger.debug('Found gir typelib at %s', d) + datas.append(d) + + hiddenimports += collect_submodules('gi.overrides', + lambda name: name.endswith('.' + module)) + + # Load dependencies recursively + for dep in typelibs_data['deps']: + m, _ = dep.rsplit('-', 1) + hiddenimports += ['gi.repository.%s' % m] + + return binaries, datas, hiddenimports + + +def gir_library_path_fix(path): + import subprocess + # 'PyInstaller.config' cannot be imported as other top-level modules. + from ...config import CONF + + path = os.path.abspath(path) + + # On OSX we need to recompile the GIR files to reference the loader path, + # but this is not necessary on other platforms + if is_darwin: + + # If using a virtualenv, the base prefix and the path of the typelib + # have really nothing to do with each other, so try to detect that + common_path = os.path.commonprefix([base_prefix, path]) + if common_path == '/': + logger.debug("virtualenv detected? fixing the gir path...") + common_path = os.path.abspath(os.path.join(path, '..', '..', '..')) + + gir_path = os.path.join(common_path, 'share', 'gir-1.0') + + typelib_name = os.path.basename(path) + gir_name = os.path.splitext(typelib_name)[0] + '.gir' + + gir_file = os.path.join(gir_path, gir_name) + + if not os.path.exists(gir_path): + logger.error('Unable to find gir directory: %s.\n' + 'Try installing your platforms gobject-introspection ' + 'package.', gir_path) + return None + if not os.path.exists(gir_file): + logger.error('Unable to find gir file: %s.\n' + 'Try installing your platforms gobject-introspection ' + 'package.', gir_file) + return None + + with open_file(gir_file, text_read_mode, encoding='utf-8') as f: + lines = f.readlines() + # GIR files are `XML encoded `_, + # which means they are by definition encoded using UTF-8. + with open_file(os.path.join(CONF['workpath'], gir_name), 'w', + encoding='utf-8') as f: + for line in lines: + if 'shared-library' in line: + split = re.split('(=)', line) + files = re.split('(["|,])', split[2]) + for count, item in enumerate(files): + if 'lib' in item: + files[count] = '@loader_path/' + os.path.basename(item) + line = ''.join(split[0:2]) + ''.join(files) + f.write(line) + + # g-ir-compiler expects a file so we cannot just pipe the fixed file to it. + command = subprocess.Popen(('g-ir-compiler', os.path.join(CONF['workpath'], gir_name), + '-o', os.path.join(CONF['workpath'], typelib_name))) + command.wait() + + return os.path.join(CONF['workpath'], typelib_name), 'gi_typelibs' + else: + return path, 'gi_typelibs' + + +def get_glib_system_data_dirs(): + statement = """ + import gi + gi.require_version('GLib', '2.0') + from gi.repository import GLib + print(GLib.get_system_data_dirs()) + """ + data_dirs = eval_statement(statement) + if not data_dirs: + logger.error("gi repository 'GIRepository 2.0' not found. " + "Please make sure libgirepository-gir2.0 resp. " + "lib64girepository-gir2.0 is installed.") + # :todo: should we raise a SystemError here? + return data_dirs + + +def get_glib_sysconf_dirs(): + """Try to return the sysconf directories, eg /etc.""" + if is_win: + # On windows, if you look at gtkwin32.c, sysconfdir is actually + # relative to the location of the GTK DLL. Since that's what + # we're actually interested in (not the user path), we have to + # do that the hard way''' + return [os.path.join(get_gi_libdir('GLib', '2.0'), 'etc')] + + statement = """ + import gi + gi.require_version('GLib', '2.0') + from gi.repository import GLib + print(GLib.get_system_config_dirs()) + """ + data_dirs = eval_statement(statement) + if not data_dirs: + logger.error("gi repository 'GIRepository 2.0' not found. " + "Please make sure libgirepository-gir2.0 resp. " + "lib64girepository-gir2.0 is installed.") + # :todo: should we raise a SystemError here? + return data_dirs + + +def collect_glib_share_files(*path): + """path is relative to the system data directory (eg, /usr/share)""" + glib_data_dirs = get_glib_system_data_dirs() + if glib_data_dirs is None: + return [] + + destdir = os.path.join('share', *path) + + # TODO: will this return too much? + collected = [] + for data_dir in glib_data_dirs: + p = os.path.join(data_dir, *path) + collected += collect_system_data_files(p, destdir=destdir, include_py_files=False) + + return collected + + +def collect_glib_etc_files(*path): + """path is relative to the system config directory (eg, /etc)""" + glib_config_dirs = get_glib_sysconf_dirs() + if glib_config_dirs is None: + return [] + + destdir = os.path.join('etc', *path) + + # TODO: will this return too much? + collected = [] + for config_dir in glib_config_dirs: + p = os.path.join(config_dir, *path) + collected += collect_system_data_files(p, destdir=destdir, include_py_files=False) + + return collected + +_glib_translations = None + + +def collect_glib_translations(prog): + """ + Return a list of translations in the system locale directory whose names equal prog.mo. + """ + global _glib_translations + if _glib_translations is None: + _glib_translations = collect_glib_share_files('locale') + + names = [os.sep + prog + '.mo', + os.sep + prog + '.po'] + namelen = len(names[0]) + + return [(src, dst) for src, dst in _glib_translations if src[-namelen:] in names] diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/qt.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/qt.py new file mode 100644 index 0000000000000000000000000000000000000000..a061b1f7dfd42586f4724eb9aa3b857b5159151d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/qt.py @@ -0,0 +1,618 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import json +import glob + +from ..hooks import eval_statement, exec_statement, get_homebrew_path, \ + get_module_file_attribute +from PyInstaller.depend.bindepend import getImports, getfullnameof +from ... import log as logging +from ...compat import is_win, is_darwin, is_linux +from ...utils import misc + +logger = logging.getLogger(__name__) + + +# Qt5LibraryInfo +# -------------- +# This class uses introspection to determine the location of Qt5 files. This is +# essential to deal with the many variants of the PyQt5 package, each of which +# places files in a different location. Therefore, this class provides all +# members of `QLibraryInfo `_. +class Qt5LibraryInfo: + def __init__(self, namespace): + if namespace not in ['PyQt5', 'PySide2']: + raise Exception('Invalid namespace: {0}'.format(namespace)) + self.namespace = namespace + self.is_PyQt5 = namespace == 'PyQt5' + + # Initialize most of this class only when values are first requested from + # it. + def __getattr__(self, name): + if 'version' in self.__dict__: + # Initialization was already done, but requested attribute is not + # availiable. + raise AttributeError(name) + else: + # Ensure self.version exists, even if PyQt5/PySide2 can't be + # imported. Hooks and util functions use `if .version` to check + # whether PyQt5/PySide2 was imported and other attributes are + # expected to be available. This also serves as a marker that + # initialization was already done. + self.version = None + # Get library path information from Qt. See QLibraryInfo_. + json_str = exec_statement(""" + import sys + + # exec_statement only captures stdout. If there are + # errors, capture them to stdout so they can be displayed to the + # user. Do this early, in case PyQt5 imports produce stderr + # output. + sys.stderr = sys.stdout + + import json + try: + from %s.QtCore import QLibraryInfo, QCoreApplication + except: + print('False') + else: + # QLibraryInfo isn't always valid until a QCoreApplication is + # instantiated. + app = QCoreApplication(sys.argv) + paths = [x for x in dir(QLibraryInfo) if x.endswith('Path')] + location = {x: QLibraryInfo.location(getattr(QLibraryInfo, x)) + for x in paths} + try: + version = QLibraryInfo.version().segments() + except AttributeError: + version = [] + print(json.dumps({ + 'isDebugBuild': QLibraryInfo.isDebugBuild(), + 'version': version, + 'location': location, + })) + """ % self.namespace) + try: + qli = json.loads(json_str) + except Exception as e: + logger.warning('Cannot read QLibraryInfo output: raised %s when ' + 'decoding:\n%s', str(e), json_str) + qli = {} + + for k, v in qli.items(): + setattr(self, k, v) + + return getattr(self, name) + + +# Provide single instances of this class to avoid each hook constructing its own. +pyqt5_library_info = Qt5LibraryInfo('PyQt5') +pyside2_library_info = Qt5LibraryInfo('PySide2') + + +def qt_plugins_dir(namespace): + """ + Return list of paths searched for plugins. + + :param namespace: Import namespace, i.e., PyQt5 or PySide2 + + :return: Plugin directory paths + """ + if namespace not in ['PyQt5', 'PySide2']: + raise Exception('Invalid namespace: {0}'.format(namespace)) + if namespace == 'PyQt5': + paths = [pyqt5_library_info.location['PluginsPath']] + elif namespace == 'PySide2': + paths = [pyside2_library_info.location['PluginsPath']] + else: + paths = eval_statement(""" + from {0}.QtCore import QCoreApplication; + app = QCoreApplication([]); + print(list(app.libraryPaths())) + """.format(namespace)) + if not paths: + raise Exception('Cannot find {0} plugin directories'.format(namespace)) + else: + valid_paths = [] + for path in paths: + if os.path.isdir(path): + valid_paths.append(str(path)) # must be 8-bit chars for one-file builds + qt_plugin_paths = valid_paths + if not qt_plugin_paths: + raise Exception(""" + Cannot find existing {0} plugin directories + Paths checked: {1} + """.format(namespace, ", ".join(paths))) + return qt_plugin_paths + + +def qt_plugins_binaries(plugin_type, namespace): + """ + Return list of dynamic libraries formatted for mod.binaries. + + :param plugin_type: Plugin to look for + :param namespace: Import namespace, i.e., PyQt5 or PySide2 + + :return: Plugin directory path corresponding to the given plugin_type + """ + if namespace not in ['PyQt5', 'PySide2']: + raise Exception('Invalid namespace: {0}'.format(namespace)) + pdir = qt_plugins_dir(namespace=namespace) + files = [] + for path in pdir: + files.extend(misc.dlls_in_dir(os.path.join(path, plugin_type))) + + # Windows: + # + # dlls_in_dir() grabs all files ending with ``*.dll``, ``*.so`` and + # ``*.dylib`` in a certain directory. On Windows this would grab debug + # copies of Qt plugins, which then causes PyInstaller to add a dependency on + # the Debug CRT *in addition* to the release CRT. + if is_win and namespace in ['PyQt5', 'PySide2']: + files = [f for f in files if not f.endswith("d.dll")] + + logger.debug("Found plugin files %s for plugin %s", files, plugin_type) + if namespace == 'PyQt5': + plugin_dir = os.path.join('PyQt5', 'Qt', 'plugins') + else: + plugin_dir = os.path.join('PySide2', 'plugins') + dest_dir = os.path.join(plugin_dir, plugin_type) + binaries = [(f, dest_dir) for f in files] + return binaries + + +def qt_menu_nib_dir(namespace): + """ + Return path to Qt resource dir qt_menu.nib on OSX only. + + :param namespace: Import namespace, i.e., PyQt5 or PySide2 + + :return: Directory containing qt_menu.nib for specified namespace + """ + if namespace not in ['PyQt5', 'PySide2']: + raise Exception('Invalid namespace: {0}'.format(namespace)) + menu_dir = None + + path = exec_statement(""" + from {0}.QtCore import QLibraryInfo + path = QLibraryInfo.location(QLibraryInfo.LibrariesPath) + print(path) + """.format(namespace)) + anaconda_path = os.path.join(sys.exec_prefix, "python.app", "Contents", + "Resources") + paths = [os.path.join(path, 'Resources'), + os.path.join(path, 'QtGui.framework', 'Resources'), anaconda_path] + + for location in paths: + # Check directory existence + path = os.path.join(location, 'qt_menu.nib') + if os.path.exists(path): + menu_dir = path + logger.debug('Found qt_menu.nib for %s at %s', namespace, path) + break + if not menu_dir: + raise Exception(""" + Cannot find qt_menu.nib for {0} + Path checked: {1} + """.format(namespace, ", ".join(paths))) + return menu_dir + + +def get_qmake_path(version=''): + """ + Try to find the path to qmake with version given by the argument as a + string. + + :param version: qmake version + """ + import subprocess + + # Use QT[45]DIR if specified in the environment + if 'QT5DIR' in os.environ and version[0] == '5': + logger.debug('Using $QT5DIR/bin as qmake path') + return os.path.join(os.environ['QT5DIR'], 'bin', 'qmake') + if 'QT4DIR' in os.environ and version[0] == '4': + logger.debug('Using $QT4DIR/bin as qmake path') + return os.path.join(os.environ['QT4DIR'], 'bin', 'qmake') + + # try the default $PATH + dirs = [''] + + # try homebrew paths + for formula in ('qt', 'qt5'): + homebrewqtpath = get_homebrew_path(formula) + if homebrewqtpath: + dirs.append(homebrewqtpath) + + for directory in dirs: + try: + qmake = os.path.join(directory, 'bin', 'qmake') + versionstring = subprocess.check_output([qmake, '-query', + 'QT_VERSION']).strip() + # version string is probably just ASCII + versionstring = versionstring.decode('utf8') + if versionstring.find(version) == 0: + logger.debug('Found qmake version "%s" at "%s".', + versionstring, qmake) + return qmake + except (OSError, subprocess.CalledProcessError): + pass + logger.debug('Could not find qmake matching version "%s".', version) + return None + + +# Qt deployment approach +# ---------------------- +# This is the core of PyInstaller's approach to Qt deployment. It's based on: +# +# - Discovering the location of Qt5 libraries by introspection, using +# Qt5LibraryInfo_. This provides compatibility with many variants of Qt5 +# (conda, self-compiled, provided by a Linux distro, etc.) and many versions +# of Qt5, all of which vary in the location of Qt5 files. +# - Placing all frozen PyQt5/Qt5 files in a standard subdirectory layout, which +# matches the layout of the PyQt5 wheel on PyPI. This is necessary to support +# Qt5 installs which are not in a subdirectory of the PyQt5 wrappers. See +# ``loader/rthooks/pyi_rth_qt5.py`` for the use of environment variables to +# establish this layout. +# - Emitting warnings on missing QML and translation files which some +# installations don't have. +# - Determining additional files needed for deployment by following the Qt +# deployment process using `_qt_dynamic_dependencies_dict`_ and +# add_qt5_dependencies_. +# +# _qt_dynamic_dependencies_dict +# ----------------------------- +# This dictionary provides dynamics dependencies (plugins and translations) that +# can't be discovered using ``getImports``. It was built by combining +# information from: +# +# - Qt `deployment `_ docs. Specifically: +# +# - The `deploying Qt for Linux/X11 `_ +# page specifies including the Qt Platform Abstraction (QPA) plugin, +# ``libqxcb.so``. There's little other guidance provided. +# - The `Qt for Windows - Deployment `_ +# page likewise specifies the ``qwindows.dll`` QPA. This is found by the +# dependency walker, so it doesn't need to explicitly specified. +# +# - For dynamic OpenGL applications, the ``libEGL.dll``, +# ``libGLESv2.dll``, ``d3dcompiler_XX.dll`` (the XX is a version +# number), and ``opengl32sw.dll`` libraries are also needed. +# - If Qt was configured to use ICU, the ``icudtXX.dll``, +# ``icuinXX.dll``, and ``icuucXX.dll`` libraries are needed. +# +# These are included by ``hook-PyQt5.py``. +# +# - The `Qt for macOS - Deployment `_ +# page specifies the ``libqcocoa.dylib`` QPA, but little else. The +# `Mac deployment tool `_ +# provides the following rules: +# +# - The platform plugin is always deployed. +# - The image format plugins are always deployed. +# - The print support plugin is always deployed. +# - SQL driver plugins are deployed if the application uses the Qt SQL +# module. +# - Script plugins are deployed if the application uses the Qt Script +# module. +# - The SVG icon plugin is deployed if the application uses the Qt SVG +# module. +# - The accessibility plugin is always deployed. +# +# - Per the `Deploying QML Applications `_ +# page, QML-based applications need the ``qml/`` directory available. +# This is handled by ``hook-PyQt5.QtQuick.py``. +# - Per the `Deploying Qt WebEngine Applications `_ +# page, deployment may include: +# +# - Libraries (handled when PyInstaller following dependencies). +# - QML imports (if Qt Quick integration is used). +# - Qt WebEngine process, which should be located at +# ``QLibraryInfo::location(QLibraryInfo::LibraryExecutablesPath)`` +# for Windows and Linux, and in ``.app/Helpers/QtWebEngineProcess`` +# for Mac. +# - Resources: the files listed in deployWebEngineCore_. +# - Translations: on macOS: ``.app/Content/Resources``; on Linux and +# Windows: ``qtwebengine_locales`` directory in the directory +# specified by ``QLibraryInfo::location(QLibraryInfo::TranslationsPath)``. +# - Audio and video codecs: Probably covered if Qt5Multimedia is +# referenced? +# +# This is handled by ``hook-PyQt5.QtWebEngineWidgets.py``. +# +# - Since `QAxContainer `_ is a +# statically-linked library, it doesn't need any special handling. +# +# - Sources for the `Windows Deployment Tool `_ +# show more detail: +# +# - The `PluginModuleMapping struct `_ +# and the following ``pluginModuleMappings`` global provide a mapping +# between a plugin directory name and an `enum of Qt plugin names +# `_. +# - The `QtModuleEntry struct `_ +# and ``qtModuleEntries`` global connect this enum to the name of the Qt5 +# library it represents and to the translation files this library +# requires. (Ignore the ``option`` member -- it's just for command-line +# parsing.) +# +# Manually combining these two provides a mapping of Qt library names to the +# translation and plugin(s) needed by the library. The process is: take the +# key of the dict below from ``QtModuleEntry.libraryName``, but make it +# lowercase (since Windows files will be normalized to lowercase). The +# ``QtModuleEntry.translation`` provides the ``translation_base``. Match the +# ``QtModuleEntry.module`` with ``PluginModuleMapping.module`` to find the +# ``PluginModuleMapping.directoryName`` for the required plugin(s). +# +# - The `deployWebEngineCore `_ +# function copies the following files from ``resources/``, and also copies +# the web engine process executable. +# +# - ``icudtl.dat`` +# - ``qtwebengine_devtools_resources.pak`` +# - ``qtwebengine_resources.pak`` +# - ``qtwebengine_resources_100p.pak`` +# - ``qtwebengine_resources_200p.pak`` +# +# - Sources for the `Mac deployment tool`_ are less helpful. The `deployPlugins +# `_ +# function seems to: +# +# - Always include ``platforms/libqcocoa.dylib``. +# - Always include ``printsupport/libcocoaprintersupport.dylib`` +# - Include ``bearer/`` if ``QtNetwork`` is included (and some other +# condition I didn't look up). +# - Always include ``imageformats/``, except for ``qsvg``. +# - Include ``imageformats/qsvg`` if ``QtSvg`` is included. +# - Always include ``iconengines/``. +# - Include ``sqldrivers/`` if ``QtSql`` is included. +# - Include ``mediaservice/`` and ``audio/`` if ``QtMultimedia`` is +# included. +# +# The always includes will be handled by ``hook-PyQt5.py`` or +# ``hook-PySide2.py``; optional includes are already covered by the dict +# below. +# +_qt_dynamic_dependencies_dict = { + ## "lib_name": (.hiddenimports, translations_base, zero or more plugins...) + "qt5bluetooth": (".QtBluetooth", None, ), # noqa: E241,E202 + "qt5concurrent": (None, "qtbase", ), + "qt5core": (".QtCore", "qtbase", ), + # This entry generated by hand -- it's not present in the Windows deployment tool sources. + "qtdbus": (".QtDBus", None, ), + "qt5declarative": (None, "qtquick1", "qml1tooling"), + "qt5designer": (".QtDesigner", None, ), + "qt5designercomponents": (None, None, ), + "enginio": (None, None, ), + "qt5gamepad": (None, None, "gamepads"), + # Note: The ``platformthemes`` plugin is for Linux only, and comes from earlier PyInstaller code in ``hook-PyQt5.QtGui.py``. The ``styles`` plugin comes from the suggestion at https://github.com/pyinstaller/pyinstaller/issues/2156. + # ``xcbglintegrations`` and ``egldeviceintegrations`` were added manually + # for linux + "qt5gui": (".QtGui", "qtbase", "accessible", "iconengines", "imageformats", "platforms", "platforminputcontexts", "platformthemes", "styles", "xcbglintegrations", "egldeviceintegrations"), # noqa + "qt5help": (".QtHelp", "qt_help", ), + # This entry generated by hand -- it's not present in the Windows deployment tool sources. + "qt5macextras": (".QtMacExtras", None, ), + "qt5multimedia": (".QtMultimedia", "qtmultimedia", "audio", "mediaservice", "playlistformats"), + "qt5multimediawidgets": (".QtMultimediaWidgets", "qtmultimedia", ), + "qt5multimediaquick_p": (None, "qtmultimedia", ), + "qt5network": (".QtNetwork", "qtbase", "bearer"), + "qt5nfc": (".QtNfc", None, ), + "qt5opengl": (".QtOpenGL", None, ), # noqa + "qt5positioning": (".QtPositioning", None, "position"), + "qt5printsupport": (".QtPrintSupport", None, "printsupport"), + "qt5qml": (".QtQml", "qtdeclarative", ), + "qmltooling": (None, None, "qmltooling"), + "qt5quick": (".QtQuick", "qtdeclarative", "scenegraph", "qmltooling"), # noqa + "qt5quickparticles": (None, None, ), + "qt5quickwidgets": (".QtQuickWidgets", None, ), + "qt5script": (None, "qtscript", ), + "qt5scripttools": (None, "qtscript", ), + "qt5sensors": (".QtSensors", None, "sensors", "sensorgestures"), + "qt5serialport": (".QtSerialPort", "qtserialport", ), + "qt5sql": (".QtSql", "qtbase", "sqldrivers"), + "qt5svg": (".QtSvg", None, ), + "qt5test": (".QtTest", "qtbase", ), + "qt5webkit": (None, None, ), + "qt5webkitwidgets": (None, None, ), + "qt5websockets": (".QtWebSockets", None, ), + "qt5widgets": (".QtWidgets", "qtbase", ), + "qt5winextras": (".QtWinExtras", None, ), + "qt5xml": (".QtXml", "qtbase", ), + "qt5xmlpatterns": (".QXmlPatterns", "qtxmlpatterns", ), + "qt5webenginecore": (".QtWebEngineCore", None, "qtwebengine"), # noqa + "qt5webengine": (".QtWebEngine", "qtwebengine", "qtwebengine"), + "qt5webenginewidgets": (".QtWebEngineWidgets", None, "qtwebengine"), + "qt53dcore": (None, None, ), + "qt53drender": (None, None, "sceneparsers", "renderplugins", "geometryloaders"), + "qt53dquick": (None, None, ), + "qt53dquickRender": (None, None, ), + "qt53dinput": (None, None, ), + "qt5location": (".QtLocation", None, "geoservices"), + "qt5webchannel": (".QtWebChannel", None, ), + "qt5texttospeech": (None, None, "texttospeech"), + "qt5serialbus": (None, None, "canbus"), +} + + +# add_qt5_dependencies +# -------------------- +# Find the Qt dependencies based on the hook name of a PyQt5 hook. Returns +# (hiddenimports, binaries, datas). Typical usage: ``hiddenimports, binaries, +# datas = add_qt5_dependencies(__file__)``. +def add_qt5_dependencies(hook_file): + # Accumulate all dependencies in a set to avoid duplicates. + hiddenimports = set() + translations_base = set() + plugins = set() + + # Find the module underlying this Qt hook: change + # ``/path/to/hook-PyQt5.blah.py`` to ``PyQt5.blah``. + hook_name, hook_ext = os.path.splitext(os.path.basename(hook_file)) + assert hook_ext.startswith('.py') + assert hook_name.startswith('hook-') + module_name = hook_name[5:] + namespace = module_name.split('.')[0] + if namespace not in ('PyQt5', 'PySide2'): + raise Exception('Invalid namespace: {0}'.format(namespace)) + is_PyQt5 = namespace == 'PyQt5' + + # Exit if the requested library can't be imported. + if ((is_PyQt5 and not pyqt5_library_info.version) or + (not is_PyQt5 and not pyside2_library_info.version)): + return [], [], [] + + # Look up the module returned by this import. + module = get_module_file_attribute(module_name) + logger.debug('add_qt5_dependencies: Examining %s, based on hook of %s.', + module, hook_file) + + # Walk through all the static dependencies of a dynamically-linked library + # (``.so``/``.dll``/``.dylib``). + imports = set(getImports(module)) + while imports: + imp = imports.pop() + + # On Windows, find this library; other platforms already provide the + # full path. + if is_win: + imp = getfullnameof(imp, + # First, look for Qt binaries in the local Qt install. + pyqt5_library_info.location['BinariesPath'] if is_PyQt5 else + pyside2_library_info.location['BinariesPath'] + ) + + # Strip off the extension and ``lib`` prefix (Linux/Mac) to give the raw + # name. Lowercase (since Windows always normalized names to lowercase). + lib_name = os.path.splitext(os.path.basename(imp))[0].lower() + # Linux libraries sometimes have a dotted version number -- + # ``libfoo.so.3``. It's now ''libfoo.so``, but the ``.so`` must also be + # removed. + if is_linux and os.path.splitext(lib_name)[1] == '.so': + lib_name = os.path.splitext(lib_name)[0] + if lib_name.startswith('lib'): + lib_name = lib_name[3:] + # Mac: rename from ``qt`` to ``qt5`` to match names in Windows/Linux. + if is_darwin and lib_name.startswith('qt'): + lib_name = 'qt5' + lib_name[2:] + + # match libs with QT_LIBINFIX set to '_conda', i.e. conda-forge builds + if lib_name.endswith('_conda'): + lib_name = lib_name[:-6] + + logger.debug('add_qt5_dependencies: raw lib %s -> parsed lib %s', + imp, lib_name) + + # Follow only Qt dependencies. + if lib_name in _qt_dynamic_dependencies_dict: + # Follow these to find additional dependencies. + logger.debug('add_qt5_dependencies: Import of %s.', imp) + imports.update(getImports(imp)) + # Look up which plugins and translations are needed. + dd = _qt_dynamic_dependencies_dict[lib_name] + lib_name_hiddenimports, lib_name_translations_base = dd[:2] + lib_name_plugins = dd[2:] + # Add them in. + if lib_name_hiddenimports: + hiddenimports.update([namespace + lib_name_hiddenimports]) + plugins.update(lib_name_plugins) + if lib_name_translations_base: + translations_base.update([lib_name_translations_base]) + + # Change plugins into binaries. + binaries = [] + for plugin in plugins: + more_binaries = qt_plugins_binaries(plugin, namespace=namespace) + binaries.extend(more_binaries) + # Change translation_base to datas. + tp = ( + pyqt5_library_info.location['TranslationsPath'] if is_PyQt5 + else pyside2_library_info.location['TranslationsPath'] + ) + datas = [] + for tb in translations_base: + src = os.path.join(tp, tb + '_*.qm') + # Not all PyQt5 installations include translations. See + # https://github.com/pyinstaller/pyinstaller/pull/3229#issuecomment-359479893 + # and + # https://github.com/pyinstaller/pyinstaller/issues/2857#issuecomment-368744341. + if glob.glob(src): + datas.append(( + src, os.path.join( + # The PySide2 Windows wheels place translations in a + # different location. + namespace, '' if not is_PyQt5 and is_win else 'Qt', + 'translations' + ) + )) + else: + logger.warning('Unable to find Qt5 translations %s. These ' + 'translations were not packaged.', src) + # Change hiddenimports to a list. + hiddenimports = list(hiddenimports) + + logger.debug('add_qt5_dependencies: imports from %s:\n' + ' hiddenimports = %s\n' + ' binaries = %s\n' + ' datas = %s', + hook_name, hiddenimports, binaries, datas) + return hiddenimports, binaries, datas + + +def find_all_or_none(globs_to_include, num_files, qt_library_info): + """ + globs_to_include is a list of file name globs + If the number of found files does not match num_files + then no files will be included. + """ + # This function is required because CI is failing to include libEGL. The + # error in AppVeyor is:: + # + # [2312] LOADER: Running pyi_lib_PyQt5-uic.py + # Failed to load libEGL (Access is denied.) + # More info: https://github.com/pyinstaller/pyinstaller/pull/3568 + # + # Since old PyQt5 wheels do not include d3dcompiler_4?.dll, libEGL.dll and + # libGLESv2.dll will not be included for PyQt5 builds during CI. + to_include = [] + dst_dll_path = '.' + for dll in globs_to_include: + dll_path = os.path.join(qt_library_info.location[ + 'BinariesPath' if qt_library_info.is_PyQt5 else 'PrefixPath' + ], dll) + dll_file_paths = glob.glob(dll_path) + for dll_file_path in dll_file_paths: + to_include.append((dll_file_path, dst_dll_path)) + if len(to_include) == num_files: + return to_include + return [] + + +# Gather required Qt binaries, but only if all binaries in a group exist. +def get_qt_binaries(qt_library_info): + binaries = [] + angle_files = ['libEGL.dll', 'libGLESv2.dll', 'd3dcompiler_??.dll'] + binaries += find_all_or_none(angle_files, 3, qt_library_info) + + opengl_software_renderer = ['opengl32sw.dll'] + binaries += find_all_or_none(opengl_software_renderer, 1, qt_library_info) + + # Include ICU files, if they exist. + # See the "Deployment approach" section in ``PyInstaller/utils/hooks/qt.py``. + icu_files = ['icudt??.dll', 'icuin??.dll', 'icuuc??.dll'] + binaries += find_all_or_none(icu_files, 3, qt_library_info) + + return binaries + + +__all__ = ('qt_plugins_dir', 'qt_plugins_binaries', 'qt_menu_nib_dir', + 'get_qmake_path') diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/README b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/README new file mode 100644 index 0000000000000000000000000000000000000000..b126845363f8dc3436acaa1cf2b7e58e1df78e6c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/README @@ -0,0 +1,6 @@ +This directory contain scripts that are executed by PyInstaller.utils.hooks +module to do some detection for some import hooks. + +In PyInstaller we cannot directly import modules that the application depends on. +These scripts do some necessary detection in a subprocess. + diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e392dedb3fb954745f899ae6d74ff6efea644228 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/__init__.py @@ -0,0 +1 @@ +__author__ = 'martin' diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/django_import_finder.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/django_import_finder.py new file mode 100644 index 0000000000000000000000000000000000000000..6c00f26054ae92ee6ee1a8831a6034dfb1350d6e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/subproc/django_import_finder.py @@ -0,0 +1,99 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 parses all Django dependencies from the module mysite.settings.py. + +NOTE: With newer version of Django this is most likely the part of PyInstaller + that will be broken. + +Tested with Django 2.2 +""" + + +import os + +# Calling django.setup() avoids the exception AppRegistryNotReady() +# and also reads the user settings from DJANGO_SETTINGS_MODULE. +# https://stackoverflow.com/questions/24793351/django-appregistrynotready +import django +django.setup() + +# This allows to access all django settings even from the settings.py module. +from django.conf import settings + +from PyInstaller.utils.hooks import collect_submodules + + +hiddenimports = list(settings.INSTALLED_APPS) + +# do not fail script when settings does not have such attributes +if hasattr(settings, 'TEMPLATE_CONTEXT_PROCESSORS'): + hiddenimports += list(settings.TEMPLATE_CONTEXT_PROCESSORS) + +if hasattr(settings, 'TEMPLATE_LOADERS'): + hiddenimports += list(settings.TEMPLATE_LOADERS) + +hiddenimports += [settings.ROOT_URLCONF] + + +def _remove_class(class_name): + return '.'.join(class_name.split('.')[0:-1]) + + +### Changes in Django 1.7. + +# Remove class names and keep just modules. +if hasattr(settings, 'AUTHENTICATION_BACKENDS'): + for cl in settings.AUTHENTICATION_BACKENDS: + cl = _remove_class(cl) + hiddenimports.append(cl) +if hasattr(settings, 'DEFAULT_FILE_STORAGE'): + cl = _remove_class(settings.DEFAULT_FILE_STORAGE) + hiddenimports.append(cl) +if hasattr(settings, 'FILE_UPLOAD_HANDLERS'): + for cl in settings.FILE_UPLOAD_HANDLERS: + cl = _remove_class(cl) + hiddenimports.append(cl) +if hasattr(settings, 'MIDDLEWARE_CLASSES'): + for cl in settings.MIDDLEWARE_CLASSES: + cl = _remove_class(cl) + hiddenimports.append(cl) +# Templates is a dict: +if hasattr(settings, 'TEMPLATES'): + for templ in settings.TEMPLATES: + backend = _remove_class(templ['BACKEND']) + # Include context_processors. + if hasattr(templ, 'OPTIONS'): + if hasattr(templ['OPTIONS'], 'context_processors'): + # Context processors are functions - strip last word. + mods = templ['OPTIONS']['context_processors'] + mods = [_remove_class(x) for x in mods] + hiddenimports += mods +# Include database backends - it is a dict. +for v in settings.DATABASES.values(): + hiddenimports.append(v['ENGINE']) + + +# Add templatetags and context processors for each installed app. +for app in settings.INSTALLED_APPS: + app_templatetag_module = app + '.templatetags' + app_ctx_proc_module = app + '.context_processors' + hiddenimports.append(app_templatetag_module) + hiddenimports += collect_submodules(app_templatetag_module) + hiddenimports.append(app_ctx_proc_module) + +# Deduplicate imports. +hiddenimports = list(set(hiddenimports)) + +# This print statement is then parsed and evaluated as Python code. +print(hiddenimports) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/win32.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/win32.py new file mode 100644 index 0000000000000000000000000000000000000000..6c7b1adb5f157f5579e36a1486862a210d4a36f1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/hooks/win32.py @@ -0,0 +1,56 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 ..hooks import exec_statement + +# NOTE: This function requires PyInstaller to be on the default "sys.path" for +# the called Python process. Running py.test changes the working dir to a temp +# dir, so PyInstaller should be installed via either "setup.py install" or +# "setup.py develop" before running py.test. + + +def get_pywin32_module_file_attribute(module_name): + """ + Get the absolute path of the PyWin32 DLL specific to the PyWin32 module + with the passed name. + + On import, each PyWin32 module: + + * Imports a DLL specific to that module. + * Overwrites the values of all module attributes with values specific to + that DLL. This includes that module's `__file__` attribute, which then + provides the absolute path of that DLL. + + This function safely imports that module in a PyWin32-aware subprocess and + returns the value of that module's `__file__` attribute. + + Parameters + ---------- + module_name : str + Fully-qualified name of that module. + + Returns + ---------- + str + Absolute path of that DLL. + + See Also + ---------- + `PyInstaller.utils.win32.winutils.import_pywin32_module()` + For further details. + """ + statement = """ + from PyInstaller.utils.win32 import winutils + module = winutils.import_pywin32_module('%s') + print(module.__file__) + """ + return exec_statement(statement % module_name) + +__all__ = ('get_pywin32_module_file_attribute', ) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/misc.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/misc.py new file mode 100644 index 0000000000000000000000000000000000000000..7ece2bbc5ee4e1e11d6b4fe29511686a47617dc9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/misc.py @@ -0,0 +1,253 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 is for the miscellaneous routines which do not fit somewhere else. +""" + +import glob +import os +import pprint +import py_compile +import sys + +from PyInstaller import log as logging +from PyInstaller.compat import BYTECODE_MAGIC, text_read_mode, is_win + +logger = logging.getLogger(__name__) + + +def dlls_in_subdirs(directory): + """Returns a list *.dll, *.so, *.dylib in given directories and subdirectories.""" + filelist = [] + for root, dirs, files in os.walk(directory): + filelist.extend(dlls_in_dir(root)) + return filelist + + +def dlls_in_dir(directory): + """Returns a list of *.dll, *.so, *.dylib in given directory.""" + return files_in_dir(directory, ["*.so", "*.dll", "*.dylib"]) + + +def files_in_dir(directory, file_patterns=[]): + """Returns a list of files which match a pattern in given directory.""" + files = [] + for file_pattern in file_patterns: + files.extend(glob.glob(os.path.join(directory, file_pattern))) + return files + + +def get_unicode_modules(): + """ + Try importing codecs and encodings to include unicode support + in created binary. + """ + modules = [] + try: + # `codecs` depends on `encodings` and this is then included. + import codecs + modules.append('codecs') + except ImportError: + logger.error("Cannot detect modules 'codecs'.") + + return modules + + +def get_path_to_toplevel_modules(filename): + """ + Return the path to top-level directory that contains Python modules. + + It will look in parent directories for __init__.py files. The first parent + directory without __init__.py is the top-level directory. + + Returned directory might be used to extend the PYTHONPATH. + """ + curr_dir = os.path.dirname(os.path.abspath(filename)) + pattern = '__init__.py' + + # Try max. 10 levels up. + try: + for i in range(10): + files = set(os.listdir(curr_dir)) + # 'curr_dir' is still not top-leve go to parent dir. + if pattern in files: + curr_dir = os.path.dirname(curr_dir) + # Top-level dir found - return it. + else: + return curr_dir + except IOError: + pass + # No top-level directory found or any error. + return None + + +def mtime(fnm): + try: + # TODO: explain why this doesn't use os.path.getmtime() ? + # - It is probably not used because it returns float and not int. + return os.stat(fnm)[8] + except: + return 0 + + +def compile_py_files(toc, workpath): + """ + Given a TOC or equivalent list of tuples, generates all the required + pyc/pyo files, writing in a local directory if required, and returns the + list of tuples with the updated pathnames. + + In the old system using ImpTracker, the generated TOC of "pure" modules + already contains paths to nm.pyc or nm.pyo and it is only necessary + to check that these files are not older than the source. + In the new system using ModuleGraph, the path given is to nm.py + and we do not know if nm.pyc/.pyo exists. The following logic works + with both (so if at some time modulegraph starts returning filenames + of .pyc, it will cope). + """ + + # For those modules that need to be rebuilt, use the build directory + # PyInstaller creates during the build process. + basepath = os.path.join(workpath, "localpycos") + + # Copy everything from toc to this new TOC, possibly unchanged. + new_toc = [] + for (nm, fnm, typ) in toc: + # Keep unrelevant items unchanged. + if typ != 'PYMODULE': + new_toc.append((nm, fnm, typ)) + continue + + if fnm in ('-', None): + # If fmn represents a namespace then skip + continue + + if fnm.endswith('.py') : + # we are given a source path, determine the object path if any + src_fnm = fnm + # assume we want pyo only when now running -O or -OO + obj_fnm = src_fnm + ('o' if sys.flags.optimize else 'c') + if not os.path.exists(obj_fnm) : + # alas that one is not there so assume the other choice + obj_fnm = src_fnm + ('c' if sys.flags.optimize else 'o') + else: + # fnm is not "name.py" so assume we are given name.pyc/.pyo + obj_fnm = fnm # take that namae to be the desired object + src_fnm = fnm[:-1] # drop the 'c' or 'o' to make a source name + + # We need to perform a build ourselves if obj_fnm doesn't exist, + # or if src_fnm is newer than obj_fnm, or if obj_fnm was created + # by a different Python version. + # TODO: explain why this does read()[:4] (reading all the file) + # instead of just read(4)? Yes for many a .pyc file, it is all + # in one sector so there's no difference in I/O but still it + # seems inelegant to copy it all then subscript 4 bytes. + needs_compile = mtime(src_fnm) > mtime(obj_fnm) + if not needs_compile: + with open(obj_fnm, 'rb') as fh: + needs_compile = fh.read()[:4] != BYTECODE_MAGIC + if needs_compile: + try: + # TODO: there should be no need to repeat the compile, + # because ModuleGraph does a compile and stores the result + # in the .code member of the graph node. Should be possible + # to get the node and write the code to obj_fnm + py_compile.compile(src_fnm, obj_fnm) + logger.debug("compiled %s", src_fnm) + except IOError: + # If we're compiling on a system directory, probably we don't + # have write permissions; thus we compile to a local directory + # and change the TOC entry accordingly. + ext = os.path.splitext(obj_fnm)[1] + + if "__init__" not in obj_fnm: + # If it's a normal module, use last part of the qualified + # name as module name and the first as leading path + leading, mod_name = nm.split(".")[:-1], nm.split(".")[-1] + else: + # In case of a __init__ module, use all the qualified name + # as leading path and use "__init__" as the module name + leading, mod_name = nm.split("."), "__init__" + + leading = os.path.join(basepath, *leading) + + if not os.path.exists(leading): + os.makedirs(leading) + + obj_fnm = os.path.join(leading, mod_name + ext) + # TODO see above regarding read()[:4] versus read(4) + needs_compile = mtime(src_fnm) > mtime(obj_fnm) + if not needs_compile: + with open(obj_fnm, 'rb') as fh: + needs_compile = fh.read()[:4] != BYTECODE_MAGIC + if needs_compile: + # TODO see above todo regarding using node.code + py_compile.compile(src_fnm, obj_fnm) + logger.debug("compiled %s", src_fnm) + # if we get to here, obj_fnm is the path to the compiled module nm.py + new_toc.append((nm, obj_fnm, typ)) + + return new_toc + + +def save_py_data_struct(filename, data): + """ + Save data into text file as Python data structure. + :param filename: + :param data: + :return: + """ + dirname = os.path.dirname(filename) + if not os.path.exists(dirname): + os.makedirs(dirname) + with open(filename, 'w', encoding='utf-8') as f: + pprint.pprint(data, f) + + +def load_py_data_struct(filename): + """ + Load data saved as python code and interpret that code. + :param filename: + :return: + """ + with open(filename, text_read_mode, encoding='utf-8') as f: + # Binding redirects are stored as a named tuple, so bring the namedtuple + # class into scope for parsing the TOC. + from ..depend.bindepend import BindingRedirect + + if is_win: + # import versioninfo so that VSVersionInfo can parse correctly + from .win32 import versioninfo # noqa: F401 + + return eval(f.read()) + + +def absnormpath(apath): + return os.path.abspath(os.path.normpath(apath)) + + +def module_parent_packages(full_modname): + """ + Return list of parent package names. + 'aaa.bb.c.dddd' -> ['aaa', 'aaa.bb', 'aaa.bb.c'] + :param full_modname: Full name of a module. + :return: List of parent module names. + """ + prefix = '' + parents = [] + # Ignore the last component in module name and get really just + # parent, grand parent, grandgrand parent, etc. + for pkg in full_modname.split('.')[0:-1]: + # Ensure first item does not start with dot '.' + prefix += '.' + pkg if prefix else pkg + parents.append(prefix) + return parents diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/osx.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/osx.py new file mode 100644 index 0000000000000000000000000000000000000000..147e3a012904438abcff36236da3a4387d3be2d0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/osx.py @@ -0,0 +1,108 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Utils for Mac OS X platform. +""" + +import os +import shutil + +from ..compat import base_prefix +from macholib.MachO import MachO + + +def is_homebrew_env(): + """ + Check if Python interpreter was installed via Homebrew command 'brew'. + + :return: True if Homebrew else otherwise. + """ + # Python path prefix should start with Homebrew prefix. + env_prefix = get_homebrew_prefix() + if env_prefix and base_prefix.startswith(env_prefix): + return True + return False + + +def is_macports_env(): + """ + Check if Python interpreter was installed via Macports command 'port'. + + :return: True if Macports else otherwise. + """ + # Python path prefix should start with Macports prefix. + env_prefix = get_macports_prefix() + if env_prefix and base_prefix.startswith(env_prefix): + return True + return False + + +def get_homebrew_prefix(): + """ + :return: Root path of the Homebrew environment. + """ + prefix = shutil.which('brew') + # Conversion: /usr/local/bin/brew -> /usr/local + prefix = os.path.dirname(os.path.dirname(prefix)) + return prefix + + +def get_macports_prefix(): + """ + :return: Root path of the Macports environment. + """ + prefix = shutil.which('port') + # Conversion: /usr/local/bin/port -> /usr/local + prefix = os.path.dirname(os.path.dirname(prefix)) + return prefix + + +def fix_exe_for_code_signing(filename): + """ + Fixes the Mach-O headers to make code signing possible. + + Code signing on OS X does not work out of the box with embedding + .pkg archive into the executable. + + The fix is done this way: + - Make the embedded .pkg archive part of the Mach-O 'String Table'. + 'String Table' is at end of the OS X exe file so just change the size + of the table to cover the end of the file. + - Fix the size of the __LINKEDIT segment. + + Mach-O format specification: + + http://developer.apple.com/documentation/Darwin/Reference/ManPages/man5/Mach-O.5.html + """ + exe_data = MachO(filename) + # Every load command is a tupple: (cmd_metadata, segment, [section1, section2]) + cmds = exe_data.headers[0].commands # '0' - Exe contains only one architecture. + file_size = exe_data.headers[0].size + + ## Make the embedded .pkg archive part of the Mach-O 'String Table'. + # Data about 'String Table' is in LC_SYMTAB load command. + for c in cmds: + if c[0].get_cmd_name() == 'LC_SYMTAB': + data = c[1] + # Increase the size of 'String Table' to cover the embedded .pkg file. + new_strsize = file_size - data.stroff + data.strsize = new_strsize + ## Fix the size of the __LINKEDIT segment. + # __LINKEDIT segment data is the 4th item in the executable. + linkedit = cmds[3][1] + new_segsize = file_size - linkedit.fileoff + linkedit.filesize = new_segsize + linkedit.vmsize = new_segsize + ## Write changes back. + with open(exe_data.filename, 'rb+') as fp: + exe_data.write(fp) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/pytest.ini b/3rdparty/pyinstaller-4.3/PyInstaller/utils/pytest.ini new file mode 100644 index 0000000000000000000000000000000000000000..fba2681660c998ea5abe49fcb5d1a06f38ef71f8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/pytest.ini @@ -0,0 +1,2 @@ +# Needed for tests discovered by entry points; see +# ``PyInstaller/utils/run_tests.py``. diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/release.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/release.py new file mode 100644 index 0000000000000000000000000000000000000000..81ae9702065c59a93012df8bc3c64d5941e12945 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/release.py @@ -0,0 +1,46 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 contains code useful for doing releases of PyInstaller. + +PyInstaller uses package 'zest.releaser' to automate releases. This module +contains mostly customization for the release process. + +zest.releaser allows customization by exposing some entry points. For details: + +https://zestreleaser.readthedocs.org/en/latest/entrypoints.html +""" + +import os +from ..compat import exec_command, getenv + + +def sign_source_distribution(data): + """ + Sign the tgz or zip archive that will be uploaded to PYPI. + :param data: + """ + print() + # zest.releaser does a clean checkout where it generates tgz/zip in 'dist' + # directory and those files will be then uploaded to pypi. + dist_dir = os.path.join(data['tagdir'], 'dist') + cmd = ['gpg', '--detach-sign', '--armor'] + if getenv("PYINSTALLER_CODESIGNING_ID"): + print("Using gpg identity", getenv("PYINSTALLER_CODESIGNING_ID"), + "for signing.") + cmd.extend(['--local-user', getenv("PYINSTALLER_CODESIGNING_ID")]) + # Sign all files in 'dist' directory. + for f in os.listdir(dist_dir): + f = os.path.join(dist_dir, f) + print('Signing file %s' % f) + exec_command(*cmd + [f]) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/run_tests.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/run_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..a89a3288df5f07474d19abe4765f95145aa36b31 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/run_tests.py @@ -0,0 +1,71 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import argparse + +import pytest +import pkg_resources + +from .. import compat + + +def paths_to_test(include_only=None): + """If ``include_only`` is falsey, this functions returns paths from all entry + points. Otherwise, this parameter must be a string or sequence of strings. + In this case, this function will return *only* paths from entry points + whose ``module_name`` begins with the provided string(s). + """ + # Convert a string to a list. + if isinstance(include_only, compat.string_types): + include_only = [include_only] + + # Walk through all entry points. + test_path_list = [] + for entry_point in pkg_resources.iter_entry_points("pyinstaller40", + "tests"): + # Implement ``include_only``. + if (not include_only # If falsey, include everything, + # Otherwise, include only the specified modules. + or any(entry_point.module_name.startswith(name) + for name in include_only)): + test_path_list += list(entry_point.load()()) + return test_path_list + + +# Run pytest on all tests registered by the PyInstaller setuptools testing +# entry point. If provided, the ``include_only`` argument is passed to +# ``path_to_test``. +def run_pytest(*args, **kwargs): + paths = paths_to_test(include_only=kwargs.pop("include_only", None)) + # Return an error code if no tests were discovered. + if not paths: + print("Error: no tests discovered.", file=sys.stderr) + # This indicates no tests were discovered; see + # https://docs.pytest.org/en/latest/usage.html#possible-exit-codes. + return 5 + else: + # See + # https://docs.pytest.org/en/latest/usage.html#calling-pytest-from-python-code. + # Omit ``args[0]``, which is the name of this script. + print("pytest " + " ".join([*paths, *args[1:]])) + return pytest.main([*paths, *args[1:]], **kwargs) + + +if __name__ == "__main__": + # Look only for the ``--include_only`` argument. + parser = argparse.ArgumentParser( + description='Run PyInstaller packaging tests.') + parser.add_argument("--include_only", action="append", + help="Only run tests from the specified package.") + args, unknown = parser.parse_known_args(sys.argv) + # Convert the parsed args into a dict using ``vars(args)``. + sys.exit(run_pytest(*unknown, **vars(args))) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/tests.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/tests.py new file mode 100644 index 0000000000000000000000000000000000000000..e51ff4fea954e99943757d10c46e119d0fbd8493 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/tests.py @@ -0,0 +1,173 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Decorators for skipping PyInstaller tests when specific requirements are not met. +""" + +import os +import sys +import traceback +import distutils.ccompiler +import inspect +import textwrap +import shutil + +import pytest +from _pytest.runner import Skipped + +from PyInstaller.compat import is_win + +# Wrap some pytest decorators to be consistent in tests. +parametrize = pytest.mark.parametrize +skipif = pytest.mark.skipif +xfail = pytest.mark.xfail + +def _check_for_compiler(): + import tempfile, sys + # change to some tempdir since cc.has_function() would compile into the + # current directory, leaving garbage + old_wd = os.getcwd() + tmp = tempfile.mkdtemp() + os.chdir(tmp) + cc = distutils.ccompiler.new_compiler() + if is_win: + try: + cc.initialize() + has_compiler = True + # This error is raised on Windows if a compiler can't be found. + except distutils.errors.DistutilsPlatformError: + has_compiler = False + else: + # The C standard library contains the ``clock`` function. Use that to + # determine if a compiler is installed. This doesn't work on Windows:: + # + # Users\bjones\AppData\Local\Temp\a.out.exe.manifest : general error + # c1010070: Failed to load and parse the manifest. The system cannot + # find the file specified. + has_compiler = cc.has_function('clock', includes=['time.h']) + os.chdir(old_wd) + # TODO: Find a way to remove the generated clockXXXX.c file, too + shutil.rmtree(tmp) + return has_compiler + + +# A decorator to skip tests if a C compiler isn't detected. +has_compiler = _check_for_compiler() +skipif_no_compiler = skipif(not has_compiler, reason="Requires a C compiler") + + +def skip(reason): + """ + Unconditionally skip the currently decorated test with the passed reason. + + This decorator is intended to be called either directly as a function _or_ + indirectly as a decorator. This differs from both: + + * `pytest.skip()`, intended to be called only directly as a function. + Attempting to call this function indirectly as a decorator produces + extraneous ignorable messages on standard output resembling + `SKIP [1] PyInstaller/utils/tests.py:65: could not import 'win32com'`. + * `pytest.mark.skip()`, intended to be called only indirectly as a + decorator. Attempting to call this decorator directly as a function + reduces to a noop. + + Parameters + ---------- + reason : str + Human-readable message justifying the skipping of this test. + """ + + return skipif(True, reason=reason) + + +def importorskip(modname, minversion=None): + """ + This decorator skips the currently decorated test if the module with the + passed name is unimportable _or_ importable but of a version less than the + passed minimum version if any. + + This decorator's name is intentionally mispelled as `importerskip` rather + than `importerskip` to coincide with the `pytest.importorskip()` function + internally called by this decorator. + + Parameters + ---------- + modname : str + Fully-qualified name of the module required by this test. + minversion : str + Optional minimum version of this module as a string (e.g., `3.14.15`) + required by this test _or_ `None` if any module version is acceptable. + Defaults to `None`. + + Returns + ---------- + pytest.skipif + Decorator describing these requirements if unmet _or_ the identity + decorator otherwise (i.e., if these requirements are met). + """ + + # Defer to the eponymous function of the same name. + try: + pytest.importorskip(modname, minversion) + # Silently convert expected import and syntax errors into @skip decoration. + except Skipped as exc: + return skip(str(exc)) + # Convert all other unexpected errors into the same decoration. + except Exception as exc: + # For debuggability, print a verbose stacktrace. + print('importorskip: Exception in module "{}":'.format(modname)) + print('-' * 60) + traceback.print_exc(file=sys.stdout) + print('-' * 60) + + return skip(str(exc)) + # Else, this module is importable and optionally satisfies this minimum + # version. Reduce this decoration to a noop. + else: + return pytest.mark.skipif(False, reason='') + + +def gen_sourcefile(tmpdir, source, test_id=None): + """ + Generate a source file for testing. + + The source will be written into a file named like the + test-function. This file will then be passed to `test_script`. + If you need other related file, e.g. as `.toc`-file for + testing the content, put it at at the normal place. Just mind + to take the basnename from the test-function's name. + + :param script: Source code to create executable from. This + will be saved into a temporary file which is + then passed on to `test_script`. + + :param test_id: Test-id for parametrized tests. If given, it + will be appended to the script filename, + separated by two underscores. + + Ensure that the caller of `test_source` is in a UTF-8 + encoded file with the correct '# -*- coding: utf-8 -*-' marker. + """ + testname = inspect.stack()[1][3] + if test_id: + # For parametrized test append the test-id. + testname = testname + '__' + test_id + + # Periods are not allowed in Python module names. + testname = testname.replace('.', '_') + scriptfile = tmpdir / (testname + '.py') + source = textwrap.dedent(source) + with scriptfile.open('w', encoding='utf-8') as ofh: + print(u'# -*- coding: utf-8 -*-', file=ofh) + print(source, file=ofh) + return scriptfile diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/__init__.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e392dedb3fb954745f899ae6d74ff6efea644228 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/__init__.py @@ -0,0 +1 @@ +__author__ = 'martin' diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/icon.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/icon.py new file mode 100644 index 0000000000000000000000000000000000000000..ffb0847336ad5c2c964e45160a6f1d310813d9ba --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/icon.py @@ -0,0 +1,261 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +''' +The code in this module supports the --icon parameter in Windows. +(For --icon support under OSX see building/osx.py.) + +The only entry point, called from api.py, is CopyIcons(), below. +All the elaborate structure of classes that follows is used to +support the operation of CopyIcons_FromIco(). None of these classes +and globals are referenced outside this module. +''' + +RT_ICON = 3 +RT_GROUP_ICON = 14 +LOAD_LIBRARY_AS_DATAFILE = 2 + +import os.path +import struct +import types +try: + StringTypes = types.StringTypes +except AttributeError: + StringTypes = [ type("") ] + +from ...compat import win32api, pywintypes +from ... import config + +import PyInstaller.log as logging +logger = logging.getLogger(__name__) + +class Structure: + def __init__(self): + size = self._sizeInBytes = struct.calcsize(self._format_) + self._fields_ = list(struct.unpack(self._format_, b'\000' * size)) + indexes = self._indexes_ = {} + for i, nm in enumerate(self._names_): + indexes[nm] = i + + def dump(self): + logger.info("DUMP of %s", self) + for name in self._names_: + if not name.startswith('_'): + logger.info("%20s = %s", name, getattr(self, name)) + logger.info("") + + def __getattr__(self, name): + if name in self._names_: + index = self._indexes_[name] + return self._fields_[index] + try: + return self.__dict__[name] + except KeyError as e: + raise AttributeError(name) from e + + def __setattr__(self, name, value): + if name in self._names_: + index = self._indexes_[name] + self._fields_[index] = value + else: + self.__dict__[name] = value + + def tostring(self): + return struct.pack(self._format_, *self._fields_) + + def fromfile(self, file): + data = file.read(self._sizeInBytes) + self._fields_ = list(struct.unpack(self._format_, data)) + +class ICONDIRHEADER(Structure): + _names_ = "idReserved", "idType", "idCount" + _format_ = "hhh" + +class ICONDIRENTRY(Structure): + _names_ = ("bWidth", "bHeight", "bColorCount", "bReserved", "wPlanes", + "wBitCount", "dwBytesInRes", "dwImageOffset") + _format_ = "bbbbhhii" + +class GRPICONDIR(Structure): + _names_ = "idReserved", "idType", "idCount" + _format_ = "hhh" + +class GRPICONDIRENTRY(Structure): + _names_ = ("bWidth", "bHeight", "bColorCount", "bReserved", "wPlanes", + "wBitCount", "dwBytesInRes", "nID") + _format_ = "bbbbhhih" + +# An IconFile instance is created for each .ico file given. +class IconFile: + def __init__(self, path): + self.path = path + if not os.path.isabs(path): + self.path = os.path.join(config.CONF['specpath'], path) + try: + # The path is from the user parameter, don't trust it. + file = open(self.path, "rb") + except OSError: + # The icon file can't be opened for some reason. Stop the + # program with an informative message. + raise SystemExit( + 'Unable to open icon file {}'.format(path) + ) + self.entries = [] + self.images = [] + header = self.header = ICONDIRHEADER() + header.fromfile(file) + for i in range(header.idCount): + entry = ICONDIRENTRY() + entry.fromfile(file) + self.entries.append(entry) + for e in self.entries: + file.seek(e.dwImageOffset, 0) + self.images.append(file.read(e.dwBytesInRes)) + + def grp_icon_dir(self): + return self.header.tostring() + + def grp_icondir_entries(self, id=1): + data = b'' + for entry in self.entries: + e = GRPICONDIRENTRY() + for n in e._names_[:-1]: + setattr(e, n, getattr(entry, n)) + e.nID = id + id = id + 1 + data = data + e.tostring() + return data + +def CopyIcons_FromIco(dstpath, srcpath, id=1): + ''' + Use the Win API UpdateResource facility to apply the icon + resource(s) to the .exe file. + + :param str dstpath: absolute path of the .exe file being built. + :param str srcpath: list of 1 or more .ico file paths + ''' + icons = map(IconFile, srcpath) + logger.info("Copying icons from %s", srcpath) + + hdst = win32api.BeginUpdateResource(dstpath, 0) + + iconid = 1 + # Each step in the following enumerate() will instantiate an IconFile + # object, as a result of deferred execution of the map() above. + for i, f in enumerate(icons): + data = f.grp_icon_dir() + data = data + f.grp_icondir_entries(iconid) + win32api.UpdateResource(hdst, RT_GROUP_ICON, i, data) + logger.info("Writing RT_GROUP_ICON %d resource with %d bytes", i, len(data)) + for data in f.images: + win32api.UpdateResource(hdst, RT_ICON, iconid, data) + logger.info("Writing RT_ICON %d resource with %d bytes", iconid, len(data)) + iconid = iconid + 1 + + win32api.EndUpdateResource(hdst, 0) + +def CopyIcons(dstpath, srcpath): + ''' + Called from building/api.py to handle icons. If the input was by + --icon on the command line, srcpath is a single string. However it + is possible to modify the spec file adding icon=['foo.ico','bar.ico'] + to the EXE() statement. In that case, srcpath is a list of strings. + + The string format is either path-to-.ico or path-to-.exe,n for n an + integer resource index in the .exe. In either case the path can be + relative or absolute. + ''' + + if type(srcpath) in StringTypes: + # just a single string, make it a one-element list + srcpath = [ srcpath ] + + def splitter(s): + ''' + Convert "pathname" to tuple ("pathname", None) + Convert "pathname,n" to tuple ("pathname", n) + ''' + try: + srcpath, index = s.split(',') + return srcpath.strip(), int(index) + except ValueError: + return s, None + + # split all the items in the list into tuples as above. + srcpath = list(map(splitter, srcpath)) + + if len(srcpath) > 1: + # More than one icon source given. We currently handle multiple + # icons by calling CopyIcons_FromIco(), which only allows .ico. + # In principle we could accept a mix of .ico and .exe, but it + # would complicate things. If you need it submit a pull request. + # + # Note that a ",index" on a .ico is just ignored in the single + # or multiple case. + srcs = [] + for s in srcpath: + e = os.path.splitext(s[0])[1] + if e.lower() != '.ico': + raise ValueError('Multiple icons supported only from .ico files') + srcs.append(s[0]) + return CopyIcons_FromIco(dstpath, srcs) + + # Just one source given. + srcpath,index = srcpath[0] + srcext = os.path.splitext(srcpath)[1] + # Handle the simple case of foo.ico, ignoring any ,index. + if srcext.lower() == '.ico': + return CopyIcons_FromIco(dstpath, [srcpath]) + + # Single source is not .ico, presumably it is .exe (and if not, some + # error will occur). If relative, make it relative to the .spec file. + if not os.path.isabs(srcpath): + srcpath = os.path.join(config.CONF['specpath'], srcpath) + if index is not None: + logger.info("Copying icon from %s, %d", srcpath, index) + else: + logger.info("Copying icons from %s", srcpath) + + try: + # Attempt to load the .ico or .exe containing the icon into memory + # using the same mechanism as if it were a DLL. If this fails for + # any reason (for example if the file does not exist or is not a + # .ico/.exe) then LoadLibraryEx returns a null handle and win32api + # raises a unique exception with a win error code and a string. + hsrc = win32api.LoadLibraryEx(srcpath, 0, LOAD_LIBRARY_AS_DATAFILE) + except pywintypes.error as W32E: + # We could continue with no icon (i.e. just return) however it seems + # best to terminate the build with a message. + raise SystemExit( + "Unable to load icon file {}\n {} (Error code {})".format( + srcpath, W32E.strerror, W32E.winerror) + ) + hdst = win32api.BeginUpdateResource(dstpath, 0) + if index is None: + grpname = win32api.EnumResourceNames(hsrc, RT_GROUP_ICON)[0] + elif index >= 0: + grpname = win32api.EnumResourceNames(hsrc, RT_GROUP_ICON)[index] + else: + grpname = -index + data = win32api.LoadResource(hsrc, RT_GROUP_ICON, grpname) + win32api.UpdateResource(hdst, RT_GROUP_ICON, grpname, data) + for iconname in win32api.EnumResourceNames(hsrc, RT_ICON): + data = win32api.LoadResource(hsrc, RT_ICON, iconname) + win32api.UpdateResource(hdst, RT_ICON, iconname, data) + win32api.FreeLibrary(hsrc) + win32api.EndUpdateResource(hdst, 0) + +if __name__ == "__main__": + import sys + + dstpath = sys.argv[1] + srcpath = sys.argv[2:] + CopyIcons(dstpath, srcpath) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/versioninfo.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/versioninfo.py new file mode 100644 index 0000000000000000000000000000000000000000..d17f43cccde38974f680b99472a310a844954728 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/versioninfo.py @@ -0,0 +1,652 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 codecs +import struct + +from ...compat import text_read_mode, win32api + +# ::TODO:: #1920 revert to using pypi version +import pefile + + +def pefile_check_control_flow_guard(filename): + """ + Checks if the specified PE file has CFG (Control Flow Guard) enabled. + + Parameters + ---------- + filename : str + Path to the PE file to inspect. + + Returns + ---------- + bool + True if file is a PE file with CFG enabled. False if CFG is not + enabled or if file could not be processed using pefile library. + """ + try: + pe = pefile.PE(filename) + # https://docs.microsoft.com/en-us/windows/win32/debug/pe-format + # IMAGE_DLLCHARACTERISTICS_GUARD_CF = 0x4000 + return bool(pe.OPTIONAL_HEADER.DllCharacteristics & 0x4000) + except Exception: + return False + + +# TODO implement read/write version information with pefile library. +# PE version info doc: http://msdn.microsoft.com/en-us/library/ms646981.aspx +def pefile_read_version(filename): + """ + Return structure like: + + { + # Translation independent information. + # VS_FIXEDFILEINFO - Contains version information about a file. This information is language and code page independent. + u'FileVersion': (1, 2, 3, 4), + u'ProductVersion': (9, 10, 11, 12), + + # PE files might contain several translations of version information. + # VS_VERSIONINFO - Depicts the organization of data in a file-version resource. It is the root structure that contains all other file-version information structures. + u'translations': { + 'lang_id1' : { + u'Comments': u'日本語, Unicode 対応.', + u'CompanyName': u'your company.', + u'FileDescription': u'your file desc.', + u'FileVersion': u'1, 2, 3, 4', + u'InternalName': u'your internal name.', + u'LegalCopyright': u'your legal copyright.', + u'LegalTrademarks': u'your legal trademarks.', + u'OriginalFilename': u'your original filename.', + u'PrivateBuild': u'5, 6, 7, 8', + u'ProductName': u'your product name', + u'ProductVersion': u'9, 10, 11, 12', + u'SpecialBuild': u'13, 14, 15, 16', + }, + + 'lang_id2' : { + ... + } + } + } + + Version info can contain multiple languages. + """ + # TODO + vers = { + 'FileVersion': (0, 0, 0, 0), + 'ProductVersion': (0, 0, 0, 0), + 'translations': { + 'lang_id1': { + 'Comments': '', + 'CompanyName': '', + 'FileDescription': '', + 'FileVersion': '', + 'InternalName': '', + 'LegalCopyright': '', + 'LegalTrademarks': '', + 'OriginalFilename': '', + 'PrivateBuild': '', + 'ProductName': '', + 'ProductVersion': '', + 'SpecialBuild': '', + } + } + } + pe = pefile.PE(filename) + #ffi = pe.VS_FIXEDFILEINFO + #vers['FileVersion'] = (ffi.FileVersionMS >> 16, ffi.FileVersionMS & 0xFFFF, ffi.FileVersionLS >> 16, ffi.FileVersionLS & 0xFFFF) + #vers['ProductVersion'] = (ffi.ProductVersionMS >> 16, ffi.ProductVersionMS & 0xFFFF, ffi.ProductVersionLS >> 16, ffi.ProductVersionLS & 0xFFFF) + #print(pe.VS_FIXEDFILEINFO.FileVersionMS) + # TODO Only first available language is used for now. + #vers = pe.FileInfo[0].StringTable[0].entries + from pprint import pprint + pprint(pe.VS_FIXEDFILEINFO) + print(dir(pe.VS_FIXEDFILEINFO)) + print(repr(pe.VS_FIXEDFILEINFO)) + print(pe.dump_info()) + pe.close() + return vers + + + +# Ensures no code from the executable is executed. +LOAD_LIBRARY_AS_DATAFILE = 2 + + +def getRaw(text): + """ + Encodes text as UTF-16LE (Microsoft 'Unicode') for use in structs. + """ + return text.encode('UTF-16LE') + + +def decode(pathnm): + h = win32api.LoadLibraryEx(pathnm, 0, LOAD_LIBRARY_AS_DATAFILE) + res = win32api.EnumResourceNames(h, pefile.RESOURCE_TYPE['RT_VERSION']) + if not len(res): + return None + data = win32api.LoadResource(h, pefile.RESOURCE_TYPE['RT_VERSION'], + res[0]) + vs = VSVersionInfo() + j = vs.fromRaw(data) + win32api.FreeLibrary(h) + return vs + + +def nextDWord(offset): + """ Align `offset` to the next 4-byte boundary """ + return ((offset + 3) >> 2) << 2 + + +class VSVersionInfo: + """ + WORD wLength; // length of the VS_VERSION_INFO structure + WORD wValueLength; // length of the Value member + WORD wType; // 1 means text, 0 means binary + WCHAR szKey[]; // Contains the Unicode string "VS_VERSION_INFO". + WORD Padding1[]; + VS_FIXEDFILEINFO Value; + WORD Padding2[]; + WORD Children[]; // zero or more StringFileInfo or VarFileInfo + // structures (or both) that are children of the + // current version structure. + """ + + def __init__(self, ffi=None, kids=None): + self.ffi = ffi + self.kids = kids or [] + + def fromRaw(self, data): + i, (sublen, vallen, wType, nm) = parseCommon(data) + #vallen is length of the ffi, typ is 0, nm is 'VS_VERSION_INFO'. + i = nextDWord(i) + # Now a VS_FIXEDFILEINFO + self.ffi = FixedFileInfo() + j = self.ffi.fromRaw(data, i) + i = j + while i < sublen: + j = i + i, (csublen, cvallen, ctyp, nm) = parseCommon(data, i) + if nm.strip() == u'StringFileInfo': + sfi = StringFileInfo() + k = sfi.fromRaw(csublen, cvallen, nm, data, i, j+csublen) + self.kids.append(sfi) + i = k + else: + vfi = VarFileInfo() + k = vfi.fromRaw(csublen, cvallen, nm, data, i, j+csublen) + self.kids.append(vfi) + i = k + i = j + csublen + i = nextDWord(i) + return i + + def toRaw(self): + raw_name = getRaw(u'VS_VERSION_INFO') + rawffi = self.ffi.toRaw() + vallen = len(rawffi) + typ = 0 + sublen = 6 + len(raw_name) + 2 + pad = b'' + if sublen % 4: + pad = b'\000\000' + sublen = sublen + len(pad) + vallen + pad2 = b'' + if sublen % 4: + pad2 = b'\000\000' + tmp = b''.join([kid.toRaw() for kid in self.kids ]) + sublen = sublen + len(pad2) + len(tmp) + return (struct.pack('hhh', sublen, vallen, typ) + + raw_name + b'\000\000' + pad + rawffi + pad2 + tmp) + + def __eq__(self, other): + return self.toRaw() == other + + def __str__(self, indent=u''): + indent = indent + u' ' + tmp = [kid.__str__(indent+u' ') + for kid in self.kids] + tmp = u', \n'.join(tmp) + return (u"""# UTF-8 +# +# For more details about fixed file info 'ffi' see: +# http://msdn.microsoft.com/en-us/library/ms646997.aspx +VSVersionInfo( +%sffi=%s, +%skids=[ +%s +%s] +) +""" % (indent, self.ffi.__str__(indent), indent, tmp, indent)) + + def __repr__(self): + return ("versioninfo.VSVersionInfo(ffi=%r, kids=%r)" % + (self.ffi, self.kids)) + + +def parseCommon(data, start=0): + i = start + 6 + (wLength, wValueLength, wType) = struct.unpack('3h', data[start:i]) + i, text = parseUString(data, i, i+wLength) + return i, (wLength, wValueLength, wType, text) + +def parseUString(data, start, limit): + i = start + while i < limit: + if data[i:i+2] == b'\000\000': + break + i += 2 + text = data[start:i].decode('UTF-16LE') + i += 2 + return i, text + + +class FixedFileInfo: + """ + DWORD dwSignature; //Contains the value 0xFEEFO4BD + DWORD dwStrucVersion; // binary version number of this structure. + // The high-order word of this member contains + // the major version number, and the low-order + // word contains the minor version number. + DWORD dwFileVersionMS; // most significant 32 bits of the file's binary + // version number + DWORD dwFileVersionLS; // + DWORD dwProductVersionMS; // most significant 32 bits of the binary version + // number of the product with which this file was + // distributed + DWORD dwProductVersionLS; // + DWORD dwFileFlagsMask; // bitmask that specifies the valid bits in + // dwFileFlags. A bit is valid only if it was + // defined when the file was created. + DWORD dwFileFlags; // VS_FF_DEBUG, VS_FF_PATCHED etc. + DWORD dwFileOS; // VOS_NT, VOS_WINDOWS32 etc. + DWORD dwFileType; // VFT_APP etc. + DWORD dwFileSubtype; // 0 unless VFT_DRV or VFT_FONT or VFT_VXD + DWORD dwFileDateMS; + DWORD dwFileDateLS; + """ + def __init__(self, filevers=(0, 0, 0, 0), prodvers=(0, 0, 0, 0), + mask=0x3f, flags=0x0, OS=0x40004, fileType=0x1, + subtype=0x0, date=(0, 0)): + self.sig = 0xfeef04bd + self.strucVersion = 0x10000 + self.fileVersionMS = (filevers[0] << 16) | (filevers[1] & 0xffff) + self.fileVersionLS = (filevers[2] << 16) | (filevers[3] & 0xffff) + self.productVersionMS = (prodvers[0] << 16) | (prodvers[1] & 0xffff) + self.productVersionLS = (prodvers[2] << 16) | (prodvers[3] & 0xffff) + self.fileFlagsMask = mask + self.fileFlags = flags + self.fileOS = OS + self.fileType = fileType + self.fileSubtype = subtype + self.fileDateMS = date[0] + self.fileDateLS = date[1] + + def fromRaw(self, data, i): + (self.sig, + self.strucVersion, + self.fileVersionMS, + self.fileVersionLS, + self.productVersionMS, + self.productVersionLS, + self.fileFlagsMask, + self.fileFlags, + self.fileOS, + self.fileType, + self.fileSubtype, + self.fileDateMS, + self.fileDateLS) = struct.unpack('13L', data[i:i + 52]) + return i + 52 + + def toRaw(self): + return struct.pack('13L', self.sig, + self.strucVersion, + self.fileVersionMS, + self.fileVersionLS, + self.productVersionMS, + self.productVersionLS, + self.fileFlagsMask, + self.fileFlags, + self.fileOS, + self.fileType, + self.fileSubtype, + self.fileDateMS, + self.fileDateLS) + + def __eq__(self, other): + return self.toRaw() == other + + def __str__(self, indent=u''): + fv = (self.fileVersionMS >> 16, self.fileVersionMS & 0xffff, + self.fileVersionLS >> 16, self.fileVersionLS & 0xFFFF) + pv = (self.productVersionMS >> 16, self.productVersionMS & 0xffff, + self.productVersionLS >> 16, self.productVersionLS & 0xFFFF) + fd = (self.fileDateMS, self.fileDateLS) + tmp = [u'FixedFileInfo(', + u'# filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)', + u'# Set not needed items to zero 0.', + u'filevers=%s,' % (fv,), + u'prodvers=%s,' % (pv,), + u"# Contains a bitmask that specifies the valid bits 'flags'r", + u'mask=%s,' % hex(self.fileFlagsMask), + u'# Contains a bitmask that specifies the Boolean attributes of the file.', + u'flags=%s,' % hex(self.fileFlags), + u'# The operating system for which this file was designed.', + u'# 0x4 - NT and there is no need to change it.', + u'OS=%s,' % hex(self.fileOS), + u'# The general type of file.', + u'# 0x1 - the file is an application.', + u'fileType=%s,' % hex(self.fileType), + u'# The function of the file.', + u'# 0x0 - the function is not defined for this fileType', + u'subtype=%s,' % hex(self.fileSubtype), + u'# Creation date and time stamp.', + u'date=%s' % (fd,), + u')' + ] + return (u'\n'+indent+u' ').join(tmp) + + def __repr__(self): + fv = (self.fileVersionMS >> 16, self.fileVersionMS & 0xffff, + self.fileVersionLS >> 16, self.fileVersionLS & 0xffff) + pv = (self.productVersionMS >> 16, self.productVersionMS & 0xffff, + self.productVersionLS >> 16, self.productVersionLS & 0xffff) + fd = (self.fileDateMS, self.fileDateLS) + return ('versioninfo.FixedFileInfo(filevers=%r, prodvers=%r, ' + 'mask=0x%x, flags=0x%x, OS=0x%x, ' + 'fileType=%r, subtype=0x%x, date=%r)' % + (fv, pv, + self.fileFlagsMask, self.fileFlags, self.fileOS, + self.fileType, self.fileSubtype, fd)) + + +class StringFileInfo(object): + """ + WORD wLength; // length of the version resource + WORD wValueLength; // length of the Value member in the current + // VS_VERSION_INFO structure + WORD wType; // 1 means text, 0 means binary + WCHAR szKey[]; // Contains the Unicode string 'StringFileInfo'. + WORD Padding[]; + StringTable Children[]; // list of zero or more String structures + """ + def __init__(self, kids=None): + self.name = u'StringFileInfo' + self.kids = kids or [] + + def fromRaw(self, sublen, vallen, name, data, i, limit): + self.name = name + while i < limit: + st = StringTable() + j = st.fromRaw(data, i, limit) + self.kids.append(st) + i = j + return i + + def toRaw(self): + raw_name = getRaw(self.name) + vallen = 0 + typ = 1 + sublen = 6 + len(raw_name) + 2 + pad = b'' + if sublen % 4: + pad = b'\000\000' + tmp = b''.join([kid.toRaw() for kid in self.kids]) + sublen = sublen + len(pad) + len(tmp) + return (struct.pack('hhh', sublen, vallen, typ) + + raw_name + b'\000\000' + pad + tmp) + + def __eq__(self, other): + return self.toRaw() == other + + def __str__(self, indent=u''): + newindent = indent + u' ' + tmp = [kid.__str__(newindent) + for kid in self.kids] + tmp = u', \n'.join(tmp) + return (u'%sStringFileInfo(\n%s[\n%s\n%s])' + % (indent, newindent, tmp, newindent)) + + def __repr__(self): + return 'versioninfo.StringFileInfo(%r)' % self.kids + + +class StringTable: + """ + WORD wLength; + WORD wValueLength; + WORD wType; + WCHAR szKey[]; + String Children[]; // list of zero or more String structures. + """ + def __init__(self, name=None, kids=None): + self.name = name or u'' + self.kids = kids or [] + + def fromRaw(self, data, i, limit): + i, (cpsublen, cpwValueLength, cpwType, self.name) = parseCodePage(data, i, limit) # should be code page junk + i = nextDWord(i) + while i < limit: + ss = StringStruct() + j = ss.fromRaw(data, i, limit) + i = j + self.kids.append(ss) + i = nextDWord(i) + return i + + def toRaw(self): + raw_name = getRaw(self.name) + vallen = 0 + typ = 1 + sublen = 6 + len(raw_name) + 2 + tmp = [] + for kid in self.kids: + raw = kid.toRaw() + if len(raw) % 4: + raw = raw + b'\000\000' + tmp.append(raw) + tmp = b''.join(tmp) + sublen += len(tmp) + return (struct.pack('hhh', sublen, vallen, typ) + + raw_name + b'\000\000' + tmp) + + def __eq__(self, other): + return self.toRaw() == other + + def __str__(self, indent=u''): + newindent = indent + u' ' + tmp = (u',\n%s' % newindent).join(str(kid) for kid in self.kids) + return (u"%sStringTable(\n%su'%s',\n%s[%s])" + % (indent, newindent, self.name, newindent, tmp)) + + def __repr__(self): + return 'versioninfo.StringTable(%r, %r)' % (self.name, self.kids) + + +class StringStruct: + """ + WORD wLength; + WORD wValueLength; + WORD wType; + WCHAR szKey[]; + WORD Padding[]; + String Value[]; + """ + def __init__(self, name=None, val=None): + self.name = name or u'' + self.val = val or u'' + + def fromRaw(self, data, i, limit): + i, (sublen, vallen, typ, self.name) = parseCommon(data, i) + limit = i + sublen + i = nextDWord(i) + i, self.val = parseUString(data, i, limit) + return i + + def toRaw(self): + raw_name = getRaw(self.name) + raw_val = getRaw(self.val) + # TODO document the size of vallen and sublen. + vallen = len(raw_val) + 2 + typ = 1 + sublen = 6 + len(raw_name) + 2 + pad = b'' + if sublen % 4: + pad = b'\000\000' + sublen = sublen + len(pad) + vallen + abcd = (struct.pack('hhh', sublen, vallen, typ) + + raw_name + b'\000\000' + pad + + raw_val + b'\000\000') + return abcd + + def __eq__(self, other): + return self.toRaw() == other + + def __str__(self, indent=''): + return u"StringStruct(u'%s', u'%s')" % (self.name, self.val) + + def __repr__(self): + return 'versioninfo.StringStruct(%r, %r)' % (self.name, self.val) + + +def parseCodePage(data, i, limit): + i, (sublen, wValueLength, wType, nm) = parseCommon(data, i) + return i, (sublen, wValueLength, wType, nm) + + +class VarFileInfo: + """ + WORD wLength; // length of the version resource + WORD wValueLength; // length of the Value member in the current + // VS_VERSION_INFO structure + WORD wType; // 1 means text, 0 means binary + WCHAR szKey[]; // Contains the Unicode string 'VarFileInfo'. + WORD Padding[]; + Var Children[]; // list of zero or more Var structures + """ + def __init__(self, kids=None): + self.kids = kids or [] + + def fromRaw(self, sublen, vallen, name, data, i, limit): + self.sublen = sublen + self.vallen = vallen + self.name = name + i = nextDWord(i) + while i < limit: + vs = VarStruct() + j = vs.fromRaw(data, i, limit) + self.kids.append(vs) + i = j + return i + + def toRaw(self): + self.vallen = 0 + self.wType = 1 + self.name = u'VarFileInfo' + raw_name = getRaw(self.name) + sublen = 6 + len(raw_name) + 2 + pad = b'' + if sublen % 4: + pad = b'\000\000' + tmp = b''.join([kid.toRaw() for kid in self.kids]) + self.sublen = sublen + len(pad) + len(tmp) + return (struct.pack('hhh', self.sublen, self.vallen, self.wType) + + raw_name + b'\000\000' + pad + tmp) + + def __eq__(self, other): + return self.toRaw() == other + + def __str__(self, indent=''): + return (indent + "VarFileInfo([%s])" % + ', '.join(str(kid) for kid in self.kids)) + + def __repr__(self): + return 'versioninfo.VarFileInfo(%r)' % self.kids + + +class VarStruct: + """ + WORD wLength; // length of the version resource + WORD wValueLength; // length of the Value member in the current + // VS_VERSION_INFO structure + WORD wType; // 1 means text, 0 means binary + WCHAR szKey[]; // Contains the Unicode string 'Translation' + // or a user-defined key string value + WORD Padding[]; // + WORD Value[]; // list of one or more values that are language + // and code-page identifiers + """ + def __init__(self, name=None, kids=None): + self.name = name or u'' + self.kids = kids or [] + + def fromRaw(self, data, i, limit): + i, (self.sublen, self.wValueLength, self.wType, self.name) = parseCommon(data, i) + i = nextDWord(i) + for j in range(0, self.wValueLength, 2): + kid = struct.unpack('h', data[i:i+2])[0] + self.kids.append(kid) + i += 2 + return i + + def toRaw(self): + self.wValueLength = len(self.kids) * 2 + self.wType = 0 + raw_name = getRaw(self.name) + sublen = 6 + len(raw_name) + 2 + pad = b'' + if sublen % 4: + pad = b'\000\000' + self.sublen = sublen + len(pad) + self.wValueLength + tmp = b''.join([struct.pack('h', kid) for kid in self.kids]) + return (struct.pack('hhh', self.sublen, self.wValueLength, self.wType) + + raw_name + b'\000\000' + pad + tmp) + + def __eq__(self, other): + return self.toRaw() == other + + def __str__(self, indent=u''): + return u"VarStruct(u'%s', %r)" % (self.name, self.kids) + + def __repr__(self): + return 'versioninfo.VarStruct(%r, %r)' % (self.name, self.kids) + + +def SetVersion(exenm, versionfile): + if isinstance(versionfile, VSVersionInfo): + vs = versionfile + else: + with codecs.open(versionfile, text_read_mode, 'utf-8') as fp: + txt = fp.read() + vs = eval(txt) + + # Remember overlay + pe = pefile.PE(exenm, fast_load=True) + overlay_before = pe.get_overlay() + pe.close() + + hdst = win32api.BeginUpdateResource(exenm, 0) + win32api.UpdateResource(hdst, pefile.RESOURCE_TYPE['RT_VERSION'], 1, vs.toRaw()) + win32api.EndUpdateResource (hdst, 0) + + if overlay_before: + # Check if the overlay is still present + pe = pefile.PE(exenm, fast_load=True) + overlay_after = pe.get_overlay() + pe.close() + + # If the update removed the overlay data, re-append it + if not overlay_after: + with open(exenm, 'ab') as exef: + exef.write(overlay_before) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winmanifest.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winmanifest.py new file mode 100644 index 0000000000000000000000000000000000000000..2eccdb93df232b562de5dd7cd2a5c918c01390c1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winmanifest.py @@ -0,0 +1,1119 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# Development notes kept for documentation purposes. +# +# Currently not implemented in the Manifest class: +# * Validation (only very basic sanity checks are currently in place) +# * comClass, typelib, comInterfaceProxyStub and windowClass child elements of +# the file element +# * comInterfaceExternalProxyStub and windowClass child elements of the +# assembly element +# * Application Configuration File and Multilanguage User Interface (MUI) +# support when searching for assembly files +# +# Isolated Applications and Side-by-side Assemblies: +# http://msdn.microsoft.com/en-us/library/dd408052%28VS.85%29.aspx +# +# Changelog: +# 2009-12-17 fix: small glitch in toxml / toprettyxml methods (xml declaration +# wasn't replaced when a different encodig than UTF-8 was used) +# chg: catch xml.parsers.expat.ExpatError and re-raise as +# ManifestXMLParseError +# chg: support initialize option in parse method also +# +# 2009-12-13 fix: fixed os import +# fix: skip invalid / empty dependent assemblies +# +# 2009-08-21 fix: Corrected assembly searching sequence for localized +# assemblies +# fix: Allow assemblies with no dependent files +# +# 2009-07-31 chg: Find private assemblies even if unversioned +# add: Manifest.same_id method to check if two manifests have the +# same assemblyIdentity +# +# 2009-07-30 fix: Potential failure in File.calc_hash method if hash +# algorythm not supported +# add: Publisher configuration (policy) support when searching for +# assembly files +# fix: Private assemblies are now actually found if present (and no +# shared assembly exists) +# add: Python 2.3 compatibility (oldest version supported by +# pyinstaller) +# +# 2009-07-28 chg: Code cleanup, removed a bit of redundancy +# add: silent mode (set silent attribute on module) +# chg: Do not print messages in silent mode +# +# 2009-06-18 chg: Use glob instead of regular expression in Manifest.find_files +# +# 2009-05-04 fix: Don't fail if manifest has empty description +# fix: Manifests created by the toxml, toprettyxml, writexml or +# writeprettyxml methods are now correctly recognized by +# Windows, which expects the XML declaration to be ordered +# version-encoding-standalone (standalone being optional) +# add: 'encoding' keyword argument in toxml, toprettyxml, writexml +# and writeprettyxml methods +# chg: UpdateManifestResourcesFromXML and +# UpdateManifestResourcesFromXMLFile: set resource name +# depending on file type ie. exe or dll +# fix: typo in __main__: UpdateManifestResourcesFromDataFile +# should have been UpdateManifestResourcesFromXMLFile +# +# 2009-03-21 First version + + +""" +Create, parse and write MS Windows Manifest files. +Find files which are part of an assembly, by searching shared and +private assemblies. +Update or add manifest resources in Win32 PE files. + +Commandline usage: +winmanifest.py +Updates or adds manifest as resource in Win32 PE file . +""" + + +import os +from glob import glob +import hashlib +import sys +import xml +from xml.dom import Node, minidom +from xml.dom.minidom import Document, Element + +from PyInstaller import compat +from PyInstaller.compat import string_types +from PyInstaller import log as logging +from PyInstaller.utils.win32 import winresource + +logger = logging.getLogger(__name__) + +LANGUAGE_NEUTRAL_NT5 = "x-ww" +LANGUAGE_NEUTRAL_NT6 = "none" +RT_MANIFEST = 24 + +Document.aChild = Document.appendChild +Document.cE = Document.createElement +Document.cT = Document.createTextNode +Document.getEByTN = Document.getElementsByTagName +Element.aChild = Element.appendChild +Element.getA = Element.getAttribute +Element.getEByTN = Element.getElementsByTagName +Element.remA = Element.removeAttribute +Element.setA = Element.setAttribute + + +def getChildElementsByTagName(self, tagName): + """ Return child elements of type tagName if found, else [] """ + result = [] + for child in self.childNodes: + if isinstance(child, Element): + if child.tagName == tagName: + result.append(child) + return result + + +def getFirstChildElementByTagName(self, tagName): + """ Return the first element of type tagName if found, else None """ + for child in self.childNodes: + if isinstance(child, Element): + if child.tagName == tagName: + return child + return None + + +Document.getCEByTN = getChildElementsByTagName +Document.getFCEByTN = getFirstChildElementByTagName +Element.getCEByTN = getChildElementsByTagName +Element.getFCEByTN = getFirstChildElementByTagName + + +class _Dummy: + pass + + +if winresource: + _File = winresource.File +else: + _File = _Dummy + + +class File(_File): + + """ A file referenced by an assembly inside a manifest. """ + + def __init__(self, filename="", hashalg=None, hash=None, comClasses=None, + typelibs=None, comInterfaceProxyStubs=None, + windowClasses=None): + if winresource: + winresource.File.__init__(self, filename) + else: + self.filename = filename + self.name = os.path.basename(filename) + if hashalg: + self.hashalg = hashalg.upper() + else: + self.hashalg = None + if (os.path.isfile(filename) and hashalg and hashlib and + hasattr(hashlib, hashalg.lower())): + self.calc_hash() + else: + self.hash = hash + self.comClasses = comClasses or [] # TO-DO: implement + self.typelibs = typelibs or [] # TO-DO: implement + self.comInterfaceProxyStubs = comInterfaceProxyStubs or [] # TO-DO: implement + self.windowClasses = windowClasses or [] # TO-DO: implement + + def calc_hash(self, hashalg=None): + """ + Calculate the hash of the file. + + Will be called automatically from the constructor if the file exists + and hashalg is given (and supported), but may also be called manually + e.g. to update the hash if the file has changed. + + """ + with open(self.filename, "rb") as fd: + buf = fd.read() + if hashalg: + self.hashalg = hashalg.upper() + self.hash = getattr(hashlib, self.hashalg.lower())(buf).hexdigest() + + def find(self, searchpath): + logger.info("Searching for file %s", self.name) + fn = os.path.join(searchpath, self.name) + if os.path.isfile(fn): + logger.info("Found file %s", fn) + return fn + else: + logger.warning("No such file %s", fn) + return None + + +class InvalidManifestError(Exception): + pass + + +class ManifestXMLParseError(InvalidManifestError): + pass + + +class Manifest(object): + + # Manifests: + # http://msdn.microsoft.com/en-us/library/aa375365%28VS.85%29.aspx + + """ + Manifest constructor. + + To build a basic manifest for your application: + mf = Manifest(type='win32', name='YourAppName', language='*', + processorArchitecture='x86', version=[1, 0, 0, 0]) + + To write the XML to a manifest file: + mf.writexml("YourAppName.exe.manifest") + or + mf.writeprettyxml("YourAppName.exe.manifest") + + """ + + def __init__(self, manifestVersion=None, noInheritable=False, + noInherit=False, type_=None, name=None, language=None, + processorArchitecture=None, version=None, + publicKeyToken=None, description=None, + requestedExecutionLevel=None, uiAccess=None, + dependentAssemblies=None, files=None, + comInterfaceExternalProxyStubs=None): + self.filename = None + self.optional = None + self.manifestType = "assembly" + self.manifestVersion = manifestVersion or [1, 0] + self.noInheritable = noInheritable + self.noInherit = noInherit + self.type = type_ + self.name = name + self.language = language + self.processorArchitecture = processorArchitecture + self.version = version + self.publicKeyToken = publicKeyToken + # publicKeyToken: + # A 16-character hexadecimal string that represents the last 8 bytes + # of the SHA-1 hash of the public key under which the assembly is + # signed. The public key used to sign the catalog must be 2048 bits + # or greater. Required for all shared side-by-side assemblies. + # http://msdn.microsoft.com/en-us/library/aa375692(VS.85).aspx + self.applyPublisherPolicy = None + self.description = None + self.requestedExecutionLevel = requestedExecutionLevel + self.uiAccess = uiAccess + self.dependentAssemblies = dependentAssemblies or [] + self.bindingRedirects = [] + self.files = files or [] + self.comInterfaceExternalProxyStubs = comInterfaceExternalProxyStubs or [] # TO-DO: implement + + def __eq__(self, other): + if isinstance(other, Manifest): + return self.toxml() == other.toxml() + if isinstance(other, string_types): + return self.toxml() == other + return False + + def __ne__(self, other): + return not self.__eq__(other) + + def __repr__(self): + return repr(self.toxml()) + + def add_dependent_assembly(self, manifestVersion=None, noInheritable=False, + noInherit=False, type_=None, name=None, language=None, + processorArchitecture=None, version=None, + publicKeyToken=None, description=None, + requestedExecutionLevel=None, uiAccess=None, + dependentAssemblies=None, files=None, + comInterfaceExternalProxyStubs=None): + """ + Shortcut for self.dependentAssemblies.append(Manifest(*args, **kwargs)) + """ + self.dependentAssemblies.append(Manifest(manifestVersion, + noInheritable, noInherit, type_, name, + language, processorArchitecture, + version, publicKeyToken, description, + requestedExecutionLevel, uiAccess, + dependentAssemblies, files, + comInterfaceExternalProxyStubs)) + if self.filename: + # Enable search for private assembly by assigning bogus filename + # (only the directory has to be correct) + self.dependentAssemblies[-1].filename = ":".join((self.filename, + name)) + + def add_file(self, name="", hashalg="", hash="", comClasses=None, + typelibs=None, comInterfaceProxyStubs=None, + windowClasses=None): + """ Shortcut for manifest.files.append """ + self.files.append(File(name, hashalg, hash, comClasses, + typelibs, comInterfaceProxyStubs, windowClasses)) + + @classmethod + def get_winsxs_dir(cls): + return os.path.join(compat.getenv("SystemRoot"), "WinSxS") + + @classmethod + def get_manifest_dir(cls): + winsxs = cls.get_winsxs_dir() + if not os.path.isdir(winsxs): + logger.warning("No such dir %s", winsxs) + manifests = os.path.join(winsxs, "Manifests") + if not os.path.isdir(manifests): + logger.warning("No such dir %s", manifests) + return manifests + + @classmethod + def get_policy_dir(cls): + winsxs = os.path.join(compat.getenv("SystemRoot"), "WinSxS") + if sys.getwindowsversion() < (6, ): + # Windows XP + pcfiles = os.path.join(winsxs, "Policies") + if not os.path.isdir(pcfiles): + logger.warning("No such dir %s", pcfiles) + else: + # Vista or later + pcfiles = cls.get_manifest_dir() + return pcfiles + + + def get_policy_redirect(self, language=None, version=None): + # Publisher Configuration (aka policy) + # A publisher configuration file globally redirects + # applications and assemblies having a dependence on one + # version of a side-by-side assembly to use another version of + # the same assembly. This enables applications and assemblies + # to use the updated assembly without having to rebuild all of + # the affected applications. + # http://msdn.microsoft.com/en-us/library/aa375680%28VS.85%29.aspx + # + # Under Windows XP and 2003, policies are stored as + # .policy files inside + # %SystemRoot%\WinSxS\Policies\ + # Under Vista and later, policies are stored as + # .manifest files inside %SystemRoot%\winsxs\Manifests + redirected = False + pcfiles = self.get_policy_dir() + if version is None: + version = self.version + if language is None: + language = self.language + + if os.path.isdir(pcfiles): + logger.debug("Searching for publisher configuration %s ...", + self.getpolicyid(True, language=language)) + if sys.getwindowsversion() < (6, ): + # Windows XP + policies = os.path.join(pcfiles, + self.getpolicyid(True, + language=language) + + ".policy") + else: + # Vista or later + policies = os.path.join(pcfiles, + self.getpolicyid(True, + language=language) + + ".manifest") + for manifestpth in glob(policies): + if not os.path.isfile(manifestpth): + logger.warning("Not a file %s", manifestpth) + continue + logger.info("Found %s", manifestpth) + try: + policy = ManifestFromXMLFile(manifestpth) + except Exception: + logger.error("Could not parse file %s", + manifestpth, exc_info=1) + else: + logger.debug("Checking publisher policy for " + "binding redirects") + for assembly in policy.dependentAssemblies: + if (not assembly.same_id(self, True) or + assembly.optional): + continue + for redirect in assembly.bindingRedirects: + old = "-".join([".".join([str(i) + for i in + part]) + for part in + redirect[0]]) + new = ".".join([str(i) + for i in + redirect[1]]) + logger.debug("Found redirect for " + "version(s) %s -> %s", + old, new) + if (version >= redirect[0][0] and + version <= redirect[0][-1] and + version != redirect[1]): + logger.debug("Applying redirect " + "%s -> %s", + ".".join([str(i) + for i in + version]), + new) + version = redirect[1] + redirected = True + if not redirected: + logger.debug("Publisher configuration not used") + + return version + + def find_files(self, ignore_policies=True): + """ Search shared and private assemblies and return a list of files. + + If any files are not found, return an empty list. + + IMPORTANT NOTE: On some Windows systems, the dependency listed in the manifest + will not actually be present, and finding its files will fail. This is because + a newer version of the dependency is installed, and the manifest's dependency + is being redirected to a newer version. To properly bundle the newer version of + the assembly, you need to find the newer version by setting + ignore_policies=False, and then either create a .config file for each bundled + assembly, or modify each bundled assembly to point to the newer version. + + This is important because Python 2.7's app manifest depends on version 21022 + of the VC90 assembly, but the Python 2.7.9 installer will install version + 30729 of the assembly along with a policy file that enacts the version redirect. + + """ + + # Shared Assemblies: + # http://msdn.microsoft.com/en-us/library/aa375996%28VS.85%29.aspx + # + # Private Assemblies: + # http://msdn.microsoft.com/en-us/library/aa375674%28VS.85%29.aspx + # + # Assembly Searching Sequence: + # http://msdn.microsoft.com/en-us/library/aa374224%28VS.85%29.aspx + # + # NOTE: + # Multilanguage User Interface (MUI) support not yet implemented + + files = [] + + languages = [] + if self.language not in (None, "", "*", "neutral"): + languages.append(self.getlanguage()) + if "-" in self.language: + # language-culture syntax, e.g. en-us + # Add only the language part + languages.append(self.language.split("-")[0]) + if self.language not in ("en-us", "en"): + languages.append("en-us") + if self.language != "en": + languages.append("en") + languages.append(self.getlanguage("*")) + + manifests = self.get_manifest_dir() + winsxs = self.get_winsxs_dir() + + for language in languages: + version = self.version + + # Search for publisher configuration + if not ignore_policies and version: + version = self.get_policy_redirect(language, version) + + # Search for assemblies according to assembly searching sequence + paths = [] + if os.path.isdir(manifests): + # Add winsxs search paths + # Search for manifests in Windows\WinSxS\Manifests + paths.extend(glob(os.path.join(manifests, + self.getid(language=language, + version=version) + + "_*.manifest"))) + if self.filename: + # Add private assembly search paths + # Search for manifests inside assembly folders that are in + # the same folder as the depending manifest. + dirnm = os.path.dirname(self.filename) + if language in (LANGUAGE_NEUTRAL_NT5, + LANGUAGE_NEUTRAL_NT6): + for ext in (".dll", ".manifest"): + paths.extend(glob(os.path.join(dirnm, self.name + ext))) + paths.extend(glob(os.path.join(dirnm, self.name, + self.name + ext))) + else: + for ext in (".dll", ".manifest"): + paths.extend(glob(os.path.join(dirnm, language, + self.name + ext))) + for ext in (".dll", ".manifest"): + paths.extend(glob(os.path.join(dirnm, language, + self.name, + self.name + ext))) + logger.info("Searching for assembly %s ...", + self.getid(language=language, version=version)) + for manifestpth in paths: + if not os.path.isfile(manifestpth): + logger.warning("Not a file %s", manifestpth) + continue + assemblynm = os.path.basename( + os.path.splitext(manifestpth)[0]) + try: + if manifestpth.endswith(".dll"): + logger.info("Found manifest in %s", manifestpth) + manifest = ManifestFromResFile(manifestpth, [1]) + else: + logger.info("Found manifest %s", manifestpth) + manifest = ManifestFromXMLFile(manifestpth) + except Exception: + logger.error("Could not parse manifest %s", + manifestpth, exc_info=1) + else: + if manifestpth.startswith(winsxs): + # Manifest is in Windows\WinSxS\Manifests, so assembly + # dir is in Windows\WinSxS + assemblydir = os.path.join(winsxs, assemblynm) + if not os.path.isdir(assemblydir): + logger.warning("No such dir %s", assemblydir) + logger.warning("Assembly incomplete") + return [] + else: + # Manifest is inside assembly dir. + assemblydir = os.path.dirname(manifestpth) + files.append(manifestpth) + for file_ in self.files or manifest.files: + fn = file_.find(assemblydir) + if fn: + files.append(fn) + else: + # If any of our files does not exist, + # the assembly is incomplete + logger.warning("Assembly incomplete") + return [] + return files + + logger.warning("Assembly not found") + return [] + + def getid(self, language=None, version=None): + """ + Return an identification string which uniquely names a manifest. + + This string is a combination of the manifest's processorArchitecture, + name, publicKeyToken, version and language. + + Arguments: + version (tuple or list of integers) - If version is given, use it + instead of the manifest's + version. + + """ + if not self.name: + logger.warning("Assembly metadata incomplete") + return "" + id = [] + if self.processorArchitecture: + id.append(self.processorArchitecture) + id.append(self.name) + if self.publicKeyToken: + id.append(self.publicKeyToken) + if version or self.version: + id.append(".".join([str(i) for i in version or self.version])) + if not language: + language = self.getlanguage() + if language: + id.append(language) + return "_".join(id) + + def getlanguage(self, language=None, windowsversion=None): + """ + Get and return the manifest's language as string. + + Can be either language-culture e.g. 'en-us' or a string indicating + language neutrality, e.g. 'x-ww' on Windows XP or 'none' on Vista + and later. + + """ + if not language: + language = self.language + if language in (None, "", "*", "neutral"): + return (LANGUAGE_NEUTRAL_NT5, + LANGUAGE_NEUTRAL_NT6)[(windowsversion or + sys.getwindowsversion()) >= (6, )] + return language + + def getpolicyid(self, fuzzy=True, language=None, windowsversion=None): + """ + Return an identification string which can be used to find a policy. + + This string is a combination of the manifest's processorArchitecture, + major and minor version, name, publicKeyToken and language. + + Arguments: + fuzzy (boolean) - If False, insert the full version in + the id string. Default is True (omit). + windowsversion - If not specified (or None), default to + (tuple or list of integers) sys.getwindowsversion(). + + """ + if not self.name: + logger.warning("Assembly metadata incomplete") + return "" + id = [] + if self.processorArchitecture: + id.append(self.processorArchitecture) + name = [] + name.append("policy") + if self.version: + name.append(str(self.version[0])) + name.append(str(self.version[1])) + name.append(self.name) + id.append(".".join(name)) + if self.publicKeyToken: + id.append(self.publicKeyToken) + if self.version and (windowsversion or sys.getwindowsversion()) >= (6, ): + # Vista and later + if fuzzy: + id.append("*") + else: + id.append(".".join([str(i) for i in self.version])) + if not language: + language = self.getlanguage(windowsversion=windowsversion) + if language: + id.append(language) + id.append("*") + id = "_".join(id) + if self.version and (windowsversion or sys.getwindowsversion()) < (6, ): + # Windows XP + if fuzzy: + id = os.path.join(id, "*") + else: + id = os.path.join(id, ".".join([str(i) for i in self.version])) + return id + + def load_dom(self, domtree, initialize=True): + """ + Load manifest from DOM tree. + + If initialize is True (default), reset existing attributes first. + + """ + if domtree.nodeType == Node.DOCUMENT_NODE: + rootElement = domtree.documentElement + elif domtree.nodeType == Node.ELEMENT_NODE: + rootElement = domtree + else: + raise InvalidManifestError("Invalid root element node type " + + str(rootElement.nodeType) + + " - has to be one of (DOCUMENT_NODE, " + "ELEMENT_NODE)") + allowed_names = ("assembly", "assemblyBinding", "configuration", + "dependentAssembly") + if rootElement.tagName not in allowed_names: + raise InvalidManifestError( + "Invalid root element <%s> - has to be one of <%s>" % + (rootElement.tagName, ">, <".join(allowed_names))) + # logger.info("loading manifest metadata from element <%s>", rootElement.tagName) + if rootElement.tagName == "configuration": + for windows in rootElement.getCEByTN("windows"): + for assemblyBinding in windows.getCEByTN("assemblyBinding"): + self.load_dom(assemblyBinding, initialize) + else: + if initialize: + self.__init__() + self.manifestType = rootElement.tagName + self.manifestVersion = [int(i) + for i in + (rootElement.getA("manifestVersion") or + "1.0").split(".")] + self.noInheritable = bool(rootElement.getFCEByTN("noInheritable")) + self.noInherit = bool(rootElement.getFCEByTN("noInherit")) + for assemblyIdentity in rootElement.getCEByTN("assemblyIdentity"): + self.type = assemblyIdentity.getA("type") or None + self.name = assemblyIdentity.getA("name") or None + self.language = assemblyIdentity.getA("language") or None + self.processorArchitecture = assemblyIdentity.getA( + "processorArchitecture") or None + version = assemblyIdentity.getA("version") + if version: + self.version = tuple(int(i) for i in version.split(".")) + self.publicKeyToken = assemblyIdentity.getA("publicKeyToken") or None + for publisherPolicy in rootElement.getCEByTN("publisherPolicy"): + self.applyPublisherPolicy = (publisherPolicy.getA("apply") or + "").lower() == "yes" + for description in rootElement.getCEByTN("description"): + if description.firstChild: + self.description = description.firstChild.wholeText + for trustInfo in rootElement.getCEByTN("trustInfo"): + for security in trustInfo.getCEByTN("security"): + for reqPriv in security.getCEByTN("requestedPrivileges"): + for reqExeLev in reqPriv.getCEByTN("requestedExecutionLevel"): + self.requestedExecutionLevel = reqExeLev.getA("level") + self.uiAccess = (reqExeLev.getA("uiAccess") or + "").lower() == "true" + if rootElement.tagName == "assemblyBinding": + dependencies = [rootElement] + else: + dependencies = rootElement.getCEByTN("dependency") + for dependency in dependencies: + for dependentAssembly in dependency.getCEByTN( + "dependentAssembly"): + manifest = ManifestFromDOM(dependentAssembly) + if not manifest.name: + # invalid, skip + continue + manifest.optional = (dependency.getA("optional") or + "").lower() == "yes" + self.dependentAssemblies.append(manifest) + if self.filename: + # Enable search for private assembly by assigning bogus + # filename (only the directory has to be correct) + self.dependentAssemblies[-1].filename = ":".join( + (self.filename, manifest.name)) + for bindingRedirect in rootElement.getCEByTN("bindingRedirect"): + oldVersion = tuple(tuple(int(i) for i in part.split(".")) + for part in + bindingRedirect.getA("oldVersion").split("-")) + newVersion = tuple(int(i) + for i in + bindingRedirect.getA("newVersion").split(".")) + self.bindingRedirects.append((oldVersion, newVersion)) + for file_ in rootElement.getCEByTN("file"): + self.add_file(name=file_.getA("name"), + hashalg=file_.getA("hashalg"), + hash=file_.getA("hash")) + + def parse(self, filename_or_file, initialize=True): + """ Load manifest from file or file object """ + if isinstance(filename_or_file, string_types): + filename = filename_or_file + else: + filename = filename_or_file.name + try: + domtree = minidom.parse(filename_or_file) + except xml.parsers.expat.ExpatError as e: + args = ['\n File "%r"\n ' % filename, str(e.args[0])] + raise ManifestXMLParseError(" ".join(args)) from e + if initialize: + self.__init__() + self.filename = filename + self.load_dom(domtree, False) + + def parse_string(self, xmlstr, initialize=True): + """ Load manifest from XML string """ + try: + domtree = minidom.parseString(xmlstr) + except xml.parsers.expat.ExpatError as e: + raise ManifestXMLParseError(e) from e + self.load_dom(domtree, initialize) + + def same_id(self, manifest, skip_version_check=False): + """ + Return a bool indicating if another manifest has the same identitiy. + + This is done by comparing language, name, processorArchitecture, + publicKeyToken, type and version. + + """ + if skip_version_check: + version_check = True + else: + version_check = self.version == manifest.version + return (self.language == manifest.language and + self.name == manifest.name and + self.processorArchitecture == manifest.processorArchitecture and + self.publicKeyToken == manifest.publicKeyToken and + self.type == manifest.type and + version_check) + + def todom(self): + """ Return the manifest as DOM tree """ + doc = Document() + docE = doc.cE(self.manifestType) + if self.manifestType == "assemblyBinding": + cfg = doc.cE("configuration") + win = doc.cE("windows") + win.aChild(docE) + cfg.aChild(win) + doc.aChild(cfg) + else: + doc.aChild(docE) + if self.manifestType != "dependentAssembly": + docE.setA("xmlns", "urn:schemas-microsoft-com:asm.v1") + if self.manifestType != "assemblyBinding": + docE.setA("manifestVersion", + ".".join([str(i) for i in self.manifestVersion])) + if self.noInheritable: + docE.aChild(doc.cE("noInheritable")) + if self.noInherit: + docE.aChild(doc.cE("noInherit")) + aId = doc.cE("assemblyIdentity") + if self.type: + aId.setAttribute("type", self.type) + if self.name: + aId.setAttribute("name", self.name) + if self.language: + aId.setAttribute("language", self.language) + if self.processorArchitecture: + aId.setAttribute("processorArchitecture", + self.processorArchitecture) + if self.version: + aId.setAttribute("version", + ".".join([str(i) for i in self.version])) + if self.publicKeyToken: + aId.setAttribute("publicKeyToken", self.publicKeyToken) + if aId.hasAttributes(): + docE.aChild(aId) + else: + aId.unlink() + if self.applyPublisherPolicy != None: + ppE = doc.cE("publisherPolicy") + if self.applyPublisherPolicy: + ppE.setA("apply", "yes") + else: + ppE.setA("apply", "no") + docE.aChild(ppE) + if self.description: + descE = doc.cE("description") + descE.aChild(doc.cT(self.description)) + docE.aChild(descE) + if self.requestedExecutionLevel in ("asInvoker", "highestAvailable", + "requireAdministrator"): + tE = doc.cE("trustInfo") + tE.setA("xmlns", "urn:schemas-microsoft-com:asm.v3") + sE = doc.cE("security") + rpE = doc.cE("requestedPrivileges") + relE = doc.cE("requestedExecutionLevel") + relE.setA("level", self.requestedExecutionLevel) + if self.uiAccess: + relE.setA("uiAccess", "true") + else: + relE.setA("uiAccess", "false") + rpE.aChild(relE) + sE.aChild(rpE) + tE.aChild(sE) + docE.aChild(tE) + if self.dependentAssemblies: + for assembly in self.dependentAssemblies: + if self.manifestType != "assemblyBinding": + dE = doc.cE("dependency") + if assembly.optional: + dE.setAttribute("optional", "yes") + daE = doc.cE("dependentAssembly") + adom = assembly.todom() + for child in adom.documentElement.childNodes: + daE.aChild(child.cloneNode(False)) + adom.unlink() + if self.manifestType != "assemblyBinding": + dE.aChild(daE) + docE.aChild(dE) + else: + docE.aChild(daE) + if self.bindingRedirects: + for bindingRedirect in self.bindingRedirects: + brE = doc.cE("bindingRedirect") + brE.setAttribute("oldVersion", + "-".join([".".join([str(i) + for i in + part]) + for part in + bindingRedirect[0]])) + brE.setAttribute("newVersion", + ".".join([str(i) for i in bindingRedirect[1]])) + docE.aChild(brE) + if self.files: + for file_ in self.files: + fE = doc.cE("file") + for attr in ("name", "hashalg", "hash"): + val = getattr(file_, attr) + if val: + fE.setA(attr, val) + docE.aChild(fE) + + # Add compatibility section: http://stackoverflow.com/a/10158920 + cE = doc.cE("compatibility") + cE.setAttribute("xmlns", "urn:schemas-microsoft-com:compatibility.v1") + caE = doc.cE("application") + supportedOS_guids = {"Vista":"{e2011457-1546-43c5-a5fe-008deee3d3f0}", + "7" :"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}", + "8" :"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}", + "8.1" :"{1f676c76-80e1-4239-95bb-83d0f6d0da78}", + "10" :"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"} + for guid in supportedOS_guids.values(): + sosE = doc.cE("supportedOS") + sosE.setAttribute("Id", guid) + caE.aChild(sosE) + cE.aChild(caE) + docE.aChild(cE) + + # Add application.windowsSettings section to enable longPathAware + # option (issue #5423). + if self.manifestType == "assembly": + aE = doc.cE("application") + aE.setAttribute("xmlns", "urn:schemas-microsoft-com:asm.v3") + wsE = doc.cE("windowsSettings") + lpaE = doc.cE("longPathAware") + lpaE.setAttribute( + "xmlns", + "http://schemas.microsoft.com/SMI/2016/WindowsSettings" + ) + lpaT = doc.cT("true") + lpaE.aChild(lpaT) + wsE.aChild(lpaE) + aE.aChild(wsE) + docE.aChild(aE) + + return doc + + def toprettyxml(self, indent=" ", newl=os.linesep, encoding="UTF-8"): + """ Return the manifest as pretty-printed XML """ + domtree = self.todom() + # WARNING: The XML declaration has to follow the order + # version-encoding-standalone (standalone being optional), otherwise + # if it is embedded in an exe the exe will fail to launch! + # ('application configuration incorrect') + xmlstr = domtree.toprettyxml(indent, newl, encoding) + xmlstr = xmlstr.decode(encoding).strip(os.linesep).replace( + '' % encoding, + '' % + encoding) + domtree.unlink() + return xmlstr + + def toxml(self, encoding="UTF-8"): + """ Return the manifest as XML """ + domtree = self.todom() + # WARNING: The XML declaration has to follow the order + # version-encoding-standalone (standalone being optional), otherwise + # if it is embedded in an exe the exe will fail to launch! + # ('application configuration incorrect') + xmlstr = domtree.toxml(encoding).decode().replace( + '' % encoding, + '' % + encoding) + domtree.unlink() + return xmlstr + + def update_resources(self, dstpath, names=None, languages=None): + """ Update or add manifest resource in dll/exe file dstpath """ + UpdateManifestResourcesFromXML(dstpath, + self.toprettyxml().encode("UTF-8"), + names, languages) + + def writeprettyxml(self, filename_or_file=None, indent=" ", newl=os.linesep, + encoding="UTF-8"): + """ Write the manifest as XML to a file or file object """ + if not filename_or_file: + filename_or_file = self.filename + if isinstance(filename_or_file, string_types): + filename_or_file = open(filename_or_file, "wb") + xmlstr = self.toprettyxml(indent, newl, encoding) + with filename_or_file: + filename_or_file.write(xmlstr.encode()) + + def writexml(self, filename_or_file=None, indent=" ", newl=os.linesep, + encoding="UTF-8"): + """ Write the manifest as XML to a file or file object """ + if not filename_or_file: + filename_or_file = self.filename + if isinstance(filename_or_file, string_types): + filename_or_file = open(filename_or_file, "wb") + xmlstr = self.toxml(encoding) + with filename_or_file: + filename_or_file.write(xmlstr.encode()) + + +def ManifestFromResFile(filename, names=None, languages=None): + """ Create and return manifest instance from resource in dll/exe file """ + res = GetManifestResources(filename, names, languages) + pth = [] + if res and res[RT_MANIFEST]: + while isinstance(res, dict) and res.keys(): + key, res = next(iter(res.items())) + pth.append(str(key)) + if isinstance(res, dict): + raise InvalidManifestError("No matching manifest resource found in '%s'" % + filename) + manifest = Manifest() + manifest.filename = ":".join([filename] + pth) + manifest.parse_string(res, False) + return manifest + + +def ManifestFromDOM(domtree): + """ Create and return manifest instance from DOM tree """ + manifest = Manifest() + manifest.load_dom(domtree) + return manifest + + +def ManifestFromXML(xmlstr): + """ Create and return manifest instance from XML """ + manifest = Manifest() + manifest.parse_string(xmlstr) + return manifest + + +def ManifestFromXMLFile(filename_or_file): + """ Create and return manifest instance from file """ + manifest = Manifest() + manifest.parse(filename_or_file) + return manifest + + +def GetManifestResources(filename, names=None, languages=None): + """ Get manifest resources from file """ + return winresource.GetResources(filename, [RT_MANIFEST], names, languages) + + +def UpdateManifestResourcesFromXML(dstpath, xmlstr, names=None, + languages=None): + """ Update or add manifest XML as resource in dstpath """ + logger.info("Updating manifest in %s", dstpath) + if dstpath.lower().endswith(".exe"): + name = 1 + else: + name = 2 + winresource.UpdateResources(dstpath, xmlstr, RT_MANIFEST, names or [name], + languages or [0, "*"]) + + +def UpdateManifestResourcesFromXMLFile(dstpath, srcpath, names=None, + languages=None): + """ Update or add manifest XML from srcpath as resource in dstpath """ + logger.info("Updating manifest from %s in %s", srcpath, dstpath) + if dstpath.lower().endswith(".exe"): + name = 1 + else: + name = 2 + winresource.UpdateResourcesFromDataFile(dstpath, srcpath, RT_MANIFEST, + names or [name], + languages or [0, "*"]) + + +def create_manifest(filename, manifest, console, uac_admin=False, uac_uiaccess=False): + """ + Create assembly manifest. + """ + if not manifest: + manifest = ManifestFromXMLFile(filename) + # /path/NAME.exe.manifest - split extension twice to get NAME. + name = os.path.basename(filename) + manifest.name = os.path.splitext(os.path.splitext(name)[0])[0] + elif isinstance(manifest, string_types) and "<" in manifest: + # Assume XML string + manifest = ManifestFromXML(manifest) + elif not isinstance(manifest, Manifest): + # Assume filename + manifest = ManifestFromXMLFile(manifest) + dep_names = set([dep.name for dep in manifest.dependentAssemblies]) + if manifest.filename != filename: + # Update dependent assemblies + depmanifest = ManifestFromXMLFile(filename) + for assembly in depmanifest.dependentAssemblies: + if not assembly.name in dep_names: + manifest.dependentAssemblies.append(assembly) + dep_names.add(assembly.name) + if (not console and + not "Microsoft.Windows.Common-Controls" in dep_names): + # Add Microsoft.Windows.Common-Controls to dependent assemblies + manifest.dependentAssemblies.append( + Manifest(type_="win32", + name="Microsoft.Windows.Common-Controls", + language="*", + processorArchitecture=processor_architecture(), + version=(6, 0, 0, 0), + publicKeyToken="6595b64144ccf1df") + ) + if uac_admin: + manifest.requestedExecutionLevel = 'requireAdministrator' + else: + manifest.requestedExecutionLevel = 'asInvoker' + if uac_uiaccess: + manifest.uiAccess = True + + # only write a new manifest if it is different from the old + need_new = not os.path.exists(filename) + if not need_new: + old_xml = ManifestFromXMLFile(filename).toprettyxml() + new_xml = manifest.toprettyxml().replace('\r','') + + # this only works if PYTHONHASHSEED is set in environment + need_new = (old_xml != new_xml) + if need_new: + manifest.writeprettyxml(filename) + + return manifest + + +def processor_architecture(): + """ + Detect processor architecture for assembly manifest. + + According to: + http://msdn.microsoft.com/en-us/library/windows/desktop/aa374219(v=vs.85).aspx + item processorArchitecture in assembly manifest is + + 'x86' - 32bit Windows + 'amd64' - 64bit Windows + """ + if compat.architecture == '32bit': + return 'x86' + else: + return 'amd64' + + +if __name__ == "__main__": + dstpath = sys.argv[1] + srcpath = sys.argv[2] + UpdateManifestResourcesFromXMLFile(dstpath, srcpath) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winresource.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winresource.py new file mode 100644 index 0000000000000000000000000000000000000000..ab1d1fa4e326f78725d3074f8ea8fdbbc3ff02a4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winresource.py @@ -0,0 +1,258 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Read and write resources from/to Win32 PE files. + +Commandline usage: +winresource.py +Updates or adds resources from file in file . +""" + +from ...compat import pywintypes, win32api + +import PyInstaller.log as logging +logger = logging.getLogger(__name__) + + +LOAD_LIBRARY_AS_DATAFILE = 2 +ERROR_BAD_EXE_FORMAT = 193 +ERROR_RESOURCE_DATA_NOT_FOUND = 1812 +ERROR_RESOURCE_TYPE_NOT_FOUND = 1813 +ERROR_RESOURCE_NAME_NOT_FOUND = 1814 +ERROR_RESOURCE_LANG_NOT_FOUND = 1815 + + +class File(object): + """ + Win32 PE file class. + """ + def __init__(self, filename): + self.filename = filename + + def get_resources(self, types=None, names=None, languages=None): + """ + Get resources. + + types = a list of resource types to search for (None = all) + names = a list of resource names to search for (None = all) + languages = a list of resource languages to search for (None = all) + Return a dict of the form {type_: {name: {language: data}}} which + might also be empty if no matching resources were found. + """ + return GetResources(self.filename, types, names, languages) + + def update_resources(self, data, type_, names=None, languages=None): + """ + Update or add resource data. + + type_ = resource type to update + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + UpdateResources(self.filename, data, type_, names, languages) + + def update_resources_from_datafile(self, srcpath, type_, names=None, + languages=None): + """ + Update or add resource data from file srcpath. + + type_ = resource type to update + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + UpdateResourcesFromDataFile(self.filename, srcpath, type_, names, + languages) + + def update_resources_from_dict(self, res, types=None, names=None, + languages=None): + """ + Update or add resources from resource dict. + + types = a list of resource types to update (None = all) + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + UpdateResourcesFromDict(self.filename, res, types, names, + languages) + + def update_resources_from_resfile(self, srcpath, types=None, names=None, + languages=None): + """ + Update or add resources from dll/exe file srcpath. + + types = a list of resource types to update (None = all) + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + UpdateResourcesFromResFile(self.filename, srcpath, types, names, + languages) + + +def _GetResources(hsrc, types=None, names=None, languages=None): + """ + Get resources from hsrc. + + types = a list of resource types to search for (None = all) + names = a list of resource names to search for (None = all) + languages = a list of resource languages to search for (None = all) + Return a dict of the form {type_: {name: {language: data}}} which + might also be empty if no matching resources were found. + """ + if types: + types = set(types) + if names: + names = set(names) + if languages: + languages = set(languages) + res = {} + try: + # logger.debug("Enumerating resource types") + enum_types = win32api.EnumResourceTypes(hsrc) + if types and not "*" in types: + enum_types = filter(lambda type_: + type_ in types, + enum_types) + for type_ in enum_types: + # logger.debug("Enumerating resources of type %s", type_) + enum_names = win32api.EnumResourceNames(hsrc, type_) + if names and not "*" in names: + enum_names = filter(lambda name: + name in names, + enum_names) + for name in enum_names: + # logger.debug("Enumerating resources of type %s name %s", type_, name) + enum_languages = win32api.EnumResourceLanguages(hsrc, + type_, + name) + if languages and not "*" in languages: + enum_languages = filter(lambda language: + language in languages, + enum_languages) + for language in enum_languages: + data = win32api.LoadResource(hsrc, type_, name, language) + if not type_ in res: + res[type_] = {} + if not name in res[type_]: + res[type_][name] = {} + res[type_][name][language] = data + except pywintypes.error as exception: + if exception.args[0] in (ERROR_RESOURCE_DATA_NOT_FOUND, + ERROR_RESOURCE_TYPE_NOT_FOUND, + ERROR_RESOURCE_NAME_NOT_FOUND, + ERROR_RESOURCE_LANG_NOT_FOUND): + # logger.info('%s: %s', exception.args[1:3]) + pass + else: + raise exception + return res + + +def GetResources(filename, types=None, names=None, languages=None): + """ + Get resources from dll/exe file. + + types = a list of resource types to search for (None = all) + names = a list of resource names to search for (None = all) + languages = a list of resource languages to search for (None = all) + Return a dict of the form {type_: {name: {language: data}}} which + might also be empty if no matching resources were found. + """ + hsrc = win32api.LoadLibraryEx(filename, 0, LOAD_LIBRARY_AS_DATAFILE) + res = _GetResources(hsrc, types, names, languages) + win32api.FreeLibrary(hsrc) + return res + + +def UpdateResources(dstpath, data, type_, names=None, languages=None): + """ + Update or add resource data in dll/exe file dstpath. + + type_ = resource type to update + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + # Look for existing resources. + res = GetResources(dstpath, [type_], names, languages) + # add type_, names and languages not already present in existing resources + if not type_ in res and type_ != "*": + res[type_] = {} + if names: + for name in names: + if not name in res[type_] and name != "*": + res[type_][name] = [] + if languages: + for language in languages: + if not language in res[type_][name] and language != "*": + res[type_][name].append(language) + # add resource to destination, overwriting existing resources + hdst = win32api.BeginUpdateResource(dstpath, 0) + for type_ in res: + for name in res[type_]: + for language in res[type_][name]: + logger.info("Updating resource type %s name %s language %s", + type_, name, language) + win32api.UpdateResource(hdst, type_, name, data, language) + win32api.EndUpdateResource(hdst, 0) + + +def UpdateResourcesFromDataFile(dstpath, srcpath, type_, names=None, + languages=None): + """ + Update or add resource data from file srcpath in dll/exe file dstpath. + + type_ = resource type to update + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + with open(srcpath, "rb") as src: + data = src.read() + UpdateResources(dstpath, data, type_, names, languages) + + +def UpdateResourcesFromDict(dstpath, res, types=None, names=None, + languages=None): + """ + Update or add resources from resource dict in dll/exe file dstpath. + + types = a list of resource types to update (None = all) + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + if types: + types = set(types) + if names: + names = set(names) + if languages: + languages = set(languages) + for type_ in res: + if not types or type_ in types: + for name in res[type_]: + if not names or name in names: + for language in res[type_][name]: + if not languages or language in languages: + UpdateResources(dstpath, + res[type_][name][language], + type_, [name], [language]) + + +def UpdateResourcesFromResFile(dstpath, srcpath, types=None, names=None, + languages=None): + """ + Update or add resources from dll/exe file srcpath in dll/exe file dstpath. + + types = a list of resource types to update (None = all) + names = a list of resource names to update (None = all) + languages = a list of resource languages to update (None = all) + """ + res = GetResources(srcpath, types, names, languages) + UpdateResourcesFromDict(dstpath, res) diff --git a/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winutils.py b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winutils.py new file mode 100644 index 0000000000000000000000000000000000000000..d7aa5585bb761483f8589536fcf4ba31a701db2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/PyInstaller/utils/win32/winutils.py @@ -0,0 +1,171 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Utils for Windows platform. +""" + +__all__ = ['get_windows_dir'] + +import os +import sys + +from ... import compat + +import PyInstaller.log as logging +logger = logging.getLogger(__name__) + + +def get_windows_dir(): + """ + Return the Windows directory e.g. C:\\Windows. + """ + # imported here to avoid circular import + from ... import compat + windir = compat.win32api.GetWindowsDirectory() + if not windir: + raise SystemExit("Error: Can not determine your Windows directory") + return windir + + +def get_system_path(): + """ + Return the required Windows system paths. + """ + # imported here to avoid circular import + from ... import compat + _bpath = [] + sys_dir = compat.win32api.GetSystemDirectory() + # Ensure C:\Windows\system32 and C:\Windows directories are + # always present in PATH variable. + # C:\Windows\system32 is valid even for 64bit Windows. Access do DLLs are + # transparently redirected to C:\Windows\syswow64 for 64bit applactions. + # http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx + _bpath = [sys_dir, get_windows_dir()] + return _bpath + + +def extend_system_path(paths): + """ + Add new paths at the beginning of environment variable PATH. + + Some hooks might extend PATH where PyInstaller should look for dlls. + """ + # imported here to avoid circular import + from ... import compat + old_PATH = compat.getenv('PATH', '') + paths.append(old_PATH) + new_PATH = os.pathsep.join(paths) + compat.setenv('PATH', new_PATH) + + +def import_pywin32_module(module_name): + """ + Import and return the PyWin32 module with the passed name. + + When imported, the `pywintypes` and `pythoncom` modules both internally + import dynamic libraries (e.g., `pywintypes.py` imports `pywintypes34.dll` + under Python 3.4). The Anaconda Python distribution for Windows installs + these libraries to non-standard directories, resulting in + `"ImportError: No system module 'pywintypes' (pywintypes34.dll)"` + exceptions. This function catches these exceptions, searches for these + libraries, adds their directories to `sys.path`, and retries. + + Parameters + ---------- + module_name : str + Fully-qualified name of this module. + + Returns + ---------- + types.ModuleType + The desired module. + """ + module = None + + try: + module = __import__( + module_name, globals={}, locals={}, fromlist=['']) + except ImportError as exc: + if str(exc).startswith('No system module'): + # True if "sys.frozen" is currently set. + is_sys_frozen = hasattr(sys, 'frozen') + + # Current value of "sys.frozen" if any. + sys_frozen = getattr(sys, 'frozen', None) + + # Force PyWin32 to search "sys.path" for DLLs. By default, PyWin32 + # only searches "site-packages\win32\lib/", "sys.prefix", and + # Windows system directories (e.g., "C:\Windows\System32"). This is + # an ugly hack, but there is no other way. + sys.frozen = '|_|GLYH@CK' + + # If isolated to a venv, the preferred site.getsitepackages() + # function is unreliable. Fallback to searching "sys.path" instead. + if compat.is_venv: + sys_paths = sys.path + else: + sys_paths = compat.getsitepackages() + + for sys_path in sys_paths: + # Absolute path of the directory containing PyWin32 DLLs. + pywin32_dll_dir = os.path.join(sys_path, 'pywin32_system32') + if os.path.isdir(pywin32_dll_dir): + sys.path.append(pywin32_dll_dir) + try: + module = __import__( + name=module_name, globals={}, locals={}, fromlist=['']) + break + except ImportError: + pass + + # If "sys.frozen" was previously set, restore its prior value. + if is_sys_frozen: + sys.frozen = sys_frozen + # Else, undo this hack entirely. + else: + del sys.frozen + + # If this module remains unimportable, PyWin32 is not installed. Fail. + if module is None: + raise + + return module + + +def convert_dll_name_to_str(dll_name): + """ + Convert dll names from 'bytes' to 'str'. + + Latest pefile returns type 'bytes'. + :param dll_name: + :return: + """ + # imported here to avoid circular import + if isinstance(dll_name, bytes): + return str(dll_name, encoding='UTF-8') + else: + return dll_name + + +def set_exe_checksum(exe_path): + """Set executable's checksum in its metadata. + + This optional checksum is supposed to protect the executable against + corruption but some anti-viral software have taken to flagging anything + without it set correctly as malware. See issue #5579. + """ + import pefile + pe = pefile.PE(exe_path) + pe.OPTIONAL_HEADER.CheckSum = pe.generate_checksum() + pe.close() + pe.write(exe_path) diff --git a/3rdparty/pyinstaller-4.3/README.rst b/3rdparty/pyinstaller-4.3/README.rst new file mode 100644 index 0000000000000000000000000000000000000000..243c3d9697d8430317d57e92bd827f521fcd23b7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/README.rst @@ -0,0 +1,169 @@ +PyInstaller Overview +==================== + +PyInstaller bundles a Python application and all its dependencies into a single +package. The user can run the packaged app without installing a Python +interpreter or any modules. + + +**Help keeping PyInstaller alive:** +Maintaining PyInstaller is a huge amount of work. +PyInstaller development can only continue +if users and companies provide sustainable funding. See +http://www.pyinstaller.org/funding.html for how to support PyInstaller. + + +:Documentation: https://pyinstaller.readthedocs.io/ +:Website: http://www.pyinstaller.org/ +:Code: https://github.com/pyinstaller/pyinstaller +:Donate, Fund: http://www.pyinstaller.org/funding.html + + +PyInstaller reads a Python script written by you. It analyzes your code +to discover every other module and library your script needs in order to +execute. Then it collects copies of all those files -- including the active +Python interpreter! -- and puts them with your script in a single folder, or +optionally in a single executable file. + + +PyInstaller is tested against Windows, Mac OS X, and GNU/Linux. +However, it is not a cross-compiler: +to make a Windows app you run PyInstaller in Windows; to make +a GNU/Linux app you run it in GNU/Linux, etc. +PyInstaller has been used successfully +with AIX, Solaris, FreeBSD and OpenBSD, +but is not tested against them as part of the continuous integration tests. + + +Main Advantages +--------------- + +- Works out-of-the-box with any Python version 3.6-3.9. +- Fully multi-platform, and uses the OS support to load the dynamic libraries, + thus ensuring full compatibility. +- Correctly bundles the major Python packages such as numpy, PyQt5, + PySide2, Django, wxPython, matplotlib and others out-of-the-box. +- Compatible with many 3rd-party packages out-of-the-box. (All the required + tricks to make external packages work are already integrated.) +- Libraries like PyQt5, PySide2, wxPython, matplotlib or Django are fully + supported, without having to handle plugins or external data files manually. +- Works with code signing on OS X. +- Bundles MS Visual C++ DLLs on Windows. + + +Installation +------------ + +PyInstaller is available on PyPI. You can install it through `pip`:: + + pip install pyinstaller + + +Requirements and Tested Platforms +------------------------------------ + +- Python: + + - 3.6-3.9 + - tinyaes_ 1.0+ (only if using bytecode encryption). + Instead of installing tinyaes, ``pip install pyinstaller[encryption]`` instead. + +- Windows (32bit/64bit): + + - PyInstaller should work on Windows 7 or newer, but we only officially support Windows 8+. + + - We don't support Python installed from the Windows store when not using virtual environments due to + `permission errors `_ + that can't easily be fixed. + +- GNU/Linux (32bit/64bit) + + - ldd: Console application to print the shared libraries required + by each program or shared library. This typically can be found in + the distribution-package `glibc` or `libc-bin`. + - objdump: Console application to display information from + object files. This typically can be found in the + distribution-package `binutils`. + - objcopy: Console application to copy and translate object files. + This typically can be found in the distribution-package `binutils`, + too. + +- Mac OS X (64bit): + + - Mac OS X 10.13 (High Sierra) or newer. + + +Usage +----- + +Basic usage is very simple, just run it against your main script:: + + pyinstaller /path/to/yourscript.py + +For more details, see the `manual`_. + + +Untested Platforms +--------------------- + +The following platforms have been contributed and any feedback or +enhancements on these are welcome. + +- FreeBSD + + - ldd + +- Solaris + + - ldd + - objdump + +- AIX + + - AIX 6.1 or newer. PyInstaller will not work with statically + linked Python libraries. + - ldd + +- PowerPC GNU/Linux (Debian) + + +Before using any contributed platform, you need to build the PyInstaller +bootloader, as we do not ship binary packages. Download PyInstaller +source, and build the bootloader:: + + cd bootloader + python ./waf all + +Then install PyInstaller:: + + python setup.py install + +or simply use it directly from the source (pyinstaller.py). + + +Support +--------------------- + +See http://www.pyinstaller.org/support.html for how to find help as well as +for commercial support. + + +Funding +--------------------- + +Maintaining PyInstaller is a huge amount of work. +PyInstaller development can only continue +if users and companies provide sustainable funding. See +http://www.pyinstaller.org/funding.html for how to support PyInstaller. + + +Changes in this Release +------------------------- + +You can find a detailed list of changes in this release +in the `change log`_ section of the manual. + + +.. _tinyaes: https://github.com/naufraghi/tinyaes-py +.. _`manual`: https://pyinstaller.readthedocs.io/en/v4.3/ +.. _`change log`: https://pyinstaller.readthedocs.io/en/v4.3/CHANGES.html diff --git a/3rdparty/pyinstaller-4.3/bootloader/.gitattributes b/3rdparty/pyinstaller-4.3/bootloader/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..5230eb90e451bc37e04a0250549219dc5a413745 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/.gitattributes @@ -0,0 +1 @@ +waf binary diff --git a/3rdparty/pyinstaller-4.3/bootloader/Dockerfile.tmp b/3rdparty/pyinstaller-4.3/bootloader/Dockerfile.tmp new file mode 100644 index 0000000000000000000000000000000000000000..386b6b34a12edd12f45a96655ae79dab2b40b265 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/Dockerfile.tmp @@ -0,0 +1,9 @@ +FROM debian:buster + +RUN dpkg --add-architecture i386 +RUN apt update && apt upgrade --yes +RUN apt install --yes gcc-multilib lib32z1-dev python3 + +WORKDIR /pyinstaller/bootloader + +CMD python3 waf all --target-arch=64bit && python3 waf all --target-arch=32bit diff --git a/3rdparty/pyinstaller-4.3/bootloader/README.txt b/3rdparty/pyinstaller-4.3/bootloader/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..82e314a020d0416b0a778b9de3d858643152bdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/README.txt @@ -0,0 +1,36 @@ +========== +Bootloader +========== + +Bootloader bootstraps Python for the frozen application. It is written in C +and the code is very platform specific. The bootloader has to be kept +standalone without any dependencies on 3rd party libraries. + +Directory Structure +=============================== + +* src + Bootloader source code common for all platforms. +* windows + Code specific to Windows. +* zlib + Library to unzip Python modules. This library is included in bootloader + for Windows. On other platforms the bootloader uses zlib library from the + system. +* images + PyInstaller icons for Windows bootloaders and the .app bundle on Mac OS X. + +Build instructions +=============================== + +In short:: + + ./waf all + +or for building a Linux Standard Base (LSB) compliant bootloader:: + + ./waf --lsb all + +For more details, esp. about building for other target-platforms, please read + (resp. +the corresponsing file in the source: `../doc/bootloader-building.rst`). diff --git a/3rdparty/pyinstaller-4.3/bootloader/Vagrantfile b/3rdparty/pyinstaller-4.3/bootloader/Vagrantfile new file mode 100644 index 0000000000000000000000000000000000000000..fe25f19aebaa3f1aee7bd4a5187f132bb2d167c7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/Vagrantfile @@ -0,0 +1,443 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : +# +# Automated rebuild of bootloader on misc. platforms +# +# Copyright (C) 2016-2021 PyInstaller Development Team +# +# Based on a Vagrantfile by Thomas Waldmann. Thanks! +# Copyright (C) 2015 The Borg Collective http://borgbackup.readthedocs.org/ + + +Vagrant.require_version ">= 1.9.5" # Enabling shared folders for winssh + +# half-official Debian 8, incl. vboxfs kernel module for shared folders +DEBIAN = "debian/contrib-buster64" + +if not (Dir.exist?('./src') and File.exist?('./wscript')) + abort("vagrant must be called from within the 'bootloader' directory") +end + +# Test if a variable is `true`, like a shell-variable: Unset (nil), empty +# string or some value expressing `false` means `false`. +def is_set?(s) + not (s == nil or s == "" or (s =~ /^(false|f|n|no|0)$/i)) +end + +# Test if the environment variable `name` is set to `value` (case-insensitive) +def is_env?(name, value) + ENV[name] and ENV[name].downcase == value.downcase +end + + +def packages_debianoid + # This always also installs the software for cross-building for OS X and + # Windows. Differentiating here is most probably not worth the effort. + return <<-EOF + dpkg --add-architecture i386 + apt-get update + # avoid any prompts, from http://askubuntu.com/questions/146921 + export DEBIAN_FRONTEND=noninteractive + apt-get -y -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" upgrade + apt-get install -y python3 python3-dev python3-setuptools \ + gcc libz-dev libz-dev:i386 \ + gcc-i686-linux-gnu \ + gcc-mingw-w64 clang + EOF +end + +def packages_osxcross_debianoid + # Install packages required for building osxcross + return <<-EOF + apt-get update + # avoid any prompts, from http://askubuntu.com/questions/146921 + export DEBIAN_FRONTEND=noninteractive + apt-get -y -o Dpkg::Options::="--force-confdef" \ + -o Dpkg::Options::="--force-confold" upgrade + # these are typically installed already on a GNU/Linux system + apt-get install -y cmake make sed kmod + apt-get install -y git fuse clang + # requirements for darling-dmg (not listed in tools/get_dependencies.sh) + apt-get install -y libxml2-dev libicu-dev libssl-dev libbz2-dev libfuse-dev + EOF +end + +def prepare_osxcross_debianiod + # Download osxcross source and install packages defined there + return <<-EOF + git clone --depth 1 https://github.com/tpoechtrager/osxcross.git + # install requirements for cctools + sudo osxcross/tools/get_dependencies.sh + EOF +end + +def build_osxcross + return <<-EOF + sudo modprobe fuse + PATH=$PATH:/sbin:/usr/sbin + SDK=/vagrant/bootloader/_sdks/osx + cd ~vagrant/osxcross + mkdir -p $SDK + # Build darling-dmg, mount the ``.dmg`` image file via fuse and extract + # the SDK into MacOSX*.tar.* + ./tools/gen_sdk_package_darling_dmg.sh $SDK/Xcode_7.3.1.dmg || exit 1 + mv MacOSX*.tar.* tarballs/ + # Build the build-tools into directory `target/` + UNATTENDED=1 ./build.sh + + cd ~vagrant/osxcross/target + echo "Minimizing the SDK to include only what is required for building" + echo "PyInstaller" + rm -rf libexec/as/{arm,ppc,ppc64} # platforms not supported by pyinstaller + rm -rf SDK/tools # darling-dmg, not used for building the bootloader + cd SDK/MacOSX*.sdk + rm -rf usr/lib/{php,dtrace} usr/share usr/bin + cd System/Library/ + mv Frameworks Frameworks.off + mkdir Frameworks + mv Frameworks.off/{ApplicationServices,CoreGraphics,ImageIO,Carbon,CoreServices,IOKit,CFNetwork,CoreText,IOSurface,CoreFoundation,DiskArbitration,Security}.framework Frameworks + rm -rf Frameworks.off CoreServices Printers PrivateFrameworks + cd ~vagrant/osxcross + + # Create a tar-ball including the SDK and the build-tools + echo "Packaging SDK and cctools - this may take a while" + tar -C target --xz -cf $SDK/osxcross.tar.xz . + EOF +end + + +def update_darwin + return <<-EOF + set +x + softwareupdate --ignore iTunesX + softwareupdate --ignore iTunes + softwareupdate --install --all + EOF +end + +def packages_darwin + return <<-EOF + ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" + export HOMEBREW_NO_ANALYTICS=1 + brew analytics off + brew update + brew upgrade --all + brew install pkg-config + touch ~vagrant/.bash_profile ; chown vagrant ~vagrant/.bash_profile + EOF +end + +# Install required cygwin packages and configure environment +# +# Microsoft/EdgeOnWindows10 image has MLS-OpenSSH installed by default, +# which is based on cygwin x86_64 but should not be used together with cygwin. +# In order to have have cygwin compatible bash 'ImagePath' is replaced with +# cygrunsrv of newly installed cygwin +# +# supported cygwin versions: +# x86_64 +# x86 +def packages_cygwin(version) + setup_exe = "setup-#{version}.exe" + + return <<-EOF + mkdir -p /cygdrive/c/cygwin + # Download setup.exe + powershell -Command '(New-Object System.Net.WebClient).DownloadFile("https://www.cygwin.com/#{setup_exe}","C:\\cygwin\\#{setup_exe}")' + + # Create a .bat file performing the installation + echo ' + REM --- Change to use different CygWin platform and final install path + set CYGSETUP=#{setup_exe} + REM --- Install build version of CygWin in a subfolder + set OURPATH=%cd% + set CYGBUILD="C:\\cygwin\\CygWin" + set CYGMIRROR=http://mirrors.kernel.org/sourceware/cygwin/ + set BASEPKGS=openssh,rsync + set BUILDPKGS=p7zip + REM set BUILDPKGS=python3,python3-setuptools,python3-devel,binutils,gcc-core + %CYGSETUP% -q -B -o -n -R %CYGBUILD% -L -D -s %CYGMIRROR% -P %BASEPKGS%,%BUILDPKGS% + cd /d C:\\cygwin\\CygWin\\bin + regtool set /HKLM/SYSTEM/CurrentControlSet/Services/OpenSSHd/ImagePath "C:\\cygwin\\CygWin\\bin\\cygrunsrv.exe" + bash -c "ssh-host-config --no" + bash -c "chown sshd_server /cygdrive/c/cygwin/CygWin/var/empty" + ' > /cygdrive/c/cygwin/install.bat + + # Prepare and source our profile + echo "alias mkdir='mkdir -p'" > ~/.profile + echo "export CYGWIN_ROOT=/cygdrive/c/cygwin/CygWin" >> ~/.profile + echo 'export PATH=$CYGWIN_ROOT/bin:$PATH' >> ~/.profile + + echo '' > ~/.bash_profile + + cmd.exe /c 'setx /m PATH "C:\\cygwin\\CygWin\\bin;%PATH%"' + source ~/.profile + + cd /cygdrive/c/cygwin && cmd.exe /c install.bat + + echo 'db_home: windows' > $CYGWIN_ROOT/etc/nsswitch.conf + EOF +end + +def packages_chocolatey() + return <<-EOF + powershell " \ + Set-ExecutionPolicy Bypass -Scope Process -Force; \ + [System.Net.ServicePointManager]::SecurityProtocol = \ + [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; \ + iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" + EOF +end + +def choco_install(packages) + # Using the full path for `choco` here saves the need to reload the machine + # to get $PATH set. + return <<-EOF + echo > install-choco.ps1 ' + $env:Path += ";$($env:ProgramData)/Chocolatey/bin" + choco install -y #{packages} + ' ; powershell -NoProfile -ExecutionPolicy Bypass -File ./install-choco.ps1 + EOF +end + +def windows_disable_updates() + # TODO: Add more entries to really disable all auto-updates and reboots. + # Also disable Cortana + return <<-EOF + echo > no-updates.ps1 ' + $base = "HKLM:/Software/Policies/Microsoft/Windows/" + New-Item -Force -Path "$base/WindowsUpdate" | Out-Null + New-Item -Force -Path "$base/WindowsUpdate/AU" | Out-Null + New-ItemProperty -Path "$base/WindowsUpdate/AU" -Name "NoAutoUpdate" -Value 1 -PropertyType DWORD -Force | Out-Null + Get-ItemProperty "$base/WindowsUpdate/AU" + New-Item -Force -Path "$base/Windows Search" | Out-Null + New-ItemProperty -Path "$base/Windows Search" -Name "AllowCortana" -Value 0 -PropertyType DWORD -Force | Out-Null + Get-ItemProperty "$base/Windows Search" + ' ; powershell.exe -NoProfile -ExecutionPolicy Bypass -File ./no-updates.ps1 + EOF +end + +def build_bootloader(boxname) + return <<-EOF + set -x + for d in /vagrant /cygdrive/c/vagrant ; do + if [ -d "$d/bootloader" ] ; then + cd "$d"/bootloader + break + fi + done + for d in /cygdrive/c $HOME ; do + if [ -d "$d/mingw64" ] ; then + export PATH="$d/mingw64/bin:$d/mingw64/opt/bin:$PATH" + break + fi + done + python3 ./waf all + python3 ./waf all CC=i686-linux-gnu-gcc + EOF +end + +def build_bootloader_on_windows(boxname) + return <<-EOF + cd c:/vagrant/bootloader + py -3 ./waf all --target-arch=64bit + py -3 ./waf all --target-arch=32bit + EOF +end + +def build_bootloader_target_win32(boxname) + return <<-EOF + cd /vagrant/bootloader + py -3 ./waf all CC=i686-w64-mingw32-gcc + py -3 ./waf all CC=x86_64-w64-mingw32-gcc + EOF +end + +def build_bootloader_target_osx(boxname) + return <<-EOF + cd /vagrant/bootloader + # Unpack the SDK and the build-tools (cctools) + if [ ! -d ~/osxcross ] ; then + mkdir -p ~/osxcross + tar -C ~/osxcross --xz -xf /vagrant/bootloader/_sdks/osx/osxcross.tar.xz + fi + PATH=~/osxcross/bin:$PATH + echo $PATH + export LD_LIBRARY_PATH=~/osxcross/lib:$LD_LIBRARY_PATH + export MACOSX_DEPLOYMENT_TARGET=10.11 + python3 ./waf all CC=x86_64-apple-darwin15-clang --clang + #python3 ./waf all CC=i386-apple-darwin15-clang + EOF +end + +Vagrant.configure(2) do |config| + # Do not leak information about usage by by update-checks. + config.vm.box_check_update = false + + # Let the VM access the dirstribution folder (the parent directory) on the + # host machine via the default shared folder. We need to use the parent + # directory since for building the windows bootloader we need to access some + # image files in ../PyInstaller/bootloader/images/. + config.vm.synced_folder "..", "/vagrant" + + config.vm.provider :virtualbox do |v| + v.gui = is_set?(ENV['GUI']) + v.cpus = 1 + end + + #--- Linux 64 bit, using Debian 8 from boxcutter --- + config.vm.define "linux64" do |b| + b.vm.box = DEBIAN + b.vm.provider :virtualbox do |v| + v.memory = 768 + end + b.vm.provision "packages debianoid", :type => :shell, :inline => packages_debianoid + + if is_env?("TARGET", "WINDOWS") + b.vm.provision "build bootloader for windows", + :type => :shell, :privileged => false, :keep_color => true, + :inline => build_bootloader_target_win32("linux64") + elsif is_env?("TARGET", "OSX") + if (not File.exist?("_sdks/osx/osxcross.tar.xz")) + abort("\nERROR: OS X cross SDK is missing. Please run once the "\ + "`build-osxcross` machine.\n\n") + end + b.vm.provision "build bootloader for osx", + :type => :shell, :privileged => false, :keep_color => true, + :inline => build_bootloader_target_osx("linux64") + else + b.vm.provision "build bootloader", + :type => :shell, :privileged => false, :keep_color => true, + :inline => build_bootloader("linux64") + end + end + + #--- Build the Darwin SDK and tools --- + config.vm.define "build-osxcross" do |b| + b.vm.box = DEBIAN + b.vm.provider :virtualbox do |v| + v.memory = 768 + end + b.vm.provision "packages osxcross debianoid", :type => :shell, + :inline => packages_osxcross_debianoid, + :keep_color => true + b.vm.provision "prepare osxcross debianoid", :type => :shell, + :inline => prepare_osxcross_debianiod, + :keep_color => true + # Reload for the case the kernel changed, thus fuse will be loaded + b.vm.provision :reload + b.vm.provision "build osxcross", :type => :shell, + :inline => build_osxcross, + :keep_color => true + end + + #--- Mac OS X --- + config.vm.define "darwin64" do |b| + b.vm.box = "jhcook/yosemite-clitools" + + # Guest additions are not available for OS X and thus no shared folders + b.vm.synced_folder "..", "/vagrant", :type => "rsync", + # Avoid rsyncing the huge SDKs not used here + :rsync__args => ["--verbose", "--archive", "--delete", "-z", + "--include=bootloader", "--include=PyInstaller", + "--exclude=/*", "--cvs-exclude", "--exclude=*.pyc", + "--exclude=bootloader/build", + "--exclude=bootloader/_sdks", + "--exclude=bootloader/.lock-*", + "--exclude=bootloader/.waf-*", + "--exclude=bootloader/waf-*"], + # chown fails on the OS X box, since group 'vagrant' is missing + :rsync__chown => false + + b.vm.provider :virtualbox do |v| + v.customize ['modifyvm', :id, '--ostype', 'MacOS1010_64'] + v.customize ['modifyvm', :id, '--paravirtprovider', 'default'] + # Adjust CPU settings according to + # https://github.com/geerlingguy/macos-virtualbox-vm + v.customize ['modifyvm', :id, '--cpuidset', + # Leaf EAX EBX ECX EDX + '00000001', '000306a9', '00020800', '80000201', '178bfbff'] + # This should make make the box less choppy (source: geerlingguy) + #v.customize ["modifyvm", :id, '--audio', 'off'] + # Disable USB variant requiring Virtualbox proprietary extension packs + v.customize ["modifyvm", :id, '--usbehci', 'off', '--usbxhci', 'off'] + # Apple recommends 128MB (source: geerlingguy) + #v.customize ["modifyvm", :id, '--videocapmaxsize', '128MB'] + end + b.vm.provision "fix permissions", :type => :shell, :privileged => true, + :inline => "chown -R vagrant: /vagrant" + ## install (security and other) updates + b.vm.provision "update software", :type => :shell, :privileged => true, + :inline => update_darwin + #b.vm.provision :reload # for the case updates require reboot + # For this box we do not need any additional packages + #b.vm.provision "packages darwin", :type => :shell, :privileged => false, + # :inline => packages_darwin + b.vm.provision "build bootloader", :type => :shell, :privileged => false, + :inline => build_bootloader("darwin64"), + :keep_color => true + end + + #--- Windows 64 bit + #- This box requires interaction, automated build is not possible at + #- the moment. Please see the README for more information. + config.vm.define "windows10" do |b| + b.vm.box = "Microsoft/EdgeOnWindows10" + b.vm.guest = :windows + b.vm.boot_timeout = 180 + b.vm.graceful_halt_timeout = 120 + + b.ssh.shell = "sh -l" + b.ssh.username = "IEUser" + b.ssh.password = "Passw0rd!" + b.ssh.insert_key = false + + # Disable auto-update - only partially working + b.vm.provision "disable auto-updates", :type => :shell, + :privileged => false, :inline => windows_disable_updates(), + :keep_color => true + + b.vm.provider :virtualbox do |v| + v.memory = 2048 + end + + if is_set?(ENV['MINGW']) + #-- Build using MinGW-64 in cygwin + # Install cygwin to get rsync and 7zip + b.vm.provision "packages cygwin", :type => :shell, :privileged => false, + :inline => packages_cygwin("x86_64") + + # Reload to get into the new cygwin environment + b.vm.provision :reload + + # Install mingw-w64 into $HOME + # Note: Our wscript file currently doesn't support cross building (here: + # from cygwin to win32), so be can't use mingw coming with cygwin. + b.vm.provision "download mingw-w64 archive", :type => :file, + source: "~/Downloads/x86_64-6.2.0-release-posix-sjlj-rt_v5-rev1.7z", + destination: "Downloads/mingw-w64.7z" # will go into $HOME + b.vm.provision "install mingw-w64", :type => :shell, :privileged => false, + :inline => "7z x -o$HOME $HOME/Downloads/mingw-w64.7z" + + # Build the bootloader + b.vm.provision "build bootloader", :type => :shell, :privileged => false, + :inline => build_bootloader("windows10-64"), + :keep_color => true + + else + #-- Build using Visual C++ + b.vm.provision "install chocolatey", + :type => :shell, :privileged => false, :keep_color => true, + :inline => packages_chocolatey() + b.vm.provision "install packages", + :type => :shell, :privileged => false, :keep_color => true, + :inline => choco_install("python3 vcbuildtools") + b.vm.provision "build bootloader", + :type => :shell, :privileged => false, :keep_color => true, + :inline => build_bootloader_on_windows("windows10") + end + + end + +end diff --git a/3rdparty/pyinstaller-4.3/bootloader/build.sh b/3rdparty/pyinstaller-4.3/bootloader/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..b554f91741066f3fa595872944ec8331e21fc134 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/build.sh @@ -0,0 +1,45 @@ +#!/bin/bash +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +# ----------------------------------------------------------------------------- + +cd $(dirname "$0") + +if [ ! -r "_sdks/osx/osxcross.tar.xz" ] ; then + echo "Building the OS X SDK and cctools" + vagrant up --no-provision build-osxcross + vagrant provision build-osxcross + vagrant destroy builx-osxcross + echo +fi + +# start the build-guests +vagrant up --no-provision linux64 windows10 + +# build the bootloaders +vagrant provision linux64 # GNU/Linux bootloaders +TARGET=OSX vagrant provision linux64 # OS X bootloaders +vagrant provision windows10 # Windows bootloaders (using msvc) + +# verify the bootloaders have been built +git status ../PyInstaller/bootloader/ + +read -n 1 -p "Destroy or shutdown machines? (D/s/n) " REPLY +echo +REPLY=${REPLY^^*} +if [ "$REPLY" = "D" ] ; then + echo + vagrant destroy -f linux64 windows10 +elif [ "${REPLY,,*}" = "s" ] ; then + echo + vagrant halt linux64 windows10 +elif [ "${REPLY,,*}" != "n" ] ; then + echo "Invalid answer. You may halt the machines manually using 'vagrant halt linux64 windows10'" +fi diff --git a/3rdparty/pyinstaller-4.3/bootloader/macos/Dockerfile b/3rdparty/pyinstaller-4.3/bootloader/macos/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..ef766b59da7a1a7ed5afd15923f49f13930060aa --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/macos/Dockerfile @@ -0,0 +1,59 @@ +######################################################################## +# Build the cross-compile toolchain # +######################################################################## +FROM fedora:33 AS builder + +# Install packages required by osxtools +RUN dnf install -y \ + git \ + cmake \ + clang \ + llvm-devel \ + libxml2-devel \ + libuuid-devel \ + openssl-devel \ + bash \ + patch \ + libstdc++-static \ + make \ + xz \ + cpio \ + bzip2-devel + +# Check out the osxcross repo +WORKDIR /tmp/osxcross-build +RUN git clone --depth 1 https://github.com/rokm/osxcross.git -b xcode_cmdline_tools . + +# Extract MacOSX 11.x SDK from Command Line Tools for Xcode +COPY ./_sdks/osx/Command_Line_Tools_for_Xcode.dmg /tmp/tools.dmg +RUN ./tools/gen_sdk_package_tools_dmg.sh /tmp/tools.dmg && mv MacOSX11*.tar.* tarballs/ + +# Build the toolchain +ENV UNATTENDED=1 +RUN ./build.sh + + +######################################################################## +# The actual compiler VM # +######################################################################## +FROM fedora:33 + +# Install packages +RUN dnf install -y python3 clang + +# Copy cross-compilation toolchain from the builder container +COPY --from=builder /tmp/osxcross-build/target /opt/osxcross + +# Set paths +ENV PATH=/opt/osxcross/bin:$PATH +ENV LD_LIBRARY_PATH=/opt/osxcross/lib:$LD_LIBRARY_PATH + +# Set macOS deployment target +ENV MACOSX_DEPLOYMENT_TARGET=10.11 + +# Build the bootloader +# The PyInstaller top source directory must be mounted as a volume to +# /pyinstaller when image is run, e.g.: +# docker run -v "/path/to/pyinstaller:/pyinstaller" +WORKDIR /pyinstaller/bootloader +CMD CC=$(basename -a /opt/osxcross/bin/x86_64-*-clang) python3 ./waf configure all --clang diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/main.c b/3rdparty/pyinstaller-4.3/bootloader/src/main.c new file mode 100644 index 0000000000000000000000000000000000000000..34c4041d26924c824330e0eb392a030facc28452 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/main.c @@ -0,0 +1,111 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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 file has a total of three different entry points, with one of them chosen + * using preprocessor defines. + * + * wWinMain: For Windows with console=False + * wmain: For Windows with console=True + * main: For OS X and Linux + */ + +#ifdef _WIN32 + #include + #include + + /* Prevent the MS CRT from expanding wildcards in command-line arguments. */ + int _CRT_glob = 0; +#endif + +#include +#include +#include +#include + +#include "pyi_main.h" +#include "pyi_global.h" +#include "pyi_win32_utils.h" + +#ifdef __FreeBSD__ + #include +#endif + +#if defined(_WIN32) + #define MS_WINDOWS +#endif + +#if defined(_WIN32) + + #if defined(WINDOWED) + +/* Entry point for Windows when console=False */ + +int WINAPI +wWinMain( + HINSTANCE hInstance, /* handle to current instance */ + HINSTANCE hPrevInstance, /* handle to previous instance */ + LPWSTR lpCmdLine, /* pointer to command line */ + int nCmdShow /* show state of window */ + ) +{ + /* store wargv in argv as UTF-8 - decode later when used. */ + char ** u8argv = pyi_win32_argv_to_utf8(__argc, __wargv); + + return pyi_main(__argc, u8argv); +} + + #else /* defined(WINDOWED) */ + +/* Entry point for Windows when console=True */ + +int +wmain(int argc, wchar_t* argv[]) +{ + char ** u8argv = pyi_win32_argv_to_utf8(__argc, __wargv); + + return pyi_main(argc, u8argv); +} + + #endif /* defined(WINDOWED) */ + +#else /* defined(_WIN32) */ + +/* Based on main() from Modules/python.c + * + * Entry point for Linux/OS X + */ + +int +main(int argc, char **argv) +{ + int res; + + #ifdef __FreeBSD__ + fp_except_t m; + #endif + + /* 754 requires that FP exceptions run in "no stop" mode by default, + * and until C vendors implement C99's ways to control FP exceptions, + * Python requires non-stop mode. Alas, some platforms enable FP + * exceptions by default. Here we disable them. + */ + #ifdef __FreeBSD__ + m = fpgetmask(); + fpsetmask(m & ~FP_X_OFL); + #endif + + res = pyi_main(argc, argv); + return res; +} + +#endif /* defined(WIN32) */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/mkdtemp.h b/3rdparty/pyinstaller-4.3/bootloader/src/mkdtemp.h new file mode 100644 index 0000000000000000000000000000000000000000..fc5bc96332fc032110986c1bc4806946ef64cf0d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/mkdtemp.h @@ -0,0 +1,35 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * On some platforms (e.g. Solaris, AIX) mkdtemp is not available. + */ +#ifndef __MKDTEMP__ +#define __MKDTEMP__ + +#include + +static char* +mkdtemp(char *template) +{ + if (!mktemp(template) ) { + return NULL; + } + + if (mkdir(template, 0700) ) { + return NULL; + } + return template; +} + +#endif /* ifndef __MKDTEMP__ */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_archive.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_archive.c new file mode 100644 index 0000000000000000000000000000000000000000..8538981e6c430bf00991a2cc0fb8ddb24dedbefe --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_archive.c @@ -0,0 +1,664 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Fuctions related to PyInstaller archive embedded in executable. + */ + +#ifdef _WIN32 + #if BYTE_ORDER == LITTLE_ENDIAN + #if defined(_MSC_VER) + #include + #define pyi_be32toh(x) _byteswap_ulong(x) + #elif defined(__GNUC__) || defined(__clang__) + #define pyi_be32toh(x) __builtin_bswap32(x) + #else + #error Unsupported compiler + #endif + #elif BYTE_ORDER == BIG_ENDIAN + #define pyi_be32toh(x) (x) + #else + #error Unsupported byte order + #endif +#else + #ifdef __FreeBSD__ +/* freebsd issue #188316 */ + #include /* ntohl */ + #else + #include /* ntohl */ + #endif + #define pyi_be32toh(x) ntohl(x) + #include /* malloc */ + #include /* strncmp, strcpy, strcat */ + #include /* fchmod */ +#endif /* ifdef _WIN32 */ +#include /* ptrdiff_t */ +#include + +/* PyInstaller headers. */ +#include "zlib.h" +#include "pyi_global.h" +#include "pyi_path.h" +#include "pyi_archive.h" +#include "pyi_utils.h" +#include "pyi_python.h" + +int pyvers = 0; + +/* + * Return pointer to next toc entry. + */ +TOC * +pyi_arch_increment_toc_ptr(const ARCHIVE_STATUS *status, const TOC* ptoc) +{ + TOC *result = (TOC*)((char *)ptoc + ptoc->structlen); + + if (result < status->tocbuff) { + FATALERROR("Cannot read Table of Contents.\n"); + return status->tocend; + } + return result; +} + +/* + * Open archive file if needed + */ +static int +pyi_arch_open_fp(ARCHIVE_STATUS *status) +{ + if (status->fp == NULL) { + status->fp = pyi_path_fopen(status->archivename, "rb"); + + if (status->fp == NULL) { + return -1; + } + } + return 0; +} + +/* + * Close archive file + * File should close after unused to avoid locking + */ +static void +pyi_arch_close_fp(ARCHIVE_STATUS *status) +{ + if (status->fp != NULL) { + pyi_path_fclose(status->fp); + status->fp = NULL; + } +} + +/* + * Helper for pyi_arch_extract/pyi_arch_extract2fs that extracts a + * compressed file from the archive, and writes it into the provided + * file handle or data buffer. Exactly one of out_fp or out_ptr needs + * to be valid. + */ +static int +_pyi_arch_extract_compressed(ARCHIVE_STATUS *status, TOC *ptoc, FILE *out_fp, unsigned char *out_ptr) +{ + const size_t CHUNK_SIZE = 8192; + unsigned char *buffer_in = NULL; + unsigned char *buffer_out = NULL; + uint64_t remaining_size; + z_stream zstream; + int rc = -1; + + /* Allocate and initialize inflate state */ + zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + zstream.avail_in = 0; + zstream.next_in = Z_NULL; + rc = inflateInit(&zstream); + if (rc != Z_OK) { + FATALERROR("Failed to extract %s: inflateInit() failed with return code %d!\n", ptoc->name, rc); + return -1; + } + + /* Allocate I/O buffers */ + buffer_in = (unsigned char *)malloc(CHUNK_SIZE); + if (buffer_in == NULL) { + FATAL_PERROR("malloc", "Failed to extract %s: failed to allocate temporary input buffer!\n", ptoc->name); + goto cleanup; + } + buffer_out = (unsigned char *)malloc(CHUNK_SIZE); + if (buffer_out == NULL) { + FATAL_PERROR("malloc", "Failed to extract %s: failed to allocate temporary output buffer!\n", ptoc->name); + goto cleanup; + } + + /* Decompress until deflate stream ends or end of file is reached */ + remaining_size = ptoc->len; + do { + /* Read chunk to input buffer */ + size_t chunk_size = (CHUNK_SIZE < remaining_size) ? CHUNK_SIZE : (size_t)remaining_size; + if (fread(buffer_in, 1, chunk_size, status->fp) != chunk_size || ferror(status->fp)) { + rc = -1; + goto cleanup; + } + remaining_size -= chunk_size; + + /* Run inflate() on input until output buffer is not full. */ + zstream.avail_in = (uInt)chunk_size; + zstream.next_in = buffer_in; + do { + size_t out_len; + zstream.avail_out = (uInt)CHUNK_SIZE; + zstream.next_out = buffer_out; + rc = inflate(&zstream, Z_NO_FLUSH); + switch (rc) { + case Z_NEED_DICT: + rc = Z_DATA_ERROR; /* and fall through */ + case Z_DATA_ERROR: + case Z_MEM_ERROR: + case Z_STREAM_ERROR: + goto decompress_end; + } + /* Copy the extracted data */ + out_len = CHUNK_SIZE - zstream.avail_out; + if (out_fp) { + /* Write to output file */ + if (fwrite(buffer_out, 1, out_len, out_fp) != out_len || ferror(out_fp)) { + rc = Z_ERRNO; + goto decompress_end; + } + } else if (out_ptr) { + /* Copy to output data buffer */ + memcpy(out_ptr, buffer_out, out_len); + out_ptr += out_len; + } + } while (zstream.avail_out == 0); + /* Done when inflate() says it's done */ + } while (rc != Z_STREAM_END && remaining_size > 0); + +decompress_end: + if (rc == Z_STREAM_END) { + rc = 0; /* Success */ + } else { + FATALERROR("Failed to extract %s: decompression resulted in return code %d!\n", ptoc->name, rc); + rc = -1; + } + +cleanup: + inflateEnd(&zstream); + free(buffer_in); + free(buffer_out); + + return rc; +} + +/* + * Helper for pyi_arch_extract2fs that extracts an uncompressed file from + * the archive into the provided file handle. + */ +static int +_pyi_arch_extract2fs_uncompressed(ARCHIVE_STATUS *status, TOC *ptoc, FILE *out) +{ + const size_t CHUNK_SIZE = 8192; + unsigned char *buffer; + uint64_t remaining_size; + int rc = 0; + + /* Allocate temporary buffer for a single chunk */ + buffer = (unsigned char *)malloc(CHUNK_SIZE); + if (buffer == NULL) { + FATAL_PERROR("malloc", "Failed to extract %s: failed to allocate temporary buffer!\n", ptoc->name); + return -1; + } + + /* ... and copy it, chunk by chunk */ + remaining_size = ptoc->ulen; + while (remaining_size > 0) { + size_t chunk_size = (CHUNK_SIZE < remaining_size) ? CHUNK_SIZE : (size_t)remaining_size; + if (fread(buffer, chunk_size, 1, status->fp) < 1) { + FATAL_PERROR("fread", "Failed to extract %s: failed to read data chunk!\n", ptoc->name); + rc = -1; + break; + } + if (fwrite(buffer, chunk_size, 1, out) < 1) { + FATAL_PERROR("fwrite", "Failed to extract %s: failed to write data chunk!\n", ptoc->name); + rc = -1; + break; + } + remaining_size -= chunk_size; + } + free(buffer); + return rc; +} + +/* + * Helper for pyi_arch_extract that extracts an uncompressed file from + * the archive into the provided (pre-allocated) buffer. + */ +static int +_pyi_arch_extract_uncompressed(ARCHIVE_STATUS *status, TOC *ptoc, unsigned char *out) +{ + const size_t CHUNK_SIZE = 8192; + unsigned char *buffer; + uint64_t remaining_size; + + /* Read the file into buffer, chunk by chunk */ + buffer = out; + remaining_size = ptoc->ulen; + while (remaining_size > 0) { + size_t chunk_size = (CHUNK_SIZE < remaining_size) ? CHUNK_SIZE : (size_t)remaining_size; + if (fread(buffer, chunk_size, 1, status->fp) < 1) { + FATAL_PERROR("fread", "Failed to extract %s: failed to read data chunk!\n", ptoc->name); + return -1; + } + remaining_size -= chunk_size; + buffer += chunk_size; + } + return 0; +} + +/* + * Extract an archive entry into data buffer. + * Returns pointer to the data (must be freed). + */ +unsigned char * +pyi_arch_extract(ARCHIVE_STATUS *status, TOC *ptoc) +{ + unsigned char *data = NULL; + int rc = 0; + + /* Open archive (source) file... */ + if (pyi_arch_open_fp(status) != 0) { + FATALERROR("Failed to extract %s: failed to open archive file!\n", ptoc->name); + return NULL; + } + /* ... and seek to the beginning of entry's data */ + if (pyi_fseek(status->fp, status->pkgstart + ptoc->pos, SEEK_SET) < 0) { + FATAL_PERROR("fseek", "Failed to extract %s: failed to seek to the entry's data!\n", ptoc->name); + return NULL; + } + + /* Allocate the data buffer */ + data = (unsigned char *)malloc(ptoc->ulen); + if (data == NULL) { + FATAL_PERROR("malloc", "Failed to extract %s: failed to allocate data buffer (%u bytes)!\n", ptoc->name, ptoc->ulen); + goto cleanup; + } + + /* Extract */ + if (ptoc->cflag == '\1') { + rc = _pyi_arch_extract_compressed(status, ptoc, NULL, data); + } else { + rc = _pyi_arch_extract_uncompressed(status, ptoc, data); + } + if (rc != 0) { + free(data); + data = NULL; + } + +cleanup: + pyi_arch_close_fp(status); + + return data; +} + +/* + * Extract an archive entry into file on the filesystem. + * The path is relative to the directory the archive is in. + */ +int +pyi_arch_extract2fs(ARCHIVE_STATUS *status, TOC *ptoc) +{ + FILE *out = NULL; + int rc = 0; + + /* Ensure that tmp dir _MEIPASSxxx exists... */ + if (pyi_create_temp_path(status) == -1) { + return -1; + } + /* ... and open target file */ + out = pyi_open_target(status->temppath, ptoc->name); + if (out == NULL) { + FATAL_PERROR("fopen", "Failed to extract %s: failed to open target file!\n", ptoc->name); + return -1; + } + + /* Open archive (source) file... */ + if (pyi_arch_open_fp(status) != 0) { + FATALERROR("Failed to extract %s: failed to open archive file!\n", ptoc->name); + rc = -1; + goto cleanup; + } + /* ... and seek to the beginning of entry's data */ + if (pyi_fseek(status->fp, status->pkgstart + ptoc->pos, SEEK_SET) < 0) { + FATAL_PERROR("fseek", "Failed to extract %s: failed to seek to the entry's data!\n", ptoc->name); + rc = -1; + goto cleanup; + } + + /* Extract */ + if (ptoc->cflag == '\1') { + rc = _pyi_arch_extract_compressed(status, ptoc, out, NULL); + } else { + rc = _pyi_arch_extract2fs_uncompressed(status, ptoc, out); + } +#ifndef WIN32 + fchmod(fileno(out), S_IRUSR | S_IWUSR | S_IXUSR); +#endif + +cleanup: + pyi_arch_close_fp(status); + fclose(out); + + return rc; +} + +/* + * Perform full back-to-front scan of the file to search for the + * MAGIC pattern of the embedded archive's COOKIE header. + * + * Returns offset within the file if MAGIC pattern is found, 0 otherwise. + */ +static uint64_t +_pyi_find_cookie_offset(FILE *fp) +{ + static const unsigned char MAGIC[] = { 'M', 'E', 'I', 014, 013, 012, 013, 016 }; + static const int SEARCH_CHUNK_SIZE = 8192; + unsigned char *buffer = NULL; + uint64_t start_pos, end_pos; + uint64_t offset = 0; /* return value */ + + /* Allocate the read buffer */ + buffer = malloc(SEARCH_CHUNK_SIZE); + if (!buffer) { + VS("LOADER: failed to allocate read buffer (%d bytes)!\n", SEARCH_CHUNK_SIZE); + goto cleanup; + } + + /* Determine file size */ + if (pyi_fseek(fp, 0, SEEK_END) < 0) { + VS("LOADER: failed to seek to the end of the file!\n"); + goto cleanup; + } + end_pos = pyi_ftell(fp); + + /* Sanity check */ + if (end_pos < sizeof(MAGIC)) { + VS("LOADER: file is too short!\n"); + goto cleanup; + } + + /* Search the file back to front, in overlapping SEARCH_CHUNK_SIZE + * chunks. */ + do { + size_t chunk_size; + start_pos = (end_pos >= SEARCH_CHUNK_SIZE) ? (end_pos - SEARCH_CHUNK_SIZE) : 0; + chunk_size = (size_t)(end_pos - start_pos); + + /* Is the remaining chunk large enough to hold the pattern? */ + if (chunk_size < sizeof(MAGIC)) { + break; + } + + /* Read the chunk */ + if (pyi_fseek(fp, start_pos, SEEK_SET) < 0) { + VS("LOADER: failed to seek to the offset 0x%" PRIX64 "!\n", start_pos); + goto cleanup; + } + if (fread(buffer, 1, chunk_size, fp) != chunk_size) { + VS("LOADER: failed to read chunk (%zd bytes)!\n", chunk_size); + goto cleanup; + } + + /* Scan the chunk */ + for (size_t i = chunk_size - sizeof(MAGIC) + 1; i > 0; i--) { + if (memcmp(buffer + i - 1, MAGIC, sizeof(MAGIC)) == 0) { + offset = start_pos + i - 1; + goto cleanup; + } + } + + /* Adjust search location for next chunk; ensure proper overlap */ + end_pos = start_pos + sizeof(MAGIC) - 1; + } while (start_pos > 0); + +cleanup: + free(buffer); + + return offset; +} + +/* + * Fix the endianess of fields in the TOC entries. + */ +static void +_pyi_arch_fix_toc_endianess(ARCHIVE_STATUS *status) +{ + TOC *ptoc = status->tocbuff; + while (ptoc < status->tocend) { + /* Fixup the current entry */ + ptoc->structlen = pyi_be32toh(ptoc->structlen); + ptoc->pos = pyi_be32toh(ptoc->pos); + ptoc->len = pyi_be32toh(ptoc->len); + ptoc->ulen = pyi_be32toh(ptoc->ulen); + /* Jump to next entry; with the current entry fixed up, we can + * use pyi_arch_increment_toc_ptr() */ + ptoc = pyi_arch_increment_toc_ptr(status, ptoc); + } +} + +/* + * Open the archive. + * Sets f_archiveFile, f_pkgstart, f_tocbuff and f_cookie. + */ +int +pyi_arch_open(ARCHIVE_STATUS *status) +{ + uint64_t cookie_pos = 0; + VS("LOADER: archivename is %s\n", status->archivename); + + /* Physically open the file */ + if (pyi_arch_open_fp(status) != 0) { + VS("LOADER: Cannot open archive: %s\n", status->archivename); + return -1; + } + + /* Search for the embedded archive's cookie */ + cookie_pos = _pyi_find_cookie_offset(status->fp); + if (cookie_pos == 0) { + VS("LOADER: Cannot find cookie!\n"); + return -1; + } + VS("LOADER: Cookie found at offset 0x%" PRIX64 "\n", cookie_pos); + + /* Read the cookie */ + if (pyi_fseek(status->fp, cookie_pos, SEEK_SET) < 0) { + FATAL_PERROR("fseek", "failed to seek to cookie position."); + return -1; + } + if (fread(&status->cookie, sizeof(COOKIE), 1, status->fp) < 1) { + FATAL_PERROR("fread", "failed to read cookie."); + return -1; + } + /* Fix endianess of COOKIE fields */ + status->cookie.len = pyi_be32toh(status->cookie.len); + status->cookie.TOC = pyi_be32toh(status->cookie.TOC); + status->cookie.TOClen = pyi_be32toh(status->cookie.TOClen); + status->cookie.pyvers = pyi_be32toh(status->cookie.pyvers); + + /* From the cookie position and declared archive size, calculate + * the archive start position */ + status->pkgstart = cookie_pos + sizeof(COOKIE) - status->cookie.len; + + /* Set the flag that Python library was not loaded yet. */ + status->is_pylib_loaded = false; + + /* Set the the Python version used. */ + pyvers = pyi_arch_get_pyversion(status); + + /* Read in in the table of contents */ + pyi_fseek(status->fp, status->pkgstart + status->cookie.TOC, SEEK_SET); + status->tocbuff = (TOC *) malloc(status->cookie.TOClen); + + if (status->tocbuff == NULL) { + FATAL_PERROR("malloc", "Could not allocate buffer for TOC."); + return -1; + } + + if (fread(status->tocbuff, status->cookie.TOClen, 1, status->fp) < 1) { + FATAL_PERROR("fread", "Could not read from file."); + return -1; + } + status->tocend = (TOC *) (((char *)status->tocbuff) + status->cookie.TOClen); + + /* Check input file is still ok (should be). */ + if (ferror(status->fp)) { + FATALERROR("Error on file\n."); + return -1; + } + + /* Fix the endianess of the fields in the TOC entries */ + _pyi_arch_fix_toc_endianess(status); + + /* Close file handler + * if file not close here it will be close in pyi_arch_status_free */ + pyi_arch_close_fp(status); + return 0; +} + +/* Setup the archive with python modules and the paths required by rest of + * this module (this always needs to be done). + * Sets f_archivename, f_homepath, f_mainpath + */ +bool +pyi_arch_setup(ARCHIVE_STATUS *status, char const * archivePath) +{ + /* Get the archive Path */ + if (strlen(archivePath) >= PATH_MAX) { + // Should never come here, since `archivePath` was already processed + // by pyi_path_executable or pyi_path_archivefile. + return false; + } + + strcpy(status->archivename, archivePath); + /* Set homepath to where the archive is */ + pyi_path_dirname(status->homepath, archivePath); + /* + * Initial value of mainpath is homepath. It might be overriden + * by temppath if it is available. + */ + status->has_temp_directory = false; + strcpy(status->mainpath, status->homepath); + + /* Open the archive */ + if (pyi_arch_open(status)) { + /* If this is not an archive, we MUST close the file, */ + /* otherwise the open file-handle will be reused when */ + /* testing the next file. */ + pyi_arch_close_fp(status); + return false; + } + return true; +} + +/* + * external API for iterating TOCs + */ +TOC * +getFirstTocEntry(ARCHIVE_STATUS *status) +{ + return status->tocbuff; +} +TOC * +getNextTocEntry(ARCHIVE_STATUS *status, TOC *entry) +{ + TOC *rslt = (TOC*)((char *)entry + entry->structlen); + + if (rslt >= status->tocend) { + return NULL; + } + return rslt; +} + +/* + * Helpers for embedders. + */ +int +pyi_arch_get_pyversion(ARCHIVE_STATUS *status) +{ + return status->cookie.pyvers; +} + +/* + * Allocate memory for archive status. + */ +ARCHIVE_STATUS * +pyi_arch_status_new() { + ARCHIVE_STATUS *archive_status; + archive_status = (ARCHIVE_STATUS *) calloc(1, sizeof(ARCHIVE_STATUS)); + if (archive_status == NULL) { + FATAL_PERROR("calloc", "Cannot allocate memory for ARCHIVE_STATUS\n"); + } + return archive_status; +} + +/* + * Free memory allocated for archive status. + */ +void +pyi_arch_status_free(ARCHIVE_STATUS *archive_status) +{ + if (archive_status != NULL) { + VS("LOADER: Freeing archive status for %s\n", archive_status->archivename); + + /* Free the TOC memory from the archive status first. */ + if (archive_status->tocbuff != NULL) { + free(archive_status->tocbuff); + } + /* Close file handler */ + pyi_arch_close_fp(archive_status); + free(archive_status); + } +} + +/* + * Returns the value of the pyi bootloader option given by optname. Returns + * NULL if the option is not present. Returns an empty string if the option is present, + * but has no associated value. + * + * The string returned is owned by the ARCHIVE_STATUS; the caller is NOT responsible + * for freeing it. + */ +char * +pyi_arch_get_option(const ARCHIVE_STATUS * status, char * optname) +{ + /* TODO: option-cache? */ + size_t optlen; + TOC *ptoc = status->tocbuff; + + optlen = strlen(optname); + + for (; ptoc < status->tocend; ptoc = pyi_arch_increment_toc_ptr(status, ptoc)) { + if (ptoc->typcd == ARCHIVE_ITEM_RUNTIME_OPTION) { + if (0 == strncmp(ptoc->name, optname, optlen)) { + if (0 != ptoc->name[optlen]) { + /* Space separates option name from option value, so add 1. */ + return ptoc->name + optlen + 1; + } + else { + /* No option value, just return the empty string. */ + return ptoc->name + optlen; + } + + } + } + } + return NULL; +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_archive.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_archive.h new file mode 100644 index 0000000000000000000000000000000000000000..71cf81067e5061b70ebf91ff30c58930c71df3ef --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_archive.h @@ -0,0 +1,144 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Declarations related to an PyInstaller archive. + */ + +#ifndef PYI_ARCHIVE_H +#define PYI_ARCHIVE_H + +#include "pyi_global.h" +#include /* FILE */ +#include /* uint64_t */ + +/* Types of CArchive items. */ +#define ARCHIVE_ITEM_BINARY 'b' /* binary */ +#define ARCHIVE_ITEM_DEPENDENCY 'd' /* runtime option */ +#define ARCHIVE_ITEM_PYZ 'z' /* zlib (pyz) - frozen Python code */ +#define ARCHIVE_ITEM_ZIPFILE 'Z' /* zlib (pyz) - frozen Python code */ +#define ARCHIVE_ITEM_PYPACKAGE 'M' /* Python package (__init__.py) */ +#define ARCHIVE_ITEM_PYMODULE 'm' /* Python module */ +#define ARCHIVE_ITEM_PYSOURCE 's' /* Python script (v3) */ +#define ARCHIVE_ITEM_DATA 'x' /* data */ +#define ARCHIVE_ITEM_RUNTIME_OPTION 'o' /* runtime option */ + +/* TOC entry for a CArchive */ +typedef struct _toc { + int structlen; /*len of this one - including full len of name */ + uint32_t pos; /* pos rel to start of concatenation */ + uint32_t len; /* len of the data (compressed) */ + uint32_t ulen; /* len of data (uncompressed) */ + char cflag; /* is it compressed (really a byte) */ + char typcd; /* type code -'b' binary, 'z' zlib, 'm' module, + * 's' script (v3),'x' data, 'o' runtime option */ + char name[1]; /* the name to save it as */ + /* starting in v5, we stretch this out to a mult of 16 */ +} TOC; + +/* The CArchive Cookie, from end of the archive. */ +typedef struct _cookie { + char magic[8]; /* 'MEI\014\013\012\013\016' */ + uint32_t len; /* len of entire package */ + uint32_t TOC; /* pos (rel to start) of TableOfContents */ + int TOClen; /* length of TableOfContents */ + int pyvers; /* new in v4 */ + char pylibname[64]; /* Filename of Python dynamic library e.g. python2.7.dll. */ +} COOKIE; + +typedef struct _archive_status { + FILE * fp; + uint64_t pkgstart; + TOC * tocbuff; + TOC * tocend; + COOKIE cookie; + /* + * On Windows: + * These strings are UTF-8 encoded (via pyi_win32_utils_to_utf8). On Python 2, + * they are re-encoded to ANSI with ShortFileNames when passed to Python. On + * Python 3, they are decoded back to wchar_t. + * + * On Linux/OS X: + * These strings are system-provided. On Python 2, they are passed as-is to Python. + * On Python 3, they are decoded to wchar_t using Py_DecodeLocale + * (formerly called _Py_char2wchar) first. + */ + char archivename[PATH_MAX]; + char homepath[PATH_MAX]; + char temppath[PATH_MAX]; + /* + * Main path could be homepath or temppath. It will be temppath + * if temppath is available. Sometimes we do not need to know if temppath + * or homepath should be used. We only need to know the path. This variable + * is used for example to set sys.path, sys.prefix, and sys._MEIPASS. + */ + char mainpath[PATH_MAX]; + /* + * Flag if temporary directory is available. This usually means running + * executable in onefile mode. Bootloader has to behave differently + * in this mode. + */ + bool has_temp_directory; + /* + * Flag if Python library was loaded. This indicates if it is safe + * to call function PI_Py_Finalize(). If Python dll is missing + * calling this function would cause segmentation fault. + */ + bool is_pylib_loaded; + /* + * Cached command-line arguments. + */ + int argc; /* Count of command-line arguments. */ + char **argv; /* + * On Windows, UTF-8 encoded form of __wargv. + * On OS X/Linux, as received in main() + */ +} ARCHIVE_STATUS; + +TOC *pyi_arch_increment_toc_ptr(const ARCHIVE_STATUS *status, const TOC* ptoc); + +unsigned char *pyi_arch_extract(ARCHIVE_STATUS *status, TOC *ptoc); +int pyi_arch_extract2fs(ARCHIVE_STATUS *status, TOC *ptoc); + +/** + * Helpers for embedders + */ +int pyi_arch_get_pyversion(ARCHIVE_STATUS *status); +extern int pyvers; + +/** + * The gory detail level + */ +int pyi_arch_open(ARCHIVE_STATUS *status); + +/* + * Memory allocation wrappers. + */ +ARCHIVE_STATUS *pyi_arch_status_new(); +void pyi_arch_status_free(ARCHIVE_STATUS *status); + +/* + * Setup the paths and open the archive + * + * @param archivePath The path including filename to the archive. + * + * @return true on success, false otherwise. + */ +bool pyi_arch_setup(ARCHIVE_STATUS *status, char const * archivePath); + +TOC *getFirstTocEntry(ARCHIVE_STATUS *status); +TOC *getNextTocEntry(ARCHIVE_STATUS *status, TOC *entry); + +char * pyi_arch_get_option(const ARCHIVE_STATUS * status, char * optname); + +#endif /* PYI_ARCHIVE_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_global.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_global.c new file mode 100644 index 0000000000000000000000000000000000000000..6f4c610e9e529b1cfa4293eb32fb1f42f85d1022 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_global.c @@ -0,0 +1,249 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Global shared fuctions used in many bootloader files. + */ + +/* + * Enable use of Sean's Tool Box -- public domain -- http://nothings.org/stb.h. + * File stb.h. + * All functions starting with 'stb_' prefix are from this toolbox. + * + * This define has to be only in one C source file! + */ +/* #define STB_DEFINE 1/ * * / */ +/* #define STB_NO_REGISTRY 1 / * No need for Windows registry functions in stb.h. * / */ + +#include /* va_list, va_start(), va_end() */ +#include + +#ifdef _WIN32 + #include + #include + #include + #include +#else + #include + #include +#endif + +/* On Mac OS X send debug msg also to syslog for gui app in debug mode. */ +#if defined(__APPLE__) && defined(WINDOWED) && defined(LAUNCH_DEBUG) + #include +#endif + +/* PyInstaller headers. */ +#include "pyi_global.h" +#include "pyi_win32_utils.h" +/* Text length of MessageBox(). */ +#define MBTXTLEN 1024 + +/* Locale is saved at the start of main(), and restored immediately before running + * scripts in pyi_launch_run_scripts + */ +char *saved_locale; + +/* + * On Windows and with windowed mode (no console) show error messages + * in message boxes. In windowed mode nothing is written to console. + */ + +#if defined(_WIN32) && defined(WINDOWED) +void +show_message_box(const char *msg, const char *caption, UINT uType) +{ + wchar_t wmsg[MBTXTLEN]; + wchar_t wcaption[MBTXTLEN] = L""; + if (pyi_win32_utils_from_utf8(wmsg, msg, MBTXTLEN)) { + /* converting the caption is expected to pass since the given caption + * is always written in US-ASCII and hard-coded, currently. + */ + pyi_win32_utils_from_utf8(wcaption, caption, MBTXTLEN); + MessageBoxW(NULL, wmsg, wcaption, MB_OK | uType); + } + else { + /* The msg here is always shown as not human-readable string, + * but can be the hint what the real message is. + */ + MessageBoxA(NULL, msg, caption, MB_OK | uType); + } +} + + +void +mbfatalerror(const char *fmt, ...) +{ + char msg[MBTXTLEN]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, MBTXTLEN, fmt, args); + va_end(args); + + show_message_box(msg, "Fatal error detected", MB_ICONEXCLAMATION); +} + +void +mbothererror(const char *fmt, ...) +{ + char msg[MBTXTLEN]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, MBTXTLEN, fmt, args); + va_end(args); + + show_message_box(msg, "Error detected", MB_ICONWARNING); +} + + void mbfatal_winerror(const char * funcname, const char *fmt, ...) + { + char fullmsg[MBTXTLEN]; + char msg[MBTXTLEN]; + DWORD error_code = GetLastError(); + va_list args; + + va_start(args, fmt); + vsnprintf(msg, MBTXTLEN, fmt, args); + va_end(args); + snprintf(fullmsg, MBTXTLEN, "%s%s: %s", msg, funcname, GetWinErrorString(error_code)); + show_message_box(fullmsg, "Fatal error detected", MB_ICONEXCLAMATION); + } + + void mbfatal_perror(const char * funcname, const char *fmt, ...) + { + char fullmsg[MBTXTLEN]; + char msg[MBTXTLEN]; + va_list args; + + va_start(args, fmt); + vsnprintf(msg, MBTXTLEN, fmt, args); + va_end(args); + snprintf(fullmsg, MBTXTLEN, "%s%s: %s", msg, funcname, strerror(errno)); + show_message_box(fullmsg, "Fatal error detected", MB_ICONEXCLAMATION); + } +#endif /* _WIN32 and WINDOWED */ + +/* Enable or disable debug output. */ + +#ifdef LAUNCH_DEBUG + #if defined(_WIN32) && defined(WINDOWED) +void +mbvs(const char *fmt, ...) +{ + char msg[MBTXTLEN]; + va_list args; + int pid_len; + + /* Add pid to the message */ + pid_len = sprintf(msg, "[%d] ", getpid()); + + va_start(args, fmt); + vsnprintf(&msg[pid_len], MBTXTLEN-pid_len, fmt, args); + /* Ensure message is trimmed to fit the buffer. */ + /* msg[MBTXTLEN-1] = '\0'; */ + va_end(args); + + OutputDebugStringA(msg); +} + #endif /* if defined(_WIN32) && defined(WINDOWED) */ +#endif /* ifdef LAUNCH_DEBUG */ + +#define VPRINTF_TO_STDERR_BUFSIZE (MBTXTLEN * 2) +void vprintf_to_stderr(const char *fmt, va_list v) { +#if defined(_WIN32) + char utf8_buffer[VPRINTF_TO_STDERR_BUFSIZE]; + char mbcs_buffer[VPRINTF_TO_STDERR_BUFSIZE]; + + vsnprintf(utf8_buffer, VPRINTF_TO_STDERR_BUFSIZE, fmt, v); + if (pyi_win32_utf8_to_mbs(mbcs_buffer, + utf8_buffer, + VPRINTF_TO_STDERR_BUFSIZE)) { + fprintf(stderr, "%s", mbcs_buffer); + } + else { + fprintf(stderr, "%s", utf8_buffer); + } +#else + vfprintf(stderr, fmt, v); +#endif /* if defined(_WIN32) */ +} + +void printf_to_stderr(const char* fmt, ...) { + va_list v; + va_start(v, fmt); + vprintf_to_stderr(fmt, v); + va_end(v); +} + +/* + * Wrap printing debug messages to console. + */ +void +pyi_global_printf(const char *fmt, ...) +{ + va_list v; + + /* Sent 'LOADER text' messages to stderr. */ + fprintf(stderr, "[%d] ", getpid()); + va_start(v, fmt); + vprintf_to_stderr(fmt, v); + va_end(v); + /* For Gui apps on Mac OS X send debug messages also to syslog. */ + /* This allows to see bootloader debug messages in the Console.app log viewer. */ + /* https://en.wikipedia.org/wiki/Console_(OS_X) */ + /* Levels DEBUG and INFO are ignored so use level NOTICE. */ +#if defined(__APPLE__) && defined(WINDOWED) && defined(LAUNCH_DEBUG) + va_start(v, fmt); + vsyslog(LOG_NOTICE, fmt, v); + va_end(v); +#endif +} + +/* + * Print a debug message followed by the name of the function that resulted in an error + * and a textual description of the error, as with perror(). + */ +void pyi_global_perror(const char *funcname, const char *fmt, ...) { + va_list v; + + va_start(v, fmt); + vprintf_to_stderr(fmt, v); + va_end(v); + perror(funcname); // perror() writes to stderr + + #if defined(__APPLE__) && defined(WINDOWED) && defined(LAUNCH_DEBUG) + va_start(v, fmt); + vsyslog(LOG_NOTICE, fmt, v); + vsyslog(LOG_NOTICE, "%m\n", NULL); // %m emits the result of strerror() + va_end(v); + #endif +} + +/* + * Windows errors. + * + * Print a debug message followed by the name of the function that resulted in an error + * and a textual description of the error, as returned by FormatMessage. + */ +#ifdef _WIN32 +void pyi_global_winerror(const char *funcname, const char *fmt, ...) { + va_list v; + + va_start(v, fmt); + vprintf_to_stderr(fmt, v); + va_end(v); + printf_to_stderr("%s: %s", funcname, GetWinErrorString(GetLastError())); +} +#endif diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_global.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_global.h new file mode 100644 index 0000000000000000000000000000000000000000..393b7d64e43f859d67118b296d2b45f6a8eb83cf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_global.h @@ -0,0 +1,174 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Global shared declarations used in many bootloader files. + */ + +#ifndef PYI_GLOBAL_H +#define PYI_GLOBAL_H + +/* + * Detect memory leaks. + * + * Use Boehm garbage collector to detect memory leaks. + * malloc(), free(), strdup() and similar functions + * are replaced by calls from the gc library. + */ +#ifdef PYI_LEAK_DETECTOR + #include +#endif + +/* + * Definition of type boolean. On OSX boolean type is available + * in header . + */ +#ifdef __APPLE__ + #include /* bool, true, false */ +#else +/* + * It looks like more recent versions of MSVC complains about 'typedef int bool'. + * They probably have the type 'bool' defined. + * TODO find out more info. + */ + #undef bool + #undef true + #undef false +typedef int bool; + #define true 1 + #define false 0 +#endif + +/* Type for dynamic library. */ +#ifdef _WIN32 + #include /* HINSTANCE */ + #define dylib_t HINSTANCE +#else + #define dylib_t void * +#endif + +/* Wrap some windows specific declarations for Unix. */ +#ifndef _WIN32 + #define HMODULE void * +#endif + +/* + * On Windows PATH_MAX does not exist but MAX_PATH does. + * WinAPI MAX_PATH limit is only 256. MSVCR fuctions does not have this limit. + * Redefine PATH_MAX for Windows to support longer path names. + */ +/* TODO use MSVCR function for file path handling. */ +#ifdef _WIN32 + #ifdef PATH_MAX + #undef PATH_MAX /* On Windows override PATH_MAX if defined. */ + #endif + #define PATH_MAX 4096 /* Default value on Linux. */ +#elif __APPLE__ + #include + #define PATH_MAX 1024 /* Recommended value for OSX. */ +#else + #include /* PATH_MAX */ +#endif + +/* + * Debug and error macros. + */ + +void pyi_global_printf(const char *fmt, ...); +void pyi_global_perror(const char *funcname, const char *fmt, ...); +#ifdef _WIN32 + void pyi_global_winerror(const char *funcname, const char *fmt, ...); +#endif +/* + * On Windows and with windowed mode (no console) show error messages + * in message boxes. In windowed mode nothing is written to console. + */ + +#if defined(_WIN32) && defined(WINDOWED) +void mbfatalerror(const char *fmt, ...); + #define FATALERROR mbfatalerror + +void mbothererror(const char *fmt, ...); + #define OTHERERROR mbothererror + + void mbfatal_perror(const char *funcname, const char *fmt, ...); + #define FATAL_PERROR mbfatal_perror + + void mbfatal_winerror(const char *funcname, const char *fmt, ...); + #define FATAL_WINERROR mbfatal_winerror + +#else +/* TODO copy over stbprint to bootloader. */ + #define FATALERROR pyi_global_printf + #define OTHERERROR pyi_global_printf + #define FATAL_PERROR pyi_global_perror + #define FATAL_WINERROR pyi_global_winerror +#endif /* WIN32 and WINDOWED */ + +/* Enable or disable debug output. */ + +#ifdef LAUNCH_DEBUG + #if defined(_WIN32) && defined(WINDOWED) + /* Don't have console, resort to debugger output */ + #define VS mbvs +void mbvs(const char *fmt, ...); + #else + /* Have console, printf works */ + #define VS pyi_global_printf + #endif +#else + #if defined(_WIN32) && defined(_MSC_VER) + #define VS + #else + #define VS(...) + #endif +#endif + +/* Path and string macros. */ + +#ifdef _WIN32 + #define PYI_PATHSEP ';' + #define PYI_CURDIR '.' + #define PYI_SEP '\\' +/* + * For some functions like strcat() we need to pass + * string and not only char. + */ + #define PYI_SEPSTR "\\" + #define PYI_PATHSEPSTR ";" + #define PYI_CURDIRSTR "." +#else + #define PYI_PATHSEP ':' + #define PYI_CURDIR '.' + #define PYI_SEP '/' + #define PYI_SEPSTR "/" + #define PYI_PATHSEPSTR ":" + #define PYI_CURDIRSTR "." +#endif + +/* Strings are usually terminated by this character. */ +#define PYI_NULLCHAR '\0' + +/* File seek and tell with large (64-bit) offsets */ +#if defined(_WIN32) && defined(_MSC_VER) + #define pyi_fseek _fseeki64 + #define pyi_ftell _ftelli64 +#else + #define pyi_fseek fseeko + #define pyi_ftell ftello +#endif + +/* Saved LC_CTYPE locale */ +extern char *saved_locale; + +#endif /* PYI_GLOBAL_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_launch.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_launch.c new file mode 100644 index 0000000000000000000000000000000000000000..d1c35f01cbf81eff8eaa16c6fd1e7a19665e2c81 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_launch.c @@ -0,0 +1,688 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Launch a python module from an archive. + */ + +#if defined(__APPLE__) && defined(WINDOWED) + #include /* TransformProcessType */ +#endif + +#ifdef _WIN32 + #include +#else + #include /* CODESET, nl_langinfo */ + #include /* malloc */ +#endif +#include /* setlocale */ +#include +#include /* ptrdiff_t */ +#include /* vsnprintf */ +#include /* strcpy */ +#include /* struct stat */ + +/* PyInstaller headers. */ +#include "pyi_launch.h" +#include "pyi_global.h" +#include "pyi_path.h" +#include "pyi_archive.h" +#include "pyi_utils.h" +#include "pyi_python.h" +#include "pyi_pythonlib.h" +#include "pyi_win32_utils.h" /* CreateActContext */ + +/* Max count of possible opened archives in multipackage mode. */ +#define _MAX_ARCHIVE_POOL_LEN 20 + +/* + * The functions in this file defined in reverse order so that forward + * declarations are not necessary. + */ + +int +checkFile(char *buf, const char *fmt, ...) +{ + va_list args; + struct stat tmp; + + va_start(args, fmt); + if (vsnprintf(buf, PATH_MAX, fmt, args) >= PATH_MAX) { + return -1; + }; + va_end(args); + + return stat(buf, &tmp); +} + +/* Splits the item in the form path:filename */ +int +splitName(char *path, char *filename, const char *item) +{ + char *p; + + VS("LOADER: Splitting item into path and filename\n"); + // copy directly into destination buffer and manipulate there + if (snprintf(path, PATH_MAX, "%s", item) >= PATH_MAX) { + return -1; + } + p = strchr(path, ':'); + if (p == NULL) { // No colon in string + return -1; + }; + p[0] ='\0'; // terminate path part + // `path` fits into PATH_MAX, so will all substrings + strcpy(filename, ++p); + if (path[0] == 0 || filename[0] == 0) { + return -1; + } + return 0; +} + +/* Copy the dependencies file from a directory to the tempdir */ +static int +copyDependencyFromDir(ARCHIVE_STATUS *status, const char *srcpath, const char *filename) +{ + if (pyi_create_temp_path(status) == -1) { + return -1; + } + + VS("LOADER: Coping file %s to %s\n", srcpath, status->temppath); + + if (pyi_copy_file(srcpath, status->temppath, filename) == -1) { + return -1; + } + return 0; +} + +/* + * Look for the archive identified by path into the ARCHIVE_STATUS pool archive_pool. + * If the archive is found, a pointer to the associated ARCHIVE_STATUS is returned + * otherwise the needed archive is opened and added to the pool and then returned. + * If an error occurs, returns NULL. + * + * Having several archives is useful for sharing binary dependencies with several + * executables (multipackage feature). + */ +static ARCHIVE_STATUS * +_get_archive(ARCHIVE_STATUS *archive_pool[], const char *path) +{ + ARCHIVE_STATUS *archive = NULL; + int index = 0; + int SELF = 0; + + VS("LOADER: Getting file from archive.\n"); + + if (pyi_create_temp_path(archive_pool[SELF]) == -1) { + return NULL; + } + + for (index = 1; archive_pool[index] != NULL; index++) { + if (strcmp(archive_pool[index]->archivename, path) == 0) { + VS("LOADER: Archive found: %s\n", path); + return archive_pool[index]; + } + VS("LOADER: Checking next archive in the list...\n"); + } + + archive = pyi_arch_status_new(); + if (archive == NULL) { + return NULL; + } + + if ((snprintf(archive->archivename, PATH_MAX, "%s", path) >= PATH_MAX) || + (snprintf(archive->homepath, PATH_MAX, "%s", + archive_pool[SELF]->homepath) >= PATH_MAX) || + (snprintf(archive->temppath, PATH_MAX, "%s", + archive_pool[SELF]->temppath) >= PATH_MAX)) { + FATALERROR("Archive path exceeds PATH_MAX\n"); + pyi_arch_status_free(archive); + return NULL; + } + + /* + * Setting this flag prevents creating another temp directory and + * the directory from the main archive status is used. + */ + archive->has_temp_directory = archive_pool[SELF]->has_temp_directory; + + if (pyi_arch_open(archive)) { + FATAL_PERROR("malloc", "Error opening archive %s\n", path); + pyi_arch_status_free(archive); + return NULL; + } + + archive_pool[index] = archive; + return archive; +} + +/* Extract a file identifed by filename from the archive associated to status. */ +static int +extractDependencyFromArchive(ARCHIVE_STATUS *status, const char *filename) +{ + TOC * ptoc = status->tocbuff; + + VS("LOADER: Extracting dependencies from archive\n"); + + while (ptoc < status->tocend) { + if (strcmp(ptoc->name, filename) == 0) { + if (pyi_arch_extract2fs(status, ptoc)) { + return -1; + } + } + ptoc = pyi_arch_increment_toc_ptr(status, ptoc); + } + return 0; +} + +/* Decide if the dependency identified by item is in a onedir or onfile archive + * then call the appropriate function. + */ +static int +_extract_dependency(ARCHIVE_STATUS *archive_pool[], const char *item) +{ + ARCHIVE_STATUS *status = NULL; + ARCHIVE_STATUS *archive_status = archive_pool[0]; + char path[PATH_MAX]; + char filename[PATH_MAX]; + char srcpath[PATH_MAX]; + char archive_path[PATH_MAX]; + + char dirname[PATH_MAX]; + + VS("LOADER: Extracting dependencies\n"); + + if (splitName(path, filename, item) == -1) { + return -1; + } + + pyi_path_dirname(dirname, path); + + /* We need to identify three situations: 1) dependecies are in a onedir archive + * next to the current onefile archive, 2) dependencies are in a onedir/onefile + * archive next to the current onedir archive, 3) dependencies are in a onefile + * archive next to the current onefile archive. + */ + VS("LOADER: Checking if file exists\n"); + + /* TODO implement pyi_path_join to accept variable length of arguments for this case. */ + if (checkFile(srcpath, "%s%s%s%s%s", archive_status->homepath, PYI_SEPSTR, dirname, + PYI_SEPSTR, filename) == 0) { + VS("LOADER: File %s found, assuming is onedir\n", srcpath); + + if (copyDependencyFromDir(archive_status, srcpath, filename) == -1) { + FATALERROR("Error copying %s\n", filename); + return -1; + } + /* TODO implement pyi_path_join to accept variable length of arguments for this case. */ + } + else if (checkFile(srcpath, "%s%s%s%s%s%s%s", archive_status->homepath, PYI_SEPSTR, + "..", PYI_SEPSTR, dirname, PYI_SEPSTR, filename) == 0) { + VS("LOADER: File %s found, assuming is onedir\n", srcpath); + + if (copyDependencyFromDir(archive_status, srcpath, filename) == -1) { + FATALERROR("Error copying %s\n", filename); + return -1; + } + } + else { + VS("LOADER: File %s not found, assuming is onefile.\n", srcpath); + + /* TODO implement pyi_path_join to accept variable length of arguments for this case. */ + if ((checkFile(archive_path, "%s%s%s.pkg", archive_status->homepath, PYI_SEPSTR, + path) != 0) && + (checkFile(archive_path, "%s%s%s.exe", archive_status->homepath, PYI_SEPSTR, + path) != 0) && + (checkFile(archive_path, "%s%s%s", archive_status->homepath, PYI_SEPSTR, + path) != 0)) { + FATALERROR("Archive not found: %s\n", archive_path); + return -1; + } + + if ((status = _get_archive(archive_pool, archive_path)) == NULL) { + FATALERROR("Archive not found: %s\n", archive_path); + return -1; + } + + if (extractDependencyFromArchive(status, filename) == -1) { + FATALERROR("Error extracting %s\n", filename); + pyi_arch_status_free(status); + return -1; + } + } + + return 0; +} + +/* + * Check if binaries need to be extracted. If not, this is probably a onedir solution, + * and a child process will not be required on windows. + */ +int +pyi_launch_need_to_extract_binaries(ARCHIVE_STATUS *archive_status) +{ + TOC * ptoc = archive_status->tocbuff; + + while (ptoc < archive_status->tocend) { + if (ptoc->typcd == ARCHIVE_ITEM_BINARY || ptoc->typcd == ARCHIVE_ITEM_DATA || + ptoc->typcd == ARCHIVE_ITEM_ZIPFILE) { + return true; + } + + if (ptoc->typcd == ARCHIVE_ITEM_DEPENDENCY) { + return true; + } + ptoc = pyi_arch_increment_toc_ptr(archive_status, ptoc); + } + return false; +} + +/* + * Extract all binaries (type 'b') and all data files (type 'x') to the filesystem + * and checks for dependencies (type 'd'). If dependencies are found, extract them. + * + * 'Multipackage' feature includes dependencies. Dependencies are files in other + * .exe files. Having files in other executables allows share binary files among + * executables and thus reduce the final size of the executable. + */ +int +pyi_launch_extract_binaries(ARCHIVE_STATUS *archive_status) +{ + int retcode = 0; + ptrdiff_t index = 0; + + /* + * archive_pool[0] is reserved for the main process, the others for dependencies. + */ + ARCHIVE_STATUS *archive_pool[_MAX_ARCHIVE_POOL_LEN]; + TOC * ptoc = archive_status->tocbuff; + + /* Clean memory for archive_pool list. */ + memset(&archive_pool, 0, _MAX_ARCHIVE_POOL_LEN * sizeof(ARCHIVE_STATUS *)); + + /* Current process is the 1st item. */ + archive_pool[0] = archive_status; + + VS("LOADER: Extracting binaries\n"); + + while (ptoc < archive_status->tocend) { + if (ptoc->typcd == ARCHIVE_ITEM_BINARY || ptoc->typcd == ARCHIVE_ITEM_DATA || + ptoc->typcd == ARCHIVE_ITEM_ZIPFILE) { + if (pyi_arch_extract2fs(archive_status, ptoc)) { + retcode = -1; + break; /* No need to extract other items in case of error. */ + } + } + + else { + /* 'Multipackage' feature - dependency is stored in different executables. */ + if (ptoc->typcd == ARCHIVE_ITEM_DEPENDENCY) { + if (_extract_dependency(archive_pool, ptoc->name) == -1) { + retcode = -1; + break; /* No need to extract other items in case of error. */ + } + + } + } + ptoc = pyi_arch_increment_toc_ptr(archive_status, ptoc); + } + + /* + * Free memory allocated for archive_pool data. Do not free memory + * of the main process - start with 2nd item. + */ + for (index = 1; archive_pool[index] != NULL; index++) { + pyi_arch_status_free(archive_pool[index]); + } + + return retcode; +} + +/* + * Extract python exception message (string representation) from pvalue + * part of the error indicator data returned by PyErr_Fetch(). + * Returns a copy of message string or NULL. Must be freed by caller. + */ +static char * +_pyi_extract_exception_message(PyObject *pvalue) +{ + PyObject *pvalue_str; + const char *pvalue_cchar; + char *retval = NULL; + + pvalue_str = PI_PyObject_Str(pvalue); + pvalue_cchar = PI_PyUnicode_AsUTF8(pvalue_str); + if (pvalue_cchar) { + retval = strdup(pvalue_cchar); + } + Py_DECREF(pvalue_str); + + return retval; +} + +/* + * Extract python exception traceback from error indicator data + * returned by PyErr_Fetch(). + * Returns a copy of traceback string or NULL. Must be freed by caller. + */ +static char * +_pyi_extract_exception_traceback(PyObject *ptype, PyObject *pvalue, + PyObject *ptraceback) +{ + PyObject *module; + char *retval = NULL; + + /* Attempt to get a full traceback, source lines will only + * be available with --noarchive option */ + module = PI_PyImport_ImportModule("traceback"); + if (module != NULL) { + PyObject *func = PI_PyObject_GetAttrString(module, "format_exception"); + if (func) { + PyObject *tb, *tb_str; + const char *tb_cchar; + tb = PI_PyObject_CallFunctionObjArgs(func, ptype, pvalue, + ptraceback, NULL); + tb_str = PI_PyObject_Str(tb); + tb_cchar = PI_PyUnicode_AsUTF8(tb_str); + if (tb_cchar) { + retval = strdup(tb_cchar); + } + Py_DECREF(tb); + Py_DECREF(tb_str); + } + Py_DECREF(func); + } + Py_DECREF(module); + + return retval; +} + +/* + * Run scripts + * Return non zero on failure + */ +int +pyi_launch_run_scripts(ARCHIVE_STATUS *status) +{ + unsigned char *data; + char buf[PATH_MAX]; + TOC * ptoc = status->tocbuff; + PyObject *__main__; + PyObject *__file__; + PyObject *main_dict; + PyObject *code, *retval; + + __main__ = PI_PyImport_AddModule("__main__"); + + if (!__main__) { + FATALERROR("Could not get __main__ module."); + return -1; + } + + main_dict = PI_PyModule_GetDict(__main__); + + if (!main_dict) { + FATALERROR("Could not get __main__ module's dict."); + return -1; + } + + /* Iterate through toc looking for scripts (type 's') */ + while (ptoc < status->tocend) { + if (ptoc->typcd == ARCHIVE_ITEM_PYSOURCE) { + /* Get data out of the archive. */ + data = pyi_arch_extract(status, ptoc); + /* Set the __file__ attribute within the __main__ module, + * for full compatibility with normal execution. */ + if (snprintf(buf, PATH_MAX, "%s%c%s.py", status->mainpath, PYI_SEP, ptoc->name) >= PATH_MAX) { + FATALERROR("Absolute path to script exceeds PATH_MAX\n"); + return -1; + } + VS("LOADER: Running %s.py\n", ptoc->name); + __file__ = PI_PyUnicode_FromString(buf); + PI_PyObject_SetAttrString(__main__, "__file__", __file__); + Py_DECREF(__file__); + + /* Unmarshall code object */ + code = PI_PyMarshal_ReadObjectFromString((const char *) data, ptoc->ulen); + + if (!code) { + FATALERROR("Failed to unmarshal code object for %s\n", ptoc->name); + PI_PyErr_Print(); + return -1; + } + /* Run it */ + retval = PI_PyEval_EvalCode(code, main_dict, main_dict); + + /* If retval is NULL, an error occurred. Otherwise, it is a Python object. + * (Since we evaluate module-level code, which is not allowed to return an + * object, the Python object returned is always None.) */ + if (!retval) { + #if defined(WINDOWED) && defined(LAUNCH_DEBUG) + /* In windowed mode, we will display error details in + * dialogs. For that, we need to extract the error + * indicator data before PyErr_Print() call below clears + * it. But it seems that for PyErr_Print() to properly + * exit on SystemExit(), we also need to restore the error + * indicator via PyErr_Restore(). Therefore, we extract + * deep copies of relevant strings, and release all + * references to error indicator and its data. + */ + PyObject *ptype, *pvalue, *ptraceback; + char *msg_exc, *msg_tb; + + PI_PyErr_Fetch(&ptype, &pvalue, &ptraceback); + msg_exc = _pyi_extract_exception_message(pvalue); + msg_tb = _pyi_extract_exception_traceback(ptype, pvalue, + ptraceback); + PI_PyErr_Restore(ptype, pvalue, ptraceback); + #endif + + /* If the error was SystemExit, PyErr_Print calls exit() without + * returning. This means we won't print "Failed to execute" on + * normal SystemExit's. + */ + PI_PyErr_Print(); + FATALERROR("Failed to execute script %s\n", ptoc->name); + + #if defined(WINDOWED) && defined(LAUNCH_DEBUG) + /* As console is unavailable in windowed mode, we display + * error details (exception message and traceback) in + * additional error dialogs (Windows only). + */ + if (msg_exc) { + FATALERROR("Error: %s\n", msg_exc); + free(msg_exc); + } + if (msg_tb) { + FATALERROR("Traceback: %s\n", msg_tb); + free(msg_tb); + } + #endif /* if defined(WINDOWED) and defined(LAUNCH_DEBUG) */ + + /* Be consistent with python interpreter, which returns + * 1 if it exits due to unhandled exception. + */ + return 1; + } + free(data); + } + + ptoc = pyi_arch_increment_toc_ptr(status, ptoc); + } + return 0; +} + +/* + * call a simple "int func(void)" entry point. Assumes such a function + * exists in the main namespace. + * Return non zero on failure, with -2 if the specific error is + * that the function does not exist in the namespace. + */ +int +callSimpleEntryPoint(char *name, int *presult) +{ + int rc = -1; + /* Objects with no ref. */ + PyObject *mod, *dict; + /* Objects with refs to kill. */ + PyObject *func = NULL, *pyresult = NULL; + + mod = PI_PyImport_AddModule("__main__"); /* NO ref added */ + + if (!mod) { + VS("LOADER: No __main__\n"); + goto done; + } + dict = PI_PyModule_GetDict(mod); /* NO ref added */ + + if (!mod) { + VS("LOADER: No __dict__\n"); + goto done; + } + func = PI_PyDict_GetItemString(dict, name); + + if (func == NULL) { /* should explicitly check KeyError */ + VS("LOADER: CallSimpleEntryPoint can't find the function name\n"); + rc = -2; + goto done; + } + pyresult = PI_PyObject_CallFunction(func, ""); + + if (pyresult == NULL) { + goto done; + } + PI_PyErr_Clear(); + *presult = PI_PyLong_AsLong(pyresult); + rc = PI_PyErr_Occurred() ? -1 : 0; + VS( rc ? "LOADER: Finished with failure\n" : "LOADER: Finished OK\n"); + /* all done! */ +done: + Py_XDECREF(func); + Py_XDECREF(pyresult); + + /* can't leave Python error set, else it may + * cause failures in later async code */ + if (rc) { + /* But we will print them 'cos they may be useful */ + PI_PyErr_Print(); + } + PI_PyErr_Clear(); + return rc; +} + +/* For finer grained control. */ + +void +pyi_launch_initialize(ARCHIVE_STATUS * status) +{ +#if defined(_WIN32) + char * manifest; + manifest = pyi_arch_get_option(status, "pyi-windows-manifest-filename"); + + if (NULL != manifest) { + char manifest_path[PATH_MAX]; + if (pyi_path_join(manifest_path, status->mainpath, manifest) == NULL) { + FATALERROR("Path of manifest-file (%s) length exceeds " + "buffer[%d] space\n", status->mainpath, PATH_MAX); + }; + CreateActContext(manifest_path); + } +#endif /* if defined(_WIN32) */ +} + +/* + * Once init'ed, you might want to extractBinaries() + * If you do, what comes after is very platform specific. + * Once you've taken care of the platform specific details, + * or if there are no binaries to extract, you go on + * to pyi_launch_execute(), which is the important part. + */ +int +pyi_launch_execute(ARCHIVE_STATUS *status) +{ + int rc = 0; + + /* Load Python DLL */ + if (pyi_pylib_load(status)) { + return -1; + } + else { + /* With this flag Python cleanup will be called. */ + status->is_pylib_loaded = true; + } + + /* Start Python. */ + if (pyi_pylib_start_python(status)) { + return -1; + } + + /* Import core pyinstaller modules from the executable - bootstrap */ + if (pyi_pylib_import_modules(status)) { + return -1; + } + + /* Install zlibs - now all hooks in place */ + if (pyi_pylib_install_zlibs(status)) { + return -1; + } + +#ifndef WIN32 + + /* + * On Linux sys.getfilesystemencoding() returns None but should not. + * If it's None(NULL), get the filesystem encoding by using direct + * C calls and override it with correct value. + * + * TODO: This may not be needed any more. Please confirm on Linux. + */ + if (!*PI_Py_FileSystemDefaultEncoding) { + char *saved_locale, *loc_codeset; + saved_locale = strdup(setlocale(LC_CTYPE, NULL)); + VS("LOADER: LC_CTYPE was %s but resulted in NULL FileSystemDefaultEncoding\n", + saved_locale); + setlocale(LC_CTYPE, ""); + loc_codeset = nl_langinfo(CODESET); + setlocale(LC_CTYPE, saved_locale); + free(saved_locale); + VS("LOADER: Setting FileSystemDefaultEncoding to %s (was NULL)\n", loc_codeset); + *PI_Py_FileSystemDefaultEncoding = loc_codeset; + } +#endif /* WIN32 */ + + /* Run scripts */ + rc = pyi_launch_run_scripts(status); + + VS("LOADER: OK.\n"); + + return rc; +} + +void +pyi_launch_finalize(ARCHIVE_STATUS *status) +{ + pyi_pylib_finalize(status); +} + +/* + * On OS X this ensures that the parent process goes to background. + * Call TransformProcessType() in the parent process. + */ +void +pyi_parent_to_background() +{ +#if defined(__APPLE__) && defined(WINDOWED) + ProcessSerialNumber psn = { 0, kCurrentProcess }; + OSStatus returnCode = TransformProcessType(&psn, + kProcessTransformToBackgroundApplication); +#endif +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_launch.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_launch.h new file mode 100644 index 0000000000000000000000000000000000000000..34546feb3019b0791b300873a4a63fec54e0754d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_launch.h @@ -0,0 +1,84 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Launch a python module from an archive and other related stuff. + */ +#ifndef PYI_LAUNCH_H +#define PYI_LAUNCH_H + +#include "pyi_archive.h" + +/***************************************************************** +* The following 4 entries are for applications which may need to +* use to 2 steps to execute +*****************************************************************/ + +/* + * Extract binaries in the archive + * + * @param workpath (OUT) Where the binaries were extracted to. If + * none extracted, is NULL. + * + * @return 0 on success, non-zero otherwise. + */ +int pyi_launch_extract_binaries(ARCHIVE_STATUS *archive_status); + +/* + * Check if binaries need to be extracted. If not, this is probably a onedir + * solution, and a child process will not be required on windows. + */ +int pyi_launch_need_to_extract_binaries(ARCHIVE_STATUS *archive_status); + +/* + * Wrapped platform specific initialization before loading Python and executing + * all scripts in the archive. + */ +void pyi_launch_initialize(ARCHIVE_STATUS *archive_status); + +/* + * Wrapped platform specific finalization before loading Python and executing + * all scripts in the archive. + */ +void pyi_launch_finalize(ARCHIVE_STATUS *archive_status); + +/* + * Load Python and execute all scripts in the archive + * + * @return -1 for internal failures, or the rc of the last script. + */ +int pyi_launch_execute(ARCHIVE_STATUS *status); + +/* + * Transform parent process to background (OSX only). + */ +void pyi_parent_to_background(); + +/* + * Call a simple "int func(void)" entry point. Assumes such a function + * exists in the main namespace. + * Return non zero on failure, with -2 if the specific error is + * that the function does not exist in the namespace. + * + * @param name Name of the function to execute. + * @param presult Integer return value. + */ +int callSimpleEntryPoint(char *name, int *presult); + +/** + * Clean up extracted binaries + */ +void cleanUp(ARCHIVE_STATUS *status); + +#endif /* PYI_LAUNCH_H */ + diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_main.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_main.c new file mode 100644 index 0000000000000000000000000000000000000000..917abec296470b953cd249677b154359e3d26e75 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_main.c @@ -0,0 +1,222 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Bootloader for a packed executable. + */ + +#ifdef _WIN32 + #include + #include +#endif +#include /* FILE */ +#include /* calloc */ +#include /* memset */ + +#if defined(__linux__) + #include /* prctl() */ +#endif + +/* PyInstaller headers. */ +#include "pyi_main.h" +#include "pyi_global.h" /* PATH_MAX */ +#include "pyi_path.h" +#include "pyi_archive.h" +#include "pyi_utils.h" +#include "pyi_pythonlib.h" +#include "pyi_launch.h" +#include "pyi_win32_utils.h" + +int +pyi_main(int argc, char * argv[]) +{ + /* archive_status contain status information of the main process. */ + ARCHIVE_STATUS *archive_status = NULL; + char executable[PATH_MAX]; + char homepath[PATH_MAX]; + char archivefile[PATH_MAX]; + int rc = 0; + char *extractionpath = NULL; + +#if defined(__linux__) + char *processname = NULL; +#endif /* defined(__linux__) */ + +#ifdef _MSC_VER + /* Visual C runtime incorrectly buffers stderr */ + setbuf(stderr, (char *)NULL); +#endif /* _MSC_VER */ + + VS("PyInstaller Bootloader 3.x\n"); + + archive_status = pyi_arch_status_new(archive_status); + if (archive_status == NULL) { + return -1; + } + if ((! pyi_path_executable(executable, argv[0])) || + (! pyi_path_archivefile(archivefile, executable)) || + (! pyi_path_homepath(homepath, executable))) { + return -1; + } + + /* For the curious: + * On Windows, the UTF-8 form of MEIPASS2 is passed to pyi_setenv, which + * decodes to UTF-16 before passing it to the Windows API. So the var's value + * is full unicode. + * + * On OS X/Linux, the MEIPASS2 value is passed as the bytes received from the OS. + * Only Python will care about its encoding, and it is passed to Python using + * PyUnicode_DecodeFSDefault. + */ + + extractionpath = pyi_getenv("_MEIPASS2"); + + /* If the Python program we are about to run invokes another PyInstaller + * one-file program as subprocess, this subprocess must not be fooled into + * thinking that it is already unpacked. Therefore, PyInstaller deletes + * the _MEIPASS2 variable from the environment. + */ + + pyi_unsetenv("_MEIPASS2"); + + VS("LOADER: _MEIPASS2 is %s\n", (extractionpath ? extractionpath : "NULL")); + + if ((! pyi_arch_setup(archive_status, executable)) && + (! pyi_arch_setup(archive_status, archivefile))) { + FATALERROR("Cannot open self %s or archive %s\n", + executable, archivefile); + return -1; + } + +#if defined(__linux__) + + /* Set process name on linux. The environment variable is set by + parent launcher process. */ + processname = pyi_getenv("_PYI_PROCNAME"); + if (processname) { + VS("LOADER: restoring linux process name from _PYI_PROCNAME: %s\n", processname); + if (prctl(PR_SET_NAME, processname, 0, 0)) { + FATALERROR("LOADER: failed to set linux process name!\n"); + return -1; + } + free(processname); + } + pyi_unsetenv("_PYI_PROCNAME"); + +#endif /* defined(__linux__) */ + + /* These are used only in pyi_pylib_set_sys_argv, which converts to wchar_t */ + archive_status->argc = argc; + archive_status->argv = argv; + +#if defined(_WIN32) || defined(__APPLE__) + + /* On Windows and Mac use single-process for --onedir mode. */ + if (!extractionpath && !pyi_launch_need_to_extract_binaries(archive_status)) { + VS("LOADER: No need to extract files to run; setting extractionpath to homepath\n"); + extractionpath = homepath; + } + +#endif + +#ifdef _WIN32 + + if (extractionpath) { + /* Add extraction folder to DLL search path */ + wchar_t * dllpath_w; + dllpath_w = pyi_win32_utils_from_utf8(NULL, extractionpath, 0); + SetDllDirectory(dllpath_w); + VS("LOADER: SetDllDirectory(%s)\n", extractionpath); + free(dllpath_w); + } +#endif /* ifdef _WIN32 */ + + if (extractionpath) { + VS("LOADER: Already in the child - running user's code.\n"); + + /* If binaries were extracted to temppath, + * we pass it through status variable + */ + if (strcmp(homepath, extractionpath) != 0) { + if (snprintf(archive_status->temppath, PATH_MAX, + "%s", extractionpath) >= PATH_MAX) { + VS("LOADER: temppath exceeds PATH_MAX\n"); + return -1; + } + /* + * Temp path exits - set appropriate flag and change + * status->mainpath to point to temppath. + */ + archive_status->has_temp_directory = true; + strcpy(archive_status->mainpath, archive_status->temppath); + } + + /* Main code to initialize Python and run user's code. */ + pyi_launch_initialize(archive_status); + rc = pyi_launch_execute(archive_status); + pyi_launch_finalize(archive_status); + + } + else { +#if defined(__linux__) + char tmp_processname[16]; /* 16 bytes as per prctl() man page */ +#endif /* defined(__linux__) */ + + /* status->temppath is created if necessary. */ + if (pyi_launch_extract_binaries(archive_status)) { + VS("LOADER: temppath is %s\n", archive_status->temppath); + VS("LOADER: Error extracting binaries\n"); + return -1; + } + + /* Run the 'child' process, then clean up. */ + + VS("LOADER: Executing self as child\n"); + pyi_setenv("_MEIPASS2", + archive_status->temppath[0] != + 0 ? archive_status->temppath : homepath); + + VS("LOADER: set _MEIPASS2 to %s\n", pyi_getenv("_MEIPASS2")); + +#if defined(__linux__) + + /* Pass the process name to child via environment variable. */ + if (!prctl(PR_GET_NAME, tmp_processname, 0, 0)) { + VS("LOADER: linux: storing process name into _PYI_PROCNAME: %s\n", tmp_processname); + pyi_setenv("_PYI_PROCNAME", tmp_processname); + } + +#endif /* defined(__linux__) */ + + if (pyi_utils_set_environment(archive_status) == -1) { + return -1; + } + + /* Transform parent to background process on OSX only. */ + pyi_parent_to_background(); + + /* Run user's code in a subprocess and pass command line arguments to it. */ + rc = pyi_utils_create_child(executable, archive_status, argc, argv); + + VS("LOADER: Back to parent (RC: %d)\n", rc); + + VS("LOADER: Doing cleanup\n"); + + if (archive_status->has_temp_directory == true) { + pyi_remove_temp_path(archive_status->temppath); + } + pyi_arch_status_free(archive_status); + + } + return rc; +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_main.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_main.h new file mode 100644 index 0000000000000000000000000000000000000000..667e88ccf472faadcd835e414a15dca2e603fd1a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_main.h @@ -0,0 +1,14 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +int pyi_main(int argc, char * argv[]); diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_path.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_path.c new file mode 100644 index 0000000000000000000000000000000000000000..673d0a89ca24ae58b33b374bb45dd5d8d5aa5a70 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_path.c @@ -0,0 +1,386 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Path manipulation utilities. + */ + +#include /* struct stat, struct _stat */ +#include /* stat() */ + +#ifdef _WIN32 + #include /* GetModuleFileNameW */ + #include + #ifdef __GNUC__ + #include /* basename(), dirname() */ + #endif +#elif __APPLE__ + #include /* basename(), dirname() */ + #include /* _NSGetExecutablePath() */ +#else + #include /* basename() */ + #include /* unlink */ +#endif + +#include /* FILE, fopen */ +#include /* _fullpath, realpath */ +#include + +/* PyInstaller headers. */ +#include "pyi_path.h" +#include "pyi_global.h" /* PATH_MAX */ +#include "pyi_utils.h" +#include "pyi_win32_utils.h" + +/* + * Giving a fullpath, it will copy to the buffer a string + * which contains the path without last component. + */ +bool +pyi_path_dirname(char *result, const char *path) +{ +#ifndef HAVE_DIRNAME + size_t len = 0; + char *match = NULL; + + /* Copy path to result and then just write '\0' to the place with path separator. */ + if (snprintf(result, PATH_MAX, "%s", path) >= PATH_MAX) { + return false; + } + + /* Remove separator from the end. */ + len = strlen(result)-1; + if (len >= 0 && result[len] == PYI_SEP) { + result[len] = PYI_NULLCHAR; + } + + /* Remove the rest of the string. */ + match = strrchr(result, PYI_SEP); + if (match != NULL) { + *match = PYI_NULLCHAR; + } + else { + /* No dir separator found, so no dir-part, so use current dir */ + *result = PYI_CURDIR; + result[1] = PYI_NULLCHAR; + } +#else /* ifndef HAVE_DIRNAME */ + /* Use dirname() for other platforms. */ + char *dirpart = NULL; + char tmp[PATH_MAX]; + /* Copy path to 'tmp' because dirname() modifies the original string! */ + if (snprintf(tmp, PATH_MAX, "%s", path) >= PATH_MAX) { + return false; + } + dirpart = (char *) dirname((char *) tmp); /* _XOPEN_SOURCE - no 'const'. */ + if (snprintf(result, PATH_MAX, "%s", dirpart) >= PATH_MAX) { + return false; + } +#endif /* ifndef HAVE_DIRNAME */ + return true; +} + +/* + * Returns the last component of the path in filename. Return result + * in new buffer. + */ +bool +pyi_path_basename(char *result, const char *path) +{ +#ifndef HAVE_BASENAME + /* Search for the last directory separator in PATH. */ + char *basename = strrchr (path, '\\'); + + if (!basename) { + basename = strrchr (path, '/'); + } + + /* If found, return the address of the following character, + * or the start of the parameter passed in. */ + strcpy(result, basename ? ++basename : (char*)path); +#else + char *base = NULL; + base = (char *) basename((char *) path); /* _XOPEN_SOURCE - no 'const'. */ + strcpy(result, base); +#endif /* ifndef HAVE_BASENAME */ + return true; +} + +/* + * Join two path components. + * Joined path is returned without slash at the end. + * + * If result is NULL, allocates and returns a new buffer which the caller + * is responsible for freeing. Otherwise, result should be a buffer of at + * least PATH_MAX characters. + * + * Returns NULL on failure. + */ +/* FIXME: Need to test for absolut path2 -- or mark this function as */ +/* only for an relative path2 */ +char * +pyi_path_join(char *result, const char *path1, const char *path2) +{ + size_t len, len2; + /* Copy path1 to result */ + len = snprintf(result, PATH_MAX, "%s", path1); + if (len >= PATH_MAX-1) { + return NULL; + } + /* Append trailing slash if missing. */ + if (result[len-1] != PYI_SEP) { + result[len++] = PYI_SEP; + result[len++] = PYI_NULLCHAR; + } + len = PATH_MAX - len; + len2 = strlen(path2); + if (len2 >= len) { + return NULL; + }; + /* Remove trailing slash from path2 if present. */ + if (path2[len2 - 1] == PYI_SEP) { + /* Append path2 without slash. */ + strncat(result, path2, len); + result[strlen(result) - 1] = PYI_NULLCHAR; + } + else { + /* path2 does not end with slash. */ + strncat(result, path2, len); + } + return result; +} + + +#if !defined(_WIN32) && !defined(__APPLE__) +/* + * Return full path to a file's directory, but keeps the basename. + * This is required to pass the correct basename to execvp(). + */ +int +pyi_path_fullpath_keep_basename(char *abs, const char *rel) +{ + char dirname[PATH_MAX]; + char full_dirname[PATH_MAX]; + char basename[PATH_MAX]; + pyi_path_basename(basename, rel); + pyi_path_dirname(dirname, rel); + if (realpath(dirname, full_dirname) == NULL) { + return false; + } + return (pyi_path_join(abs, full_dirname, basename) != NULL); +} +#endif + +/* + * Return full path to a file. Wraps platform specific function. + */ +int +pyi_path_fullpath(char *abs, size_t abs_size, const char *rel) +{ +#ifdef _WIN32 + wchar_t wrel[PATH_MAX + 1]; + wchar_t *wabs = NULL; + + pyi_win32_utils_from_utf8(wrel, rel, PATH_MAX); + + wabs = _wfullpath(NULL, wrel, PATH_MAX); + if (wabs == NULL) { + return 0; + } + + char *ret = pyi_win32_utils_to_utf8(abs, wabs, abs_size); + free(wabs); + + return ret != NULL; +#else + return realpath(rel, abs) != NULL; +#endif +} + +int +pyi_path_exists(char * path) +{ +#ifdef _WIN32 + wchar_t wpath[PATH_MAX + 1]; + struct _stat result; + pyi_win32_utils_from_utf8(wpath, path, PATH_MAX); + return _wstat(wpath, &result) == 0; +#else + struct stat result; + return stat(path, &result) == 0; +#endif +} + +/* Search $PATH for the program named 'appname' and return its full path. + * 'result' should be a buffer of at least PATH_MAX characters. + */ +bool +pyi_search_path(char * result, const char * appname) +{ + char *path = pyi_getenv("PATH"); // returns a copy + char *dirname; + + if (NULL == path) { + return false; + } + + dirname = strtok(path, PYI_PATHSEPSTR); + while (dirname != NULL) { + if ((pyi_path_join(result, dirname, appname) != NULL) + && pyi_path_exists(result)) { + return true; + } + dirname = strtok(NULL, PYI_PATHSEPSTR); + } + return false; +} + +/* + * Return full path to the current executable. + * Executable is the .exe created by pyinstaller: path/myappname.exe + * Because the calling process can set argv[0] to whatever it wants, + * we use a few alternate methods to get the executable path. + * + * execfile - buffer where to put path to executable. + * appname - usually the item argv[0]. + */ +bool +pyi_path_executable(char *execfile, const char *appname) +{ +#ifdef _WIN32 + wchar_t modulename_w[PATH_MAX]; + + /* GetModuleFileNameW returns an absolute, fully qualified path + */ + if (!GetModuleFileNameW(NULL, modulename_w, PATH_MAX)) { + FATAL_WINERROR("GetModuleFileNameW", "Failed to get executable path."); + return false; + } + + if (!pyi_win32_utils_to_utf8(execfile, modulename_w, PATH_MAX)) { + FATALERROR("Failed to convert executable path to UTF-8."); + return false; + } + +#elif __APPLE__ + char buffer[PATH_MAX]; + uint32_t length = sizeof(buffer); + + /* Mac OS X has special function to obtain path to executable. + * This may return a symlink. + */ + if (_NSGetExecutablePath(buffer, &length) != 0) { + FATALERROR("System error - unable to load!\n"); + return false; + } + + if (pyi_path_fullpath(execfile, PATH_MAX, buffer) == false) { + VS("LOADER: Cannot get fullpath for %s\n", execfile); + return false; + } + +#else /* ifdef _WIN32 */ + /* On Linux, Cygwin, FreeBSD, and Solaris, we try these /proc paths first + */ + size_t name_len = -1; + + #if defined(__linux__) || defined(__CYGWIN__) + name_len = readlink("/proc/self/exe", execfile, PATH_MAX-1); /* Linux, Cygwin */ + #elif defined(__FreeBSD__) + name_len = readlink("/proc/curproc/file", execfile, PATH_MAX-1); /* FreeBSD */ + #elif defined(__sun) + name_len = readlink("/proc/self/path/a.out", execfile, PATH_MAX-1); /* Solaris */ + #endif + + if (name_len != -1) { + /* execfile is not yet zero-terminated. result is the byte count. */ + *(execfile + name_len) = '\0'; + } else { + if (strchr(appname, PYI_SEP)) { + /* Absolute or relative path: Canonicalize directory path, + * but keep original basename. + */ + if (pyi_path_fullpath_keep_basename(execfile, appname) == false) { + VS("LOADER: Cannot get fullpath for %s\n", execfile); + return false; + } + } + else { + /* No absolute or relative path, just program name: search $PATH. + */ + char buffer[PATH_MAX]; + if (! pyi_search_path(buffer, appname)) { + /* Searching $PATH failed, user is crazy. */ + VS("LOADER: Searching $PATH failed for %s", appname); + if (snprintf(buffer, PATH_MAX, "%s", appname) >= PATH_MAX) { + VS("LOADER: Full path to application exceeds PATH_MAX: %s\n", appname); + return false; + } + } + if (pyi_path_fullpath_keep_basename(execfile, buffer) == false) { + VS("LOADER: Cannot get fullpath for %s\n", execfile); + return false; + } + } + } +#endif /* ifdef _WIN32 */ + VS("LOADER: executable is %s\n", execfile); + return true; +} + +/* + * Return absolute path to homepath. It is the directory containing executable. + */ +bool +pyi_path_homepath(char *homepath, const char *thisfile) +{ + /* Fill in here (directory of thisfile). */ + bool rc = pyi_path_dirname(homepath, thisfile); + VS("LOADER: homepath is %s\n", homepath); + return rc; +} + +/* + * Return full path to an external PYZ-archive. + * The name is based on the excutable's name: path/myappname.pkg + * + * archivefile - buffer where to put path the .pkg. + * thisfile - usually the executable's filename. + */ +bool +pyi_path_archivefile(char *archivefile, const char *thisfile) +{ +#ifdef _WIN32 + strcpy(archivefile, thisfile); + strcpy(archivefile + strlen(archivefile) - 3, "pkg"); + return true; +#else + return (snprintf(archivefile, PATH_MAX, "%s.pkg", thisfile) < PATH_MAX); +#endif +} + +/* + * Multiplatform wrapper around function fopen(). + */ +#ifdef _WIN32 +FILE* +pyi_path_fopen(const char* filename, const char* mode) +{ + wchar_t wfilename[PATH_MAX]; + wchar_t wmode[10]; + + pyi_win32_utils_from_utf8(wfilename, filename, PATH_MAX); + pyi_win32_utils_from_utf8(wmode, mode, 10); + return _wfopen(wfilename, wmode); +} +#endif diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_path.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_path.h new file mode 100644 index 0000000000000000000000000000000000000000..a276673c4a75d069cb244ace80e73c7e1d94fd0b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_path.h @@ -0,0 +1,43 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Path manipulation utilities. + */ + +#ifndef PYI_PATH_H +#define PYI_PATH_H + +#include "pyi_global.h" + +/* Path manipulation. Result is added to the supplied buffer. */ +bool pyi_path_basename(char *result, const char *path); +bool pyi_path_dirname(char *result, const char *path); +char *pyi_path_join(char *result, const char *path1, const char *path2); +int pyi_path_fullpath(char *abs, size_t abs_size, const char *rel); +/* TODO implement. */ +/* void *pyi_path_abspath(char *result, const char *path); */ +int pyi_path_exists(char *path); + +bool pyi_path_executable(char *execfile, const char *appname); +bool pyi_path_homepath(char *homepath, const char *executable); +bool pyi_path_archivefile(char *archivefile, const char *executable); + +#ifdef _WIN32 +FILE *pyi_path_fopen(const char *filename, const char *mode); +#else + #define pyi_path_fopen(x, y) fopen(x, y) +#endif +#define pyi_path_fclose(x) fclose(x) + +#endif /* PYI_PATH_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_python.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_python.c new file mode 100644 index 0000000000000000000000000000000000000000..ecf3a0824862882baf7a4be9f60a2bddc630b480 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_python.c @@ -0,0 +1,165 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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.h replacements. + */ + +#ifdef _WIN32 + #include /* HMODULE */ +#else + #include /* dlsym */ +#endif +#include /* ptrdiff_t */ +#include + +/* PyInstaller headers. */ +#include "pyi_global.h" +#include "pyi_python.h" + +/* + * Python Entry point declarations (see macros in pyi_python.h). + */ +/* external variables */ +DECLVAR(Py_DontWriteBytecodeFlag); +DECLVAR(Py_FileSystemDefaultEncoding); +DECLVAR(Py_FrozenFlag); +DECLVAR(Py_IgnoreEnvironmentFlag); +DECLVAR(Py_NoSiteFlag); +DECLVAR(Py_NoUserSiteDirectory); +DECLVAR(Py_OptimizeFlag); +DECLVAR(Py_VerboseFlag); +DECLVAR(Py_UnbufferedStdioFlag); + +/* functions with prefix `Py_` */ +DECLPROC(Py_BuildValue); +DECLPROC(Py_DecRef); +DECLPROC(Py_Finalize); +DECLPROC(Py_IncRef); +DECLPROC(Py_Initialize); +DECLPROC(Py_SetPath); +DECLPROC(Py_GetPath); +DECLPROC(Py_SetProgramName); +DECLPROC(Py_SetPythonHome); + +/* other functions */ +DECLPROC(PyDict_GetItemString); +DECLPROC(PyErr_Clear); +DECLPROC(PyErr_Occurred); +DECLPROC(PyErr_Print); +DECLPROC(PyErr_Fetch); +DECLPROC(PyErr_Restore); + +DECLPROC(PyImport_AddModule); +DECLPROC(PyImport_ExecCodeModule); +DECLPROC(PyImport_ImportModule); +DECLPROC(PyList_Append); +DECLPROC(PyList_New); +DECLPROC(PyLong_AsLong); +DECLPROC(PyModule_GetDict); +DECLPROC(PyObject_CallFunction); +DECLPROC(PyObject_CallFunctionObjArgs); +DECLPROC(PyObject_SetAttrString); +DECLPROC(PyObject_GetAttrString); +DECLPROC(PyObject_Str); +DECLPROC(PyRun_SimpleString); +DECLPROC(PySys_AddWarnOption); +DECLPROC(PySys_SetArgvEx); +DECLPROC(PySys_GetObject); +DECLPROC(PySys_SetObject); +DECLPROC(PySys_SetPath); +DECLPROC(PyUnicode_FromString); + +DECLPROC(Py_DecodeLocale); +DECLPROC(PyMem_RawFree); +DECLPROC(PyUnicode_FromFormat); +DECLPROC(PyUnicode_DecodeFSDefault); +DECLPROC(PyUnicode_Decode); +DECLPROC(PyUnicode_AsUTF8); + +DECLPROC(PyEval_EvalCode); +DECLPROC(PyMarshal_ReadObjectFromString); + +/* + * Get all of the entry points from libpython + * that we are interested in. + */ +int +pyi_python_map_names(HMODULE dll, int pyvers) +{ + GETVAR(dll, Py_DontWriteBytecodeFlag); + GETVAR(dll, Py_FileSystemDefaultEncoding); + GETVAR(dll, Py_FrozenFlag); + GETVAR(dll, Py_IgnoreEnvironmentFlag); + GETVAR(dll, Py_NoSiteFlag); + GETVAR(dll, Py_NoUserSiteDirectory); + GETVAR(dll, Py_OptimizeFlag); + GETVAR(dll, Py_VerboseFlag); + GETVAR(dll, Py_UnbufferedStdioFlag); + + /* functions with prefix `Py_` */ + GETPROC(dll, Py_BuildValue); + GETPROC(dll, Py_DecRef); + GETPROC(dll, Py_Finalize); + GETPROC(dll, Py_IncRef); + GETPROC(dll, Py_Initialize); + + GETPROC(dll, Py_SetPath); + GETPROC(dll, Py_GetPath); + GETPROC(dll, Py_SetProgramName); + GETPROC(dll, Py_SetPythonHome); + + /* other functions */ + GETPROC(dll, PyDict_GetItemString); + GETPROC(dll, PyErr_Clear); + GETPROC(dll, PyErr_Occurred); + GETPROC(dll, PyErr_Print); + GETPROC(dll, PyErr_Fetch); + GETPROC(dll, PyErr_Restore); + GETPROC(dll, PyImport_AddModule); + GETPROC(dll, PyImport_ExecCodeModule); + GETPROC(dll, PyImport_ImportModule); + GETPROC(dll, PyList_Append); + GETPROC(dll, PyList_New); + GETPROC(dll, PyLong_AsLong); + GETPROC(dll, PyModule_GetDict); + GETPROC(dll, PyObject_CallFunction); + GETPROC(dll, PyObject_CallFunctionObjArgs); + GETPROC(dll, PyObject_SetAttrString); + GETPROC(dll, PyObject_GetAttrString); + GETPROC(dll, PyObject_Str); + + GETPROC(dll, PyRun_SimpleString); + + GETPROC(dll, PySys_AddWarnOption); + GETPROC(dll, PySys_SetArgvEx); + GETPROC(dll, PySys_GetObject); + GETPROC(dll, PySys_SetObject); + GETPROC(dll, PySys_SetPath); + GETPROC(dll, PyEval_EvalCode); + GETPROC(dll, PyMarshal_ReadObjectFromString); + + GETPROC(dll, PyUnicode_FromString); + + GETPROC(dll, Py_DecodeLocale); + GETPROC(dll, PyMem_RawFree); + + GETPROC(dll, PyUnicode_FromFormat); + GETPROC(dll, PyUnicode_Decode); + GETPROC(dll, PyUnicode_DecodeFSDefault); + GETPROC(dll, PyUnicode_AsUTF8); + + VS("LOADER: Loaded functions from Python library.\n"); + + return 0; +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_python.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_python.h new file mode 100644 index 0000000000000000000000000000000000000000..68558cfc5f6cab0a30553b1c7a69957ac6c0d18a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_python.h @@ -0,0 +1,248 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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.h replacements. + * + * We use dynamic loading -> one binary can be used with (nearly) any Python + * version. This is the cruft necessary to do dynamic loading. + */ + +#ifndef PYI_PYTHON_H +#define PYI_PYTHON_H + +#include "pyi_global.h" +#ifdef _WIN32 + #include /* HMODULE */ +#endif +#include + +/* + * These macros used to define variables to hold dynamically accessed entry + * points. These are declared 'extern' in this header, and defined fully later. + */ +#ifdef _WIN32 + + #define EXTDECLPROC(result, name, args) \ + typedef result (__cdecl *__PROC__ ## name) args; \ + extern __PROC__ ## name PI_ ## name; + + #define EXTDECLVAR(vartyp, name) \ + typedef vartyp __VAR__ ## name; \ + extern __VAR__ ## name *PI_ ## name; + +#else + + #define EXTDECLPROC(result, name, args) \ + typedef result (*__PROC__ ## name) args; \ + extern __PROC__ ## name PI_ ## name; + + #define EXTDECLVAR(vartyp, name) \ + typedef vartyp __VAR__ ## name; \ + extern __VAR__ ## name *PI_ ## name; + +#endif /* WIN32 */ + +/* + * Python.h replacements. + * + * We do not want to include Python.h because we do no want to bind + * to a specific version of Python. If we were to, eg., use the + * Py_INCREF macro from Python.h, the compiled code would depend + * on the specific layout in memory of PyObject, and thus change + * when Python changes (or if your platform changes between 32bit + * and 64bit). In other words, you wouldn't be able to build a single + * bootloader working across all Python versions (which is specifically + * important on Windows). + * + * Instead, the bootloader does not depend on the Python ABI at all. + * It dynamically-load the Python library (after having unpacked it) + * and bind the exported functions. All Python objects are used as + * opaque data structures (through pointers only), so the code is + * fully compatible if the Python data structure layouts change. + */ + +/* Forward declarations of opaque Python types. */ +struct _object; +typedef struct _object PyObject; +struct _PyThreadState; +typedef struct _PyThreadState PyThreadState; + +/* The actual declarations of var & function entry points used. */ + +/* Flags. */ +EXTDECLVAR(int, Py_FrozenFlag); +EXTDECLVAR(int, Py_NoSiteFlag); +EXTDECLVAR(int, Py_OptimizeFlag); +EXTDECLVAR(const char*, Py_FileSystemDefaultEncoding); +EXTDECLVAR(int, Py_VerboseFlag); +EXTDECLVAR(int, Py_IgnoreEnvironmentFlag); +EXTDECLVAR(int, Py_DontWriteBytecodeFlag); +EXTDECLVAR(int, Py_NoUserSiteDirectory); +EXTDECLVAR(int, Py_UnbufferedStdioFlag); + +/* This initializes the table of loaded modules (sys.modules), and creates the fundamental modules builtins, __main__ and sys. It also initializes the module search path (sys.path). It does not set sys.argv; */ +EXTDECLPROC(int, Py_Initialize, (void)); +/* Undo all initializations made by Py_Initialize() and subsequent use of Python/C API functions, and destroy all sub-interpreters. */ +EXTDECLPROC(int, Py_Finalize, (void)); + +EXTDECLPROC(void, Py_IncRef, (PyObject *)); +EXTDECLPROC(void, Py_DecRef, (PyObject *)); + +/* + * These functions have to be called before Py_Initialize() + */ +EXTDECLPROC(void, Py_SetProgramName, (wchar_t *)); +EXTDECLPROC(void, Py_SetPythonHome, (wchar_t *)); +EXTDECLPROC(void, Py_SetPath, (wchar_t *)); /* new in Python 3 */ +EXTDECLPROC(wchar_t *, Py_GetPath, (void)); /* new in Python 3 */ + +EXTDECLPROC(void, PySys_SetPath, (wchar_t *)); +EXTDECLPROC(int, PySys_SetArgvEx, (int, wchar_t **, int)); +EXTDECLPROC(int, PyRun_SimpleString, (char *)); /* Py3: UTF-8 encoded string */ + +/* In Python 3 for these the first argument has to be a UTF-8 encoded string: */ +EXTDECLPROC(PyObject *, PyImport_ExecCodeModule, (char *, PyObject *)); +EXTDECLPROC(PyObject *, PyImport_ImportModule, (char *)); +EXTDECLPROC(PyObject *, PyImport_AddModule, (char *)); + +EXTDECLPROC(int, PyObject_SetAttrString, (PyObject *, char *, PyObject *)); +EXTDECLPROC(PyObject *, PyList_New, (int)); +EXTDECLPROC(int, PyList_Append, (PyObject *, PyObject *)); +/* Create a new value based on a format string similar to those accepted by the PyArg_Parse*() */ +EXTDECLPROC(PyObject *, Py_BuildValue, (char *, ...)); +/* Create a Unicode object from the char buffer. The bytes will be interpreted as being UTF-8 encoded. */ +EXTDECLPROC(PyObject *, PyUnicode_FromString, (const char *)); +EXTDECLPROC(PyObject *, PyObject_CallFunction, (PyObject *, char *, ...)); +EXTDECLPROC(PyObject *, PyObject_CallFunctionObjArgs, (PyObject *, ...)); +EXTDECLPROC(PyObject *, PyModule_GetDict, (PyObject *)); +EXTDECLPROC(PyObject *, PyDict_GetItemString, (PyObject *, char *)); +EXTDECLPROC(void, PyErr_Clear, (void) ); +EXTDECLPROC(PyObject *, PyErr_Occurred, (void) ); +EXTDECLPROC(void, PyErr_Print, (void) ); +EXTDECLPROC(void, PySys_AddWarnOption, (wchar_t *)); +/* Return a C long representation of the contents of pylong. */ +EXTDECLPROC(long, PyLong_AsLong, (PyObject *) ); + +EXTDECLPROC(int, PySys_SetObject, (char *, PyObject *)); + +/* + * Used to convert argv to wchar_t on Linux/OS X + * On Python 3.0-3.4, this function was called _Py_char2wchar + */ +EXTDECLPROC(wchar_t *, Py_DecodeLocale, (char *, size_t *)); +EXTDECLPROC(void, PyMem_RawFree, (void *)); + +/* Used to add PYZ to sys.path */ +EXTDECLPROC(PyObject *, PySys_GetObject, (const char *)); +EXTDECLPROC(PyObject *, PyUnicode_FromFormat, (const char *, ...)); +EXTDECLPROC(PyObject *, PyUnicode_DecodeFSDefault, (const char *)); +EXTDECLPROC(PyObject *, PyUnicode_Decode, + (const char *, size_t, const char *, const char *)); /* Py_ssize_t */ + +/* Used to load and execute marshalled code objects */ +EXTDECLPROC(PyObject *, PyEval_EvalCode, (PyObject *, PyObject *, PyObject *)); +EXTDECLPROC(PyObject *, PyMarshal_ReadObjectFromString, (const char *, size_t)); /* Py_ssize_t */ + +/* Used to get traceback information while launching run scripts */ +EXTDECLPROC(void, PyErr_Fetch, (PyObject **, PyObject **, PyObject **)); +EXTDECLPROC(void, PyErr_Restore, (PyObject *, PyObject *, PyObject *)); +EXTDECLPROC(PyObject *, PyObject_Str, (PyObject *)); +EXTDECLPROC(PyObject *, PyObject_GetAttrString, (PyObject *, const char *)); +EXTDECLPROC(const char *, PyUnicode_AsUTF8, (PyObject *)); + +/* + * Macros for reference counting through exported functions + * (that is: without binding to the binary structure of a PyObject. + * These rely on the Py_IncRef/Py_DecRef API functions on Pyhton 2.4+. + * + * Python versions before 2.4 do not export IncRef/DecRef as a binary API, + * but only as macros in header files. Since we support Python 2.4+ we do not + * need to provide an emulated incref/decref as it was with older Python + * versions. + * + * We do not want to depend on Python.h for many reasons (including the fact + * that we would like to have a single binary for all Python versions). + */ + +#define Py_XINCREF(o) PI_Py_IncRef(o) +#define Py_XDECREF(o) PI_Py_DecRef(o) +#define Py_DECREF(o) Py_XDECREF(o) +#define Py_INCREF(o) Py_XINCREF(o) + +/* Macros to declare and get Python entry points in the C file. + * Typedefs '__PROC__...' have been done above + * + * GETPROC_RENAMED is to support Python APIs that are simply renamed. We use + * the new name, and when loading an old Python lib, load the old symbol into the + * new name. + */ +#ifdef _WIN32 + + #define DECLPROC(name) \ + __PROC__ ## name PI_ ## name = NULL; + #define GETPROCOPT(dll, name, sym) \ + PI_ ## name = (__PROC__ ## name)GetProcAddress (dll, #sym) + #define GETPROC(dll, name) \ + GETPROCOPT(dll, name, name); \ + if (!PI_ ## name) { \ + FATAL_WINERROR("GetProcAddress", "Failed to get address for " #name "\n");\ + return -1; \ + } + #define GETPROC_RENAMED(dll, name, sym) \ + GETPROCOPT(dll, name, sym); \ + if (!PI_ ## name) { \ + FATAL_WINERROR("GetProcAddress", "Failed to get address for " #sym "\n");\ + return -1; \ + } + #define DECLVAR(name) \ + __VAR__ ## name * PI_ ## name = NULL; + #define GETVAR(dll, name) \ + PI_ ## name = (__VAR__ ## name *)GetProcAddress (dll, #name); \ + if (!PI_ ## name) { \ + FATAL_WINERROR("GetProcAddress", "Failed to get address for " #name "\n");\ + return -1; \ + } + +#else /* ifdef _WIN32 */ + + #define DECLPROC(name) \ + __PROC__ ## name PI_ ## name = NULL; + #define GETPROCOPT(dll, name, sym) \ + PI_ ## name = (__PROC__ ## name)dlsym (dll, #sym) + #define GETPROC(dll, name) \ + GETPROCOPT(dll, name, name); \ + if (!PI_ ## name) { \ + FATALERROR ("Cannot dlsym for " #name "\n"); \ + return -1; \ + } + #define GETPROC_RENAMED(dll, name, sym) \ + GETPROCOPT(dll, name, sym); \ + if (!PI_ ## name) { \ + FATALERROR ("Cannot dlsym for " #sym "\n"); \ + return -1; \ + } + #define DECLVAR(name) \ + __VAR__ ## name * PI_ ## name = NULL; + #define GETVAR(dll, name) \ + PI_ ## name = (__VAR__ ## name *)dlsym(dll, #name); \ + if (!PI_ ## name) { \ + FATALERROR ("Cannot dlsym for " #name "\n"); \ + return -1; \ + } + +#endif /* WIN32 */ + +int pyi_python_map_names(HMODULE dll, int pyvers); + +#endif /* PYI_PYTHON_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_pythonlib.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_pythonlib.c new file mode 100644 index 0000000000000000000000000000000000000000..e5ed36ceb2916b6eb1fc9fc923662f5a45e83b58 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_pythonlib.c @@ -0,0 +1,723 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Functions to load, initialize and launch Python. + */ +/* size of buffer to store the name of the Python DLL library */ +#define DLLNAME_LEN (64) + +#ifdef _WIN32 + #include /* HMODULE */ + #include /* O_BINARY */ + #include /* _setmode */ +#else + #include /* dlerror */ + #include /* mbstowcs */ +#endif /* ifdef _WIN32 */ +#include /* ptrdiff_t */ +#include +#include +#include /* setlocale */ + +/* PyInstaller headers. */ +#include "pyi_pythonlib.h" +#include "pyi_global.h" +#include "pyi_path.h" +#include "pyi_archive.h" +#include "pyi_utils.h" +#include "pyi_python.h" +#include "pyi_win32_utils.h" + +/* + * Load the Python DLL, and get all of the necessary entry points + */ +int +pyi_pylib_load(ARCHIVE_STATUS *status) +{ + dylib_t dll; + char dllpath[PATH_MAX]; + char dllname[DLLNAME_LEN]; + size_t len; + +/* + * On AIX Append the name of shared object library path might be an archive. + * In that case, modify the name to make it look like: + * libpython3.6.a(libpython3.6.so) + * Shared object names ending with .so may be used asis. + */ +#ifdef AIX + /* + * Determine if shared lib is in libpython?.?.so or + * libpython?.?.a(libpython?.?.so) format + */ + char *p; + if ((p = strrchr(status->cookie.pylibname, '.')) != NULL && strcmp(p, ".a") == 0) { + /* + * On AIX 'ar' archives are used for both static and shared object. + * To load a shared object from a library, it should be loaded like this: + * dlopen("libpythonX.Y.a(libpythonX.Y.so)", RTLD_MEMBER) + */ + uint32_t pyvers_major; + uint32_t pyvers_minor; + + pyvers_major = pyvers / 10; + pyvers_minor = pyvers % 10; + + len = snprintf(dllname, DLLNAME_LEN, + "libpython%01d.%01d.a(libpython%01d.%01d.so)", + pyvers_major, pyvers_minor, pyvers_major, pyvers_minor); + } + else { + len = snprintf(dllname, DLLNAME_LEN, "%s", status->cookie.pylibname); + } +#else + len = snprintf(dllname, DLLNAME_LEN, "%s", status->cookie.pylibname); +#endif + + if (len >= DLLNAME_LEN) { + FATALERROR("Reported length (%d) of DLL name (%s) length exceeds buffer[%d] space\n", + len, status->cookie.pylibname, DLLNAME_LEN); + return -1; + } + +#ifdef _WIN32 + /* + * If ucrtbase.dll exists in temppath, load it proactively before Python + * library loading to avoid Python library loading failure (unresolved + * symbol errors) on systems with Universal CRT update not installed. + */ + if (status->has_temp_directory) { + char ucrtpath[PATH_MAX]; + if (pyi_path_join(ucrtpath, + status->temppath, "ucrtbase.dll") == NULL) { + FATALERROR("Path of ucrtbase.dll (%s) length exceeds " + "buffer[%d] space\n", status->temppath, PATH_MAX); + }; + if (pyi_path_exists(ucrtpath)) { + VS("LOADER: ucrtbase.dll found: %s\n", ucrtpath); + pyi_utils_dlopen(ucrtpath); + } + } +#endif + + /* + * Look for Python library in homepath or temppath. + * It depends on the value of mainpath. + */ + if (pyi_path_join(dllpath, status->mainpath, dllname) == NULL) { + FATALERROR("Path of DLL (%s) length exceeds buffer[%d] space\n", + status->mainpath, PATH_MAX); + }; + + VS("LOADER: Python library: %s\n", dllpath); + + /* Load the DLL */ + dll = pyi_utils_dlopen(dllpath); + + /* Check success of loading Python library. */ + if (dll == 0) { +#ifdef _WIN32 + FATAL_WINERROR("LoadLibrary", "Error loading Python DLL '%s'.\n", dllpath); +#else + FATALERROR("Error loading Python lib '%s': dlopen: %s\n", + dllpath, dlerror()); +#endif + return -1; + } + + return pyi_python_map_names(dll, pyvers); +} + +/* + * Use this from a dll instead of pyi_pylib_load(). + * It will attach to an existing pythonXX.dll or load one if needed. + */ +int +pyi_pylib_attach(ARCHIVE_STATUS *status, int *loadedNew) +{ +#ifdef _WIN32 + HMODULE dll; + char nm[PATH_MAX + 1]; + int ret = 0; + /* Get python's name */ + sprintf(nm, "python%02d.dll", pyvers); + + /* See if it's loaded */ + dll = GetModuleHandleA(nm); + + if (dll == 0) { + *loadedNew = 1; + return pyi_pylib_load(status); + } + ret = pyi_python_map_names(dll, pyvers); + *loadedNew = 0; + return ret; +#endif /* ifdef _WIN32 */ + return 0; +} + +/* + * A toc entry of type 'o' holds runtime options + * toc->name is the arg + * this is so you can freeze in command line args to Python + */ +static int +pyi_pylib_set_runtime_opts(ARCHIVE_STATUS *status) +{ + int unbuffered = 0; + TOC *ptoc = status->tocbuff; + wchar_t wchar_tmp[PATH_MAX + 1]; + + /* + * Startup flags - default values. 1 means enabled, 0 disabled. + */ + /* Suppress 'import site'. */ + *PI_Py_NoSiteFlag = 1; + /* Needed by getpath.c from Python. */ + *PI_Py_FrozenFlag = 1; + /* Suppress writing bytecode files (*.py[co]) */ + *PI_Py_DontWriteBytecodeFlag = 1; + /* Do not try to find any packages in user's site directory. */ + *PI_Py_NoUserSiteDirectory = 1; + /* This flag ensures PYTHONPATH and PYTHONHOME are ignored by Python. */ + *PI_Py_IgnoreEnvironmentFlag = 1; + /* Disable verbose imports by default. */ + *PI_Py_VerboseFlag = 0; + + /* Override some runtime options by custom values from PKG archive. + * User is allowed to changes these options. */ + for (; ptoc < status->tocend; ptoc = pyi_arch_increment_toc_ptr(status, ptoc)) { + if (ptoc->typcd == ARCHIVE_ITEM_RUNTIME_OPTION) { + if (0 == strncmp(ptoc->name, "pyi-", 4)) { + VS("LOADER: Bootloader option: %s\n", ptoc->name); + continue; /* Not handled here - use pyi_arch_get_option(status, ...) */ + } + VS("LOADER: Runtime option: %s\n", ptoc->name); + + switch (ptoc->name[0]) { + case 'v': + *PI_Py_VerboseFlag = 1; + break; + case 'u': + unbuffered = 1; + break; + case 'W': + /* TODO: what encoding is ptoc->name? May not be important */ + /* as all known Wflags are ASCII. */ + if ((size_t)-1 == mbstowcs(wchar_tmp, &ptoc->name[2], PATH_MAX)) { + FATALERROR("Failed to convert Wflag %s using mbstowcs " + "(invalid multibyte string)\n", &ptoc->name[2]); + return -1; + } + PI_PySys_AddWarnOption(wchar_tmp); + break; + case 'O': + *PI_Py_OptimizeFlag = 1; + break; + } + } + } + + if (unbuffered) { +#ifdef _WIN32 + _setmode(fileno(stdin), _O_BINARY); + _setmode(fileno(stdout), _O_BINARY); +#endif + fflush(stdout); + fflush(stderr); + + setbuf(stdin, (char *)NULL); + setbuf(stdout, (char *)NULL); + setbuf(stderr, (char *)NULL); + + /* Enable unbuffered mode via Py_UnbufferedStdioFlag */ + *PI_Py_UnbufferedStdioFlag = 1; + } + return 0; +} + +void +pyi_free_wargv(wchar_t ** wargv) +{ + wchar_t ** arg = wargv; + + while (arg[0]) { +#ifdef _WIN32 + // allocated using `malloc` in pyi_win32_wargv_from_utf8 + free(arg[0]); +#else + // allocated using Py_DecodeLocale in pyi_wargv_from_argv + PI_PyMem_RawFree(arg[0]); +#endif + arg++; + } + free(wargv); +} + +/* Convert argv to wchar_t for Python 3. Based on code from Python's main(). + * + * Uses 'Py_DecodeLocale' ('_Py_char2wchar' in 3.0-3.4) function from python lib, + * so don't call until after python lib is loaded. + * + * Returns NULL on failure. Caller is responsible for freeing + * both argv and argv[0..argc] + */ + +wchar_t ** +pyi_wargv_from_argv(int argc, char ** argv) +{ + wchar_t ** wargv; + char *oldloc; + int i; + + oldloc = strdup(setlocale(LC_CTYPE, NULL)); + + if (!oldloc) { + FATALERROR("out of memory\n"); + return NULL; + } + + wargv = (wchar_t **)calloc(sizeof(wchar_t*) * (argc + 1), 1); + + if (!wargv) { + FATALERROR("out of memory\n"); + return NULL; + } + + setlocale(LC_CTYPE, ""); + + for (i = 0; i < argc; i++) { + + wargv[i] = PI_Py_DecodeLocale(argv[i], NULL); + + if (!wargv[i]) { + pyi_free_wargv(wargv); + free(oldloc); + FATALERROR("Fatal error: " + "unable to decode the command line argument #%i\n", + i + 1); + return NULL; + } + } + wargv[argc] = NULL; + + setlocale(LC_CTYPE, oldloc); + free(oldloc); + return wargv; +} + +/* + * Set Python list sys.argv from *argv/argc. (Command-line options). + * sys.argv[0] should be full absolute path to the executable (Derived from + * status->archivename). + */ +static int +pyi_pylib_set_sys_argv(ARCHIVE_STATUS *status) +{ + wchar_t ** wargv; + + VS("LOADER: Setting sys.argv\n"); + +#ifdef _WIN32 + /* Convert UTF-8 argv back to wargv */ + wargv = pyi_win32_wargv_from_utf8(status->argc, status->argv); +#else + /* Convert argv to wargv using Python's Py_DecodeLocale */ + wargv = pyi_wargv_from_argv(status->argc, status->argv); +#endif + + if (wargv) { + /* last parameter '0' to PySys_SetArgv means do not update sys.path. */ + PI_PySys_SetArgvEx(status->argc, wargv, 0); + pyi_free_wargv(wargv); + } + else { + FATALERROR("Failed to convert argv to wchar_t\n"); + return -1; + }; + return 0; +} + +/* Convenience function to convert current locale to wchar_t on Linux/OS X + * and convert UTF-8 to wchar_t on Windows. + * + * To be called when converting internal PyI strings to wchar_t for + * Python 3's consumption + */ +wchar_t * +pyi_locale_char2wchar(wchar_t * dst, char * src, size_t len) +{ +#ifdef _WIN32 + return pyi_win32_utils_from_utf8(dst, src, len); +#else + wchar_t * buffer; + saved_locale = strdup(setlocale(LC_CTYPE, NULL)); + setlocale(LC_CTYPE, ""); + + buffer = PI_Py_DecodeLocale(src, &len); + + setlocale(LC_CTYPE, saved_locale); + + if (!buffer) { + return NULL; + } + wcsncpy(dst, buffer, len); + PI_PyMem_RawFree(buffer); + return dst; +#endif /* ifdef _WIN32 */ +} + +/* + * Start python - return 0 on success + */ +int +pyi_pylib_start_python(ARCHIVE_STATUS *status) +{ + /* Set sys.path, sys.prefix, and sys.executable so dynamic libs will load. + * + * The Python APIs we use here (Py_SetProgramName, Py_SetPythonHome) + * specify their argument should be a "string in static storage". + * That is, the APIs use the string pointer as given and will neither copy + * its contents nor free its memory. + * + * NOTE: Statics are zero-initialized. */ + #define MAX_PYPATH_SIZE (3 * PATH_MAX + 32) + static char pypath[MAX_PYPATH_SIZE]; + + /* Wide string forms of the above, for Python 3. */ + static wchar_t pypath_w[MAX_PYPATH_SIZE]; + static wchar_t pyhome_w[PATH_MAX + 1]; + static wchar_t progname_w[PATH_MAX + 1]; + + /* Decode using current locale */ + if (!pyi_locale_char2wchar(progname_w, status->archivename, PATH_MAX)) { + FATALERROR("Failed to convert progname to wchar_t\n"); + return -1; + } + /* Py_SetProgramName() should be called before Py_SetPath(). */ + PI_Py_SetProgramName(progname_w); + + VS("LOADER: Manipulating environment (sys.path, sys.prefix)\n"); + + /* Set sys.prefix and sys.exec_prefix using Py_SetPythonHome */ + /* Decode using current locale */ + if (!pyi_locale_char2wchar(pyhome_w, status->mainpath, PATH_MAX)) { + FATALERROR("Failed to convert pyhome to wchar_t\n"); + return -1; + } + VS("LOADER: sys.prefix is %s\n", status->mainpath); + PI_Py_SetPythonHome(pyhome_w); + + /* Set sys.path */ + /* sys.path = [mainpath/base_library.zip, mainpath/lib-dynload, mainpath] */ + if (snprintf(pypath, MAX_PYPATH_SIZE, "%s%c%s" "%c" "%s%c%s" "%c" "%s", + status->mainpath, PYI_SEP, "base_library.zip", + PYI_PATHSEP, + status->mainpath, PYI_SEP, "lib-dynload", + PYI_PATHSEP, + status->mainpath) + >= MAX_PYPATH_SIZE) { + // This should never happen, since mainpath is < PATH_MAX and pypath is + // huge enough + FATALERROR("sys.path (based on %s) exceeds buffer[%d] space\n", + status->mainpath, MAX_PYPATH_SIZE); + return -1; + } + + /* + * We must set sys.path to have base_library.zip before + * calling Py_Initialize as it needs `encodings` and other modules. + */ + /* Decode using current locale */ + if (!pyi_locale_char2wchar(pypath_w, pypath, MAX_PYPATH_SIZE)) { + FATALERROR("Failed to convert pypath to wchar_t\n"); + return -1; + } + VS("LOADER: Pre-init sys.path is %s\n", pypath); +#ifdef _WIN32 + // Call GetPath first, so the static dllpath will be set as a side + // effect. Workaround for http://bugs.python.org/issue29778, see #2496. + // Due to another bug calling this on non-win32 with Python 3.6 causes + // memory corruption, see #2812 and + // https://bugs.python.org/issue31532. But the workaround is only + // needed for win32. + PI_Py_GetPath(); +#endif + PI_Py_SetPath(pypath_w); + + /* Start python. */ + VS("LOADER: Setting runtime options\n"); + pyi_pylib_set_runtime_opts(status); + + /* + * Py_Initialize() may rudely call abort(), and on Windows this triggers the error + * reporting service, which results in a dialog box that says "Close program", "Check + * for a solution", and also "Debug" if Visual Studio is installed. The dialog box + * makes it frustrating to run the test suite. + * + * For debug builds of the bootloader, disable the error reporting before calling + * Py_Initialize and enable it afterward. + */ + +#if defined(_WIN32) && defined(LAUNCH_DEBUG) + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); +#endif + + VS("LOADER: Initializing python\n"); + PI_Py_Initialize(); + +#if defined(_WIN32) && defined(LAUNCH_DEBUG) + SetErrorMode(0); +#endif + + /* + * Set sys.path list. + * Python's default sys.path is no good - it includes the working directory + * and the folder containing the executable. Replace sys.path with only + * the paths we want. + */ + VS("LOADER: Overriding Python's sys.path\n"); + VS("LOADER: Post-init sys.path is %s\n", pypath); + PI_PySys_SetPath(pypath_w); + + /* Setting sys.argv should be after Py_Initialize() call. */ + if (pyi_pylib_set_sys_argv(status)) { + return -1; + } + + /* Check for a python error */ + if (PI_PyErr_Occurred()) { + FATALERROR("Error detected starting Python VM."); + return -1; + } + + return 0; +} + +/* + * Import modules embedded in the archive - return 0 on success + */ +int +pyi_pylib_import_modules(ARCHIVE_STATUS *status) +{ + PyObject *marshal; + PyObject *marshaldict; + PyObject *loadfunc; + TOC *ptoc; + PyObject *co; + PyObject *mod; + PyObject *meipass_obj; + + VS("LOADER: setting sys._MEIPASS\n"); + + /* TODO extract function pyi_char_to_pyobject */ +#ifdef _WIN32 + meipass_obj = PI_PyUnicode_Decode(status->mainpath, + strlen(status->mainpath), + "utf-8", + "strict"); +#else + meipass_obj = PI_PyUnicode_DecodeFSDefault(status->mainpath); +#endif + + if (!meipass_obj) { + FATALERROR("Failed to get _MEIPASS as PyObject.\n"); + return -1; + } + + PI_PySys_SetObject("_MEIPASS", meipass_obj); + + VS("LOADER: importing modules from CArchive\n"); + + /* Get the Python function marshall.load + * Here we collect some reference to PyObject that we don't dereference + * Doesn't matter because the objects won't be going away anyway. + */ + marshal = PI_PyImport_ImportModule("marshal"); + marshaldict = PI_PyModule_GetDict(marshal); + loadfunc = PI_PyDict_GetItemString(marshaldict, "loads"); + + /* Iterate through toc looking for module entries (type 'm') + * this is normally just bootstrap stuff (archive and iu) + */ + ptoc = status->tocbuff; + + while (ptoc < status->tocend) { + if (ptoc->typcd == ARCHIVE_ITEM_PYMODULE || + ptoc->typcd == ARCHIVE_ITEM_PYPACKAGE) { + unsigned char *modbuf = pyi_arch_extract(status, ptoc); + + VS("LOADER: extracted %s\n", ptoc->name); + + /* .pyc/.pyo files have 8 bytes header. Skip it and load marshalled + * data form the right point. + */ + if (pyvers >= 37) { + /* Python >= 3.7 the header: size was changed to 16 bytes. */ + co = PI_PyObject_CallFunction(loadfunc, "y#", modbuf + 16, + ptoc->ulen - 16); + } + else { + /* It looks like from python 3.3 the header */ + /* size was changed to 12 bytes. */ + co = + PI_PyObject_CallFunction(loadfunc, "y#", modbuf + 12, + ptoc->ulen - 12); + }; + + if (co != NULL) { + VS("LOADER: callfunction returned...\n"); + mod = PI_PyImport_ExecCodeModule(ptoc->name, co); + } + else { + /* TODO callfunctions might return NULL - find yout why and foor what modules. */ + VS("LOADER: callfunction returned NULL"); + mod = NULL; + } + + /* Check for errors in loading */ + if (mod == NULL) { + FATALERROR("mod is NULL - %s", ptoc->name); + } + + if (PI_PyErr_Occurred()) { + PI_PyErr_Print(); + PI_PyErr_Clear(); + } + + free(modbuf); + } + ptoc = pyi_arch_increment_toc_ptr(status, ptoc); + } + + return 0; +} + +/* + * Install a zlib from a toc entry. + * + * Must be called after Py_Initialize (i.e. after pyi_pylib_start_python) + * + * The installation is done by adding an entry like + * absolute_path/dist/hello_world/hello_world?123456 + * to sys.path. The end number is the offset where the + * Python bootstrap code should read the zip data. + * Return non zero on failure. + * NB: This entry is removed from sys.path by the bootstrap scripts. + */ +int +pyi_pylib_install_zlib(ARCHIVE_STATUS *status, TOC *ptoc) +{ + int rc = 0; + uint64_t zlibpos = status->pkgstart + ptoc->pos; + PyObject * sys_path, *zlib_entry, *archivename_obj; + + /* Note that sys.path contains PyUnicode on py3. Ensure + * that filenames are encoded or decoded correctly. + */ +#ifdef _WIN32 + /* Decode UTF-8 to PyUnicode */ + archivename_obj = PI_PyUnicode_Decode(status->archivename, + strlen(status->archivename), + "utf-8", + "strict"); +#else + /* Decode locale-encoded filename to PyUnicode object using Python's + * preferred decoding method for filenames. + */ + archivename_obj = PI_PyUnicode_DecodeFSDefault(status->archivename); +#endif + zlib_entry = PI_PyUnicode_FromFormat("%U?%" PRIu64, archivename_obj, zlibpos); + PI_Py_DecRef(archivename_obj); + + sys_path = PI_PySys_GetObject("path"); + + if (NULL == sys_path) { + FATALERROR("Installing PYZ: Could not get sys.path\n"); + PI_Py_DecRef(zlib_entry); + return -1; + } + + rc = PI_PyList_Append(sys_path, zlib_entry); + + if (rc) { + FATALERROR("Failed to append to sys.path\n"); + } + + return rc; +} + +/* + * Install PYZ + * Return non zero on failure + */ +int +pyi_pylib_install_zlibs(ARCHIVE_STATUS *status) +{ + TOC * ptoc; + + VS("LOADER: Installing PYZ archive with Python modules.\n"); + + /* Iterate through toc looking for zlibs (PYZ, type 'z') */ + ptoc = status->tocbuff; + + while (ptoc < status->tocend) { + if (ptoc->typcd == ARCHIVE_ITEM_PYZ) { + VS("LOADER: PYZ archive: %s\n", ptoc->name); + pyi_pylib_install_zlib(status, ptoc); + } + + ptoc = pyi_arch_increment_toc_ptr(status, ptoc); + } + return 0; +} + +void +pyi_pylib_finalize(ARCHIVE_STATUS *status) +{ + /* + * Call this function only if Python library was initialized. + * + * Otherwise it should be NULL pointer. If Python library is not properly + * loaded then calling this function might cause some segmentation faults. + */ + if (status->is_pylib_loaded == true) { + #ifndef WINDOWED + /* + * We need to manually flush the buffers because otherwise there can be errors. + * The native python interpreter flushes buffers before calling Py_Finalize, + * so we need to manually do the same. See isse #4908. + */ + + VS("LOADER: Manually flushing stdout and stderr\n"); + + /* sys.stdout.flush() */ + PI_PyRun_SimpleString( + "import sys; sys.stdout.flush(); \ + (sys.__stdout__.flush if sys.__stdout__ \ + is not sys.stdout else (lambda: None))()"); + + /* sys.stderr.flush() */ + PI_PyRun_SimpleString( + "import sys; sys.stderr.flush(); \ + (sys.__stderr__.flush if sys.__stderr__ \ + is not sys.stderr else (lambda: None))()"); + + #endif + + /* Finalize the interpreter. This function call calls all of the atexit functions. */ + VS("LOADER: Cleaning up Python interpreter.\n"); + PI_Py_Finalize(); + } +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_pythonlib.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_pythonlib.h new file mode 100644 index 0000000000000000000000000000000000000000..e3c57a4ed41bb88935f687727f538b81bd6c5a3c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_pythonlib.h @@ -0,0 +1,32 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Functions to load, initialize and launch Python. + */ + +#ifndef PYI_PYTHONLIB_H +#define PYI_PYTHONLIB_H + +#include "pyi_archive.h" + +int pyi_pylib_attach(ARCHIVE_STATUS *status, int *loadedNew); +int pyi_pylib_load(ARCHIVE_STATUS *status); /* note - pyi_pylib_attach will call this if not already loaded */ +int pyi_pylib_start_python(ARCHIVE_STATUS *status); +int pyi_pylib_import_modules(ARCHIVE_STATUS *status); +int pyi_pylib_install_zlibs(ARCHIVE_STATUS *status); +int pyi_pylib_run_scripts(ARCHIVE_STATUS *status); + +void pyi_pylib_finalize(ARCHIVE_STATUS *status); + +#endif /* PYI_PYTHONLIB_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_utils.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..8c0fcc1a7b8d81bd796f838a49d2de1e77ad4e9f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_utils.c @@ -0,0 +1,1552 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Portable wrapper for some utility functions like getenv/setenv, + * file path manipulation and other shared data types or functions. + */ + +#ifdef _WIN32 + #include + #include /* _rmdir */ + #include /* _finddata_t */ + #include /* getpid */ + #include /* signal */ +#else + #include +/* + * On AIX RTLD_MEMBER flag is only visible when _ALL_SOURCE flag is defined. + * + * There are quite a few issues with xlC compiler. GCC is much better, + * Without flag _ALL_SOURCE gcc get stuck on the RTLD_MEMBER flax when + * compiling the bootloader. + * This fix was tested wigh gcc on AIX6.1. + */ + #if defined(AIX) && !defined(_ALL_SOURCE) + #define _ALL_SOURCE + #include + #undef _ALL_SOURCE + #else + #include + #endif + #include /* kill, */ + #include + #include /* rmdir, unlink, mkdtemp */ +#endif /* ifdef _WIN32 */ +#ifndef SIGCLD +#define SIGCLD SIGCHLD /* not defined on OS X */ +#endif +#ifndef sighandler_t +typedef void (*sighandler_t)(int); +#endif +#include +#include /* ptrdiff_t */ +#include /* FILE */ +#include +#include +#include /* struct stat */ +#include /* wchar_t */ +#if defined(__APPLE__) && defined(WINDOWED) + #include /* AppleEventsT */ + #include /* GetProcessForPID, etc */ + /* Not declared in modern headers but exists in Carbon libs since time immemorial + * See: https://applescriptlibrary.files.wordpress.com/2013/11/apple-events-programming-guide.pdf */ + extern Boolean ConvertEventRefToEventRecord(EventRef inEvent, EventRecord *outEvent); +#endif + +/* + * Function 'mkdtemp' (make temporary directory) is missing on some *nix platforms: + * - On Solaris function 'mkdtemp' is missing. + * - On AIX 5.2 function 'mkdtemp' is missing. It is there in version 6.1 but we don't know + * the runtime platform at compile time, so we always include our own implementation on AIX. + */ +#if defined(SUNOS) || defined(AIX) || defined(HPUX) + #if !defined(HAVE_MKDTEMP) + #include "mkdtemp.h" + #endif +#endif + +/* PyInstaller headers. */ +#include "pyi_global.h" +#include "pyi_path.h" +#include "pyi_archive.h" +#include "pyi_utils.h" +#include "pyi_win32_utils.h" + +/* + * global variables that are used to copy argc/argv, so that PyIstaller can manipulate them + * if need be. One case in which the incoming argc/argv is manipulated is in the case of + * Apple/Windowed, where we watch for AppleEvents in order to add files to the command line. + * (this is argv_emulation). These variables must be of file global scope to be able to + * be accessed inside of the AppleEvents handlers. + */ +static char **argv_pyi = NULL; +static int argc_pyi = 0; + +/* + * Watch for OpenDocument AppleEvents and add the files passed in to the + * sys.argv command line on the Python side. + * + * This allows on Mac OS X to open files when a file is dragged and dropped + * on the App icon in the OS X dock. + */ +#if defined(__APPLE__) && defined(WINDOWED) +static void process_apple_events(Boolean); +#endif + + +// some platforms do not provide strnlen +#ifndef HAVE_STRNLEN +size_t +strnlen(const char *str, size_t n) +{ + const char *stop = (char *)memchr(str, '\0', n); + return stop ? stop - str : n; +} +#endif + +// some platforms do not provide strndup +#ifndef HAVE_STRNDUP +char * +strndup(const char * str, size_t n) +{ + char *ret = NULL; + size_t len = strnlen(str, n); + ret = (char *)malloc(len + 1); + if (ret == NULL) return NULL; + ret[len] = '\0'; + return (char *)memcpy(ret, str, len); +} +#endif + + +char * +pyi_strjoin(const char *first, const char *sep, const char *second){ + /* join first and second string, using sep as separator. + * any of them may be either a null-terminated string or NULL. + * sep will be only used if first and second string are not empty. + * returns a null-terminated string which the caller is responsible + * for freeing. Returns NULL if memory could not be allocated. + */ + size_t first_len, sep_len, second_len; + char *result; + first_len = first ? strlen(first) : 0; + sep_len = sep ? strlen(sep) : 0; + second_len = second ? strlen(second) : 0; + result = malloc(first_len + sep_len + second_len + 1); + if (!result) { + return NULL; + } + *result = '\0'; + if (first_len) { + strcat(result, first); + } + if (sep_len && first_len && second_len) { + strcat(result, sep); + } + if (second_len) { + strcat(result, second); + } + return result; +} + +/* Return string copy of environment variable. */ +char * +pyi_getenv(const char *variable) +{ + char *env = NULL; + +#ifdef _WIN32 + wchar_t * wenv = NULL; + wchar_t * wvar = NULL; + wchar_t buf1[PATH_MAX], buf2[PATH_MAX]; + DWORD rc; + + wvar = pyi_win32_utils_from_utf8(NULL, variable, 0); + rc = GetEnvironmentVariableW(wvar, buf1, sizeof(buf1)); + + if (rc > 0) { + wenv = buf1; + /* Expand environment variables like %VAR% in value. */ + rc = ExpandEnvironmentStringsW(wenv, buf2, sizeof(buf2)); + + if (rc > 0) { + wenv = buf1; + } + } + + if (wenv) { + env = pyi_win32_utils_to_utf8(NULL, wenv, 0); + } +#else /* + * ifdef _WIN32 + * Standard POSIX function. + */ + env = getenv(variable); +#endif /* ifdef _WIN32 */ + + /* If the Python program we are about to run invokes another PyInstaller + * one-file program as subprocess, this subprocess must not be fooled into + * thinking that it is already unpacked. Therefore, PyInstaller deletes + * the _MEIPASS2 variable from the environment in pyi_main(). + * + * However, on some platforms (e.g. AIX) the Python function 'os.unsetenv()' + * does not always exist. In these cases we cannot delete the _MEIPASS2 + * environment variable from Python but only set it to the empty string. + * The code below takes into account that a variable may exist while its + * value is only the empty string. + * + * Return copy of string to avoid modification of the process environment. + */ + return (env && env[0]) ? strdup(env) : NULL; +} + +/* Set environment variable. */ +int +pyi_setenv(const char *variable, const char *value) +{ + int rc; + +#ifdef _WIN32 + wchar_t * wvar, *wval; + + wvar = pyi_win32_utils_from_utf8(NULL, variable, 0); + wval = pyi_win32_utils_from_utf8(NULL, value, 0); + + // Not sure why, but SetEnvironmentVariableW() didn't work with _wtempnam() + // Replaced it with _wputenv_s() + rc = _wputenv_s(wvar, wval); + + free(wvar); + free(wval); +#else + rc = setenv(variable, value, true); +#endif + return rc; +} + +/* Unset environment variable. */ +int +pyi_unsetenv(const char *variable) +{ + int rc; + +#ifdef _WIN32 + wchar_t * wvar; + wvar = pyi_win32_utils_from_utf8(NULL, variable, 0); + rc = SetEnvironmentVariableW(wvar, NULL); + free(wvar); +#else /* _WIN32 */ + #if HAVE_UNSETENV + rc = unsetenv(variable); + #else /* HAVE_UNSETENV */ + rc = setenv(variable, "", true); + #endif /* HAVE_UNSETENV */ +#endif /* _WIN32 */ + return rc; +} + +#ifdef _WIN32 + +/* Resolve the runtime tmpdir path and build nested directories */ +wchar_t +*pyi_build_temp_folder(char *runtime_tmpdir) +{ + wchar_t *wruntime_tmpdir; + wchar_t wruntime_tmpdir_expanded[PATH_MAX]; + wchar_t *wruntime_tmpdir_abspath; + wchar_t *cursor; + wchar_t path_builder[PATH_MAX]; + DWORD rc; + // Expand environment variables like %LOCALAPPDATA% + wruntime_tmpdir = pyi_win32_utils_from_utf8(NULL, runtime_tmpdir, 0); + if (!wruntime_tmpdir) { + FATALERROR("LOADER: Failed to convert runtime-tmpdir to a wide string.\n"); + return NULL; + } + rc = ExpandEnvironmentStringsW(wruntime_tmpdir, wruntime_tmpdir_expanded, + PATH_MAX); + free(wruntime_tmpdir); + if (!rc) { + FATALERROR("LOADER: Failed to expand environment variables in the runtime-tmpdir.\n"); + return NULL; + } + // Get the absolute path + wruntime_tmpdir_abspath = _wfullpath(NULL, wruntime_tmpdir_expanded, + PATH_MAX); + if (!wruntime_tmpdir_abspath) { + FATALERROR("LOADER: Failed to obtain the absolute path of the runtime-tmpdir.\n"); + return NULL; + } + VS("LOADER: absolute runtime tmpdir is %ls\n", wruntime_tmpdir_abspath); + // Create the directory path if it does not yet already exist (e.g. + // %AppData%\NewFolder\NestedFolder) + ZeroMemory(path_builder, PATH_MAX * sizeof(wchar_t)); + cursor = wcschr(wruntime_tmpdir_abspath, L'\\'); + while(cursor != NULL) { + wcsncpy(path_builder, wruntime_tmpdir_abspath, + cursor - wruntime_tmpdir_abspath + 1); + CreateDirectoryW(path_builder, NULL); + // We expect ERROR_ALREADY_EXISTS, ERROR_ACCESS_DENIED (if try to + // create a drive when running as an admin), etc... + cursor = wcschr(++cursor, L'\\'); + } + // May not have a string terminated with \, so run CreateDirectoryW one + // last time to handle that case + CreateDirectoryW(wruntime_tmpdir_abspath, NULL); + return wruntime_tmpdir_abspath; +} + +/* TODO rename fuction and revisit */ +int +pyi_get_temp_path(char *buffer, char *runtime_tmpdir) +{ + int i; + wchar_t *wchar_ret; + wchar_t prefix[16]; + wchar_t wchar_buffer[PATH_MAX]; + char *original_tmpdir; + wchar_t *wruntime_tmpdir_abspath; + DWORD rc; + + if (runtime_tmpdir != NULL) { + /* + * Get original TMP environment variable so it can be restored + * after this is done. + */ + original_tmpdir = pyi_getenv("TMP"); + /* + * Set TMP to runtime_tmpdir for _wtempnam() later + */ + wruntime_tmpdir_abspath = pyi_build_temp_folder(runtime_tmpdir); + if (!wruntime_tmpdir_abspath) { + return 0; + } + // Store in the TMP environment variable + rc = _wputenv_s(L"TMP", wruntime_tmpdir_abspath); + free(wruntime_tmpdir_abspath); + if (rc) { + FATALERROR("LOADER: Failed to set the TMP environment variable.\n"); + return 0; + } + VS("LOADER: Successfully resolved the specified runtime-tmpdir\n"); + } + + GetTempPathW(PATH_MAX, wchar_buffer); + + swprintf(prefix, 16, L"_MEI%d", getpid()); + + /* + * Windows does not have a race-free function to create a temporary + * directory. Thus, we rely on _tempnam, and simply try several times + * to avoid stupid race conditions. + */ + for (i = 0; i < 5; i++) { + /* TODO use race-free fuction - if any exists? */ + wchar_ret = _wtempnam(wchar_buffer, prefix); + + if (pyi_win32_mkdir(wchar_ret) == 0) { + pyi_win32_utils_to_utf8(buffer, wchar_ret, PATH_MAX); + free(wchar_ret); + if (runtime_tmpdir != NULL) { + /* + * Restore TMP to what it was + */ + if (original_tmpdir != NULL) { + pyi_setenv("TMP", original_tmpdir); + free(original_tmpdir); + } else { + pyi_unsetenv("TMP"); + } + } + return 1; + } + free(wchar_ret); + } + if (runtime_tmpdir != NULL) { + /* + * Restore TMP to what it was + */ + if (original_tmpdir != NULL) { + pyi_setenv("TMP", original_tmpdir); + free(original_tmpdir); + } else { + pyi_unsetenv("TMP"); + } + } + return 0; +} + +#else /* ifdef _WIN32 */ + +/* TODO Is this really necessary to test for temp path? Why not just use mkdtemp()? */ +int +pyi_test_temp_path(char *buff) +{ + /* + * If path does not end with directory separator - append it there. + * On OSX the value from $TMPDIR ends with '/'. + */ + if (buff[strlen(buff) - 1] != PYI_SEP) { + strcat(buff, PYI_SEPSTR); + } + strcat(buff, "_MEIXXXXXX"); + + if (mkdtemp(buff)) { + return 1; + } + return 0; +} + +/* TODO merge this function with windows version. */ +static int +pyi_get_temp_path(char *buff, char *runtime_tmpdir) +{ + if (runtime_tmpdir != NULL) { + strcpy(buff, runtime_tmpdir); + if (pyi_test_temp_path(buff)) + return 1; + } else { + /* On OSX the variable TMPDIR is usually defined. */ + static const char *envname[] = { + "TMPDIR", "TEMP", "TMP", 0 + }; + static const char *dirname[] = { + "/tmp", "/var/tmp", "/usr/tmp", 0 + }; + int i; + char *p; + + for (i = 0; envname[i]; i++) { + p = pyi_getenv(envname[i]); + + if (p) { + strcpy(buff, p); + + if (pyi_test_temp_path(buff)) { + return 1; + } + } + } + + for (i = 0; dirname[i]; i++) { + strcpy(buff, dirname[i]); + + if (pyi_test_temp_path(buff)) { + return 1; + } + } + } + return 0; +} + +#endif /* ifdef _WIN32 */ + +/* + * Creates a temporany directory if it doesn't exists + * and properly sets the ARCHIVE_STATUS members. + */ +int +pyi_create_temp_path(ARCHIVE_STATUS *status) +{ + char *runtime_tmpdir = NULL; + + if (status->has_temp_directory != true) { + runtime_tmpdir = pyi_arch_get_option(status, "pyi-runtime-tmpdir"); + if(runtime_tmpdir != NULL) { + VS("LOADER: Found runtime-tmpdir %s\n", runtime_tmpdir); + } + + if (!pyi_get_temp_path(status->temppath, runtime_tmpdir)) { + FATALERROR("INTERNAL ERROR: cannot create temporary directory!\n"); + return -1; + } + /* Set flag that temp directory is created and available. */ + status->has_temp_directory = true; + } + return 0; +} + +/* TODO merge unix/win versions of remove_one() and pyi_remove_temp_path() */ +#ifdef _WIN32 +static void +remove_one(wchar_t *wfnm, size_t pos, struct _wfinddata_t wfinfo) +{ + char fnm[PATH_MAX + 1]; + + if (wcscmp(wfinfo.name, L".") == 0 || wcscmp(wfinfo.name, L"..") == 0) { + return; + } + wfnm[pos] = PYI_NULLCHAR; + wcscat(wfnm, wfinfo.name); + + if (wfinfo.attrib & _A_SUBDIR) { + /* Use recursion to remove subdirectories. */ + pyi_win32_utils_to_utf8(fnm, wfnm, PATH_MAX); + pyi_remove_temp_path(fnm); + } + else if (_wremove(wfnm)) { + /* HACK: Possible concurrency issue... spin a little while */ + Sleep(100); + _wremove(wfnm); + } +} + +/* TODO Find easier and more portable implementation of removing directory recursively. */ +/* e.g. */ +void +pyi_remove_temp_path(const char *dir) +{ + wchar_t wfnm[PATH_MAX + 1]; + wchar_t wdir[PATH_MAX + 1]; + struct _wfinddata_t wfinfo; + intptr_t h; + size_t dirnmlen; + + pyi_win32_utils_from_utf8(wdir, dir, PATH_MAX); + wcscpy(wfnm, wdir); + dirnmlen = wcslen(wfnm); + + if (wfnm[dirnmlen - 1] != L'/' && wfnm[dirnmlen - 1] != L'\\') { + wcscat(wfnm, L"\\"); + dirnmlen++; + } + wcscat(wfnm, L"*"); + h = _wfindfirst(wfnm, &wfinfo); + + if (h != -1) { + remove_one(wfnm, dirnmlen, wfinfo); + + while (_wfindnext(h, &wfinfo) == 0) { + remove_one(wfnm, dirnmlen, wfinfo); + } + _findclose(h); + } + _wrmdir(wdir); +} +#else /* ifdef _WIN32 */ +static void +remove_one(char *pnm, int pos, const char *fnm) +{ + struct stat sbuf; + + if (strcmp(fnm, ".") == 0 || strcmp(fnm, "..") == 0) { + return; + } + pnm[pos] = PYI_NULLCHAR; + strcat(pnm, fnm); + + if (stat(pnm, &sbuf) == 0) { + if (S_ISDIR(sbuf.st_mode) ) { + /* Use recursion to remove subdirectories. */ + pyi_remove_temp_path(pnm); + } + else { + unlink(pnm); + } + } +} + +void +pyi_remove_temp_path(const char *dir) +{ + char fnm[PATH_MAX + 1]; + DIR *ds; + struct dirent *finfo; + int dirnmlen; + + /* Leave 1 char for PY_SEP if needed */ + strncpy(fnm, dir, PATH_MAX); + dirnmlen = strlen(fnm); + + if (fnm[dirnmlen - 1] != PYI_SEP) { + strcat(fnm, PYI_SEPSTR); + dirnmlen++; + } + ds = opendir(dir); + if (!ds) { + return; + } + finfo = readdir(ds); + + while (finfo) { + remove_one(fnm, dirnmlen, finfo->d_name); + finfo = readdir(ds); + } + closedir(ds); + rmdir(dir); +} +#endif /* ifdef _WIN32 */ + +/* TODO is this function still used? Could it be removed? */ +/* + * If binaries were extracted, this should be called + * to remove them + */ +void +cleanUp(ARCHIVE_STATUS *status) +{ + if (status->temppath[0]) { + pyi_remove_temp_path(status->temppath); + } +} + +/* + * helper for extract2fs + * which may try multiple places + */ +/* TODO find better name for function. */ +FILE * +pyi_open_target(const char *path, const char* name_) +{ + +#ifdef _WIN32 + wchar_t wchar_buffer[PATH_MAX]; + struct _stat sbuf; +#else + struct stat sbuf; +#endif + char fnm[PATH_MAX]; + char name[PATH_MAX]; + char *dir; + size_t len; + + if (snprintf(fnm, PATH_MAX, "%s", path) >= PATH_MAX || + snprintf(name, PATH_MAX, "%s", name_) >= PATH_MAX) { + return NULL; + } + + len = strlen(fnm); + dir = strtok(name, PYI_SEPSTR); + + while (dir != NULL) { + len += strlen(dir) + strlen(PYI_SEPSTR); + /* Check if fnm does not exceed the buffer size */ + if (len >= PATH_MAX-1) { + return NULL; + } + strcat(fnm, PYI_SEPSTR); + strcat(fnm, dir); + dir = strtok(NULL, PYI_SEPSTR); + + if (!dir) { + break; + } + +#ifdef _WIN32 + pyi_win32_utils_from_utf8(wchar_buffer, fnm, PATH_MAX); + + if (_wstat(wchar_buffer, &sbuf) < 0) { + pyi_win32_mkdir(wchar_buffer); + } +#else + + if (stat(fnm, &sbuf) < 0) { + mkdir(fnm, 0700); + } +#endif + } + +#ifdef _WIN32 + pyi_win32_utils_from_utf8(wchar_buffer, fnm, PATH_MAX); + + if (_wstat(wchar_buffer, &sbuf) == 0) { + OTHERERROR("WARNING: file already exists but should not: %s\n", fnm); + } +#else + + if (stat(fnm, &sbuf) == 0) { + OTHERERROR("WARNING: file already exists but should not: %s\n", fnm); + } +#endif + /* + * pyi_path_fopen() wraps different fopen names. On Windows it uses + * wide-character version of fopen. + */ + return pyi_path_fopen(fnm, "wb"); +} + +/* Copy the file src to dst 4KB per time */ +int +pyi_copy_file(const char *src, const char *dst, const char *filename) +{ + FILE *in = pyi_path_fopen(src, "rb"); + FILE *out = pyi_open_target(dst, filename); + char buf[4096]; + size_t read_count = 0; + int error = 0; + + if (in == NULL || out == NULL) { + if (in) { + fclose(in); + } + if (out) { + fclose(out); + } + return -1; + } + + while (!feof(in)) { + read_count = fread(buf, 1, 4096, in); + if (read_count <= 0 ) { + if (ferror(in)) { + clearerr(in); + error = -1; + break; + } + } + else { + size_t rc = fwrite(buf, 1, read_count, out); + if (rc <= 0 || ferror(out)) { + clearerr(out); + error = -1; + break; + } + } + } +#ifndef WIN32 + fchmod(fileno(out), S_IRUSR | S_IWUSR | S_IXUSR); +#endif + fclose(in); + fclose(out); + + return error; +} + +/* TODO use dlclose() when exiting. */ +/* Load the shared dynamic library (DLL) */ +dylib_t +pyi_utils_dlopen(const char *dllpath) +{ + +#ifdef _WIN32 + wchar_t * dllpath_w; + dylib_t ret; +#else + int dlopenMode = RTLD_NOW | RTLD_GLOBAL; +#endif + +#ifdef AIX + /* Append the RTLD_MEMBER to the open mode for 'dlopen()' + * in order to load shared object member from library. + */ + dlopenMode |= RTLD_MEMBER; +#endif + +#ifdef _WIN32 + dllpath_w = pyi_win32_utils_from_utf8(NULL, dllpath, 0); + ret = LoadLibraryExW(dllpath_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); + free(dllpath_w); + return ret; +#else + return dlopen(dllpath, dlopenMode); +#endif + +} + +/* ////////////////////////////////////////////////////////////////// */ +/* TODO better merging of the following platform specific functions. */ +/* ////////////////////////////////////////////////////////////////// */ + +#ifdef _WIN32 + +int +pyi_utils_set_environment(const ARCHIVE_STATUS *status) +{ + return 0; +} + +int +pyi_utils_create_child(const char *thisfile, const ARCHIVE_STATUS* status, + const int argc, char *const argv[]) +{ + SECURITY_ATTRIBUTES sa; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + int rc = 0; + wchar_t buffer[PATH_MAX]; + + /* TODO is there a replacement for this conversion or just use wchar_t everywhere? */ + /* Convert file name to wchar_t from utf8. */ + pyi_win32_utils_from_utf8(buffer, thisfile, PATH_MAX); + + /* the parent process should ignore all signals it can */ + signal(SIGABRT, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGBREAK, SIG_IGN); + + VS("LOADER: Setting up to run child\n"); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + GetStartupInfoW(&si); + si.lpReserved = NULL; + si.lpDesktop = NULL; + si.lpTitle = NULL; + si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; + si.wShowWindow = SW_NORMAL; + si.hStdInput = (void*)_get_osfhandle(fileno(stdin)); + si.hStdOutput = (void*)_get_osfhandle(fileno(stdout)); + si.hStdError = (void*)_get_osfhandle(fileno(stderr)); + + VS("LOADER: Creating child process\n"); + + if (CreateProcessW( + buffer, /* Pointer to name of executable module. */ + GetCommandLineW(), /* pointer to command line string */ + &sa, /* pointer to process security attributes */ + NULL, /* pointer to thread security attributes */ + TRUE, /* handle inheritance flag */ + 0, /* creation flags */ + NULL, /* pointer to new environment block */ + NULL, /* pointer to current directory name */ + &si, /* pointer to STARTUPINFO */ + &pi /* pointer to PROCESS_INFORMATION */ + )) { + VS("LOADER: Waiting for child process to finish...\n"); + WaitForSingleObject(pi.hProcess, INFINITE); + GetExitCodeProcess(pi.hProcess, (unsigned long *)&rc); + } + else { + FATAL_WINERROR("CreateProcessW", "Error creating child process!\n"); + rc = -1; + } + return rc; +} + +#else /* ifdef _WIN32 */ + +static int +set_dynamic_library_path(const char* path) +{ + int rc = 0; + char *env_var, *env_var_orig; + char *new_path, *orig_path; + + #ifdef AIX + /* LIBPATH is used to look up dynamic libraries on AIX. */ + env_var = "LIBPATH"; + env_var_orig = "LIBPATH_ORIG"; + #else + /* LD_LIBRARY_PATH is used on other *nix platforms (except Darwin). */ + env_var = "LD_LIBRARY_PATH"; + env_var_orig = "LD_LIBRARY_PATH_ORIG"; + #endif /* AIX */ + + /* keep original value in a new env var so the application can restore it + * before forking subprocesses. This is important so that e.g. a forked + * (system installed) ssh can find the matching (system installed) ssh + * related libraries - not the potentially different versions of same libs + * that we have bundled. + */ + orig_path = pyi_getenv(env_var); + if (orig_path) { + pyi_setenv(env_var_orig, orig_path); + VS("LOADER: %s=%s\n", env_var_orig, orig_path); + } + /* prepend our path to the original path, pyi_strjoin can deal with orig_path being NULL or empty string */ + new_path = pyi_strjoin(path, ":", orig_path); + rc = pyi_setenv(env_var, new_path); + VS("LOADER: %s=%s\n", env_var, new_path); + free(new_path); + return rc; +} + +int +pyi_utils_set_environment(const ARCHIVE_STATUS *status) +{ + int rc = 0; + + #ifdef __APPLE__ + /* On Mac OS X we do not use environment variables DYLD_LIBRARY_PATH + * or others to tell OS where to look for dynamic libraries. + * There were some issues with this approach. In some cases some + * system libraries were trying to load incompatible libraries from + * the dist directory. For instance this was experienced with macprots + * and PyQt applications. + * + * To tell the OS where to look for dynamic libraries we modify + * .so/.dylib files to use relative paths to other dependend + * libraries starting with @executable_path. + * + * For more information see: + * http://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac + * http://developer.apple.com/library/mac/#documentation/DeveloperTools/ \ + * Conceptual/DynamicLibraries/100-Articles/DynamicLibraryUsageGuidelines.html + */ + /* For environment variable details see 'man dyld'. */ + pyi_unsetenv("DYLD_FRAMEWORK_PATH"); + pyi_unsetenv("DYLD_FALLBACK_FRAMEWORK_PATH"); + pyi_unsetenv("DYLD_VERSIONED_FRAMEWORK_PATH"); + pyi_unsetenv("DYLD_LIBRARY_PATH"); + pyi_unsetenv("DYLD_FALLBACK_LIBRARY_PATH"); + pyi_unsetenv("DYLD_VERSIONED_LIBRARY_PATH"); + pyi_unsetenv("DYLD_ROOT_PATH"); + + #else + + /* Set library path to temppath. This is only for onefile mode.*/ + if (status->temppath[0] != PYI_NULLCHAR) { + rc = set_dynamic_library_path(status->temppath); + } + /* Set library path to homepath. This is for default onedir mode.*/ + else { + rc = set_dynamic_library_path(status->homepath); + } + #endif /* ifdef __APPLE__ */ + + return rc; +} + +/* + * If the program is actived by a systemd socket, systemd will set + * LISTEN_PID, LISTEN_FDS environment variable for that process. + * + * LISTEN_PID is set to the pid of the parent process of bootloader, + * which is forked by systemd. + * + * Bootloader will duplicate LISTEN_FDS to child process, but the + * LISTEN_PID environment variable remains unchanged. + * + * Here we change the LISTEN_PID to the child pid in child process. + * So the application can detecte it and use the LISTEN_FDS created + * by systemd. + */ +int +set_systemd_env() +{ + const char * env_var = "LISTEN_PID"; + if(pyi_getenv(env_var) != NULL) { + /* the ULONG_STRING_SIZE is roughly equal to log10(max number) + * but can be calculated in compile time. + * The idea is from an answer on stackoverflow, + * https://stackoverflow.com/questions/8257714/ + */ + #define ULONG_STRING_SIZE (sizeof (unsigned long) * CHAR_BIT / 3 + 2) + char pid_str[ULONG_STRING_SIZE]; + snprintf(pid_str, ULONG_STRING_SIZE, "%ld", (unsigned long)getpid()); + return pyi_setenv(env_var, pid_str); + } + return 0; +} + +/* Remember child process id. It allows sending a signal to child process. + * Frozen application always runs in a child process. Parent process is used + * to setup environment for child process and clean the environment when + * child exited. + */ +pid_t child_pid = 0; + +static void +_ignoring_signal_handler(int signum) +{ + VS("LOADER: Ignoring signal %d\n", signum); +} + +static void +_signal_handler(int signum) +{ + VS("LOADER: Forwarding signal %d to child pid %d\n", signum, child_pid); + kill(child_pid, signum); +} + +/* Start frozen application in a subprocess. The frozen application runs + * in a subprocess. + */ +int +pyi_utils_create_child(const char *thisfile, const ARCHIVE_STATUS* status, + const int argc, char *const argv[]) +{ + pid_t pid = 0; + int rc = 0; + int i; + + /* cause nonzero return unless this is overwritten + * with a successful return code from wait() */ + int wait_rc = -1; + + /* As indicated in signal(7), signal numbers range from 1-31 (standard) + * and 32-64 (Linux real-time). */ + const size_t num_signals = 65; + + sighandler_t handler; + int ignore_signals; + int signum; + + argv_pyi = (char**)calloc(argc + 1, sizeof(char*)); + argc_pyi = 0; + if (!argv_pyi) { + FATALERROR("LOADER: failed to allocate argv_pyi: %s\n", strerror(errno)); + goto cleanup; + } + + for (i = 0; i < argc; i++) { + #if defined(__APPLE__) && defined(WINDOWED) + + /* if we are on a Mac, it passes a strange -psnxxx argument. Filter it out. */ + if (strstr(argv[i], "-psn") == argv[i]) { + /* skip */ + } + else + #endif + { + char *const tmp = strdup(argv[i]); + if (!tmp) { + FATALERROR("LOADER: failed to strdup argv[%d]: %s\n", i, strerror(errno)); + /* If we can't allocate basic amounts of memory at this critical point, + * we should probably just give up. */ + goto cleanup; + } + argv_pyi[argc_pyi++] = tmp; + } + } + + #if defined(__APPLE__) && defined(WINDOWED) + process_apple_events(true /* short timeout (250 ms) */); + #endif + + pid = fork(); + if (pid < 0) { + VS("LOADER: failed to fork child process: %s\n", strerror(errno)); + goto cleanup; + } + + /* Child code. */ + if (pid == 0) { + /* Replace process by starting a new application. */ + if (set_systemd_env() != 0) { + VS("WARNING: Application is started by systemd socket," + "but we can't set proper LISTEN_PID on it.\n"); + } + if (execvp(thisfile, argv_pyi) < 0) { + VS("Failed to exec: %s\n", strerror(errno)); + goto cleanup; + } + /* NOTREACHED */ + } + + /* From here to end-of-function is parent code (since the child exec'd). + * The exception is the `cleanup` block that frees argv_pyi; in the child, + * wait_rc is -1, so the child exit code checking is skipped. */ + + child_pid = pid; + ignore_signals = (pyi_arch_get_option(status, "pyi-bootloader-ignore-signals") != NULL); + handler = ignore_signals ? &_ignoring_signal_handler : &_signal_handler; + + /* Redirect all signals received by parent to child process. */ + if (ignore_signals) { + VS("LOADER: Ignoring all signals in parent\n"); + } else { + VS("LOADER: Registering signal handlers\n"); + } + for (signum = 0; signum < num_signals; ++signum) { + // don't mess with SIGCHLD/SIGCLD; it affects our ability + // to wait() for the child to exit + // don't change SIGTSP handling to allow Ctrl-Z + if (signum != SIGCHLD && signum != SIGCLD && signum != SIGTSTP) { + signal(signum, handler); + } + } + + #if defined(__APPLE__) && defined(WINDOWED) + /* MacOS code -- forward events to child! */ + do { + /* The below loop will iterate about once every second on Apple, + * waiting on the event queue most of that time. */ + wait_rc = waitpid(child_pid, &rc, WNOHANG); + if (wait_rc == 0) { + /* Child not done yet -- wait for and process AppleEvents with a + * 1 second timeout, forwarding file-open events to the child. */ + process_apple_events(false /* long timeout (1 sec) */); + } + } while (!wait_rc); + #else + wait_rc = waitpid(child_pid, &rc, 0); + #endif + if (wait_rc < 0) { + VS("LOADER: failed to wait for child process: %s\n", strerror(errno)); + } + + /* When child process exited, reset signal handlers to default values. */ + VS("LOADER: Restoring signal handlers\n"); + for (signum = 0; signum < num_signals; ++signum) { + signal(signum, SIG_DFL); + } + + cleanup: + VS("LOADER: freeing args\n"); + for (i = 0; i < argc_pyi; i++) { + free(argv_pyi[i]); + } + free(argv_pyi); + + /* Either wait() failed, or we jumped to `cleanup` and + * didn't wait() at all. Either way, exit with error, + * because rc does not contain a valid process exit code. */ + if (wait_rc < 0) { + VS("LOADER: exiting early\n"); + return 1; + } + + if (WIFEXITED(rc)) { + VS("LOADER: returning child exit status %d\n", WEXITSTATUS(rc)); + return WEXITSTATUS(rc); + } + + /* Process ended abnormally */ + if (WIFSIGNALED(rc)) { + VS("LOADER: re-raising child signal %d\n", WTERMSIG(rc)); + /* Mimick the signal the child received */ + raise(WTERMSIG(rc)); + } + return 1; +} + +/* + * On Mac OS X this converts events from kAEOpenDocuments and kAEGetURL into sys.argv. + * After startup, it also forwards kAEOpenDocuments and KAEGetURL events at runtime to the child process. + * + * TODO: The below can be simplified considerably if re-written in Objective C (e.g. put into pyi_utils_osx.m). + */ +#if defined(__APPLE__) && defined(WINDOWED) + +/* Convert a FourCharCode into a string (useful for debug). Returned buffer is a static buffer, so subsequent calls + * may overwrite the same buffer. */ +static const char *CC2Str(FourCharCode code) { + /* support up to 3 calls on the same debug print line */ + static char bufs[3][5]; + static unsigned int bufsidx = 0; + char *buf = bufs[bufsidx++ % 3u]; + snprintf(buf, 5, "%c%c%c%c", (code >> 24) & 0xFF, (code >> 16) & 0xFF, (code >> 8) & 0xFF, code & 0xFF); + /* buffer is guaranteed to be nul terminated here */ + return buf; +} + +/* Generic event forwarder -- forwards an event destined for this process to the child process, + * copying its param object, if any. Parameter `theAppleEvent` may be NULL, in which case a new + * event is created with the specified class and id (containing 0 params / no param object). */ +static OSErr generic_forward_apple_event(const AppleEvent *const theAppleEvent /* NULL ok */, + const AEEventClass eventClass, const AEEventID evtID, + const char *const descStr) +{ + const FourCharCode evtCode = (FourCharCode)evtID; + OSErr err; + AppleEvent childEvent; + AEAddressDesc target; + DescType actualType = 0, typeCode = typeWildCard; + char *buf = NULL; /* dynamic buffer to hold copied event param data */ + Size bufSize = 0, actualSize = 0; + + VS("LOADER [AppleEvent]: Forwarder called for \"%s\".\n", descStr); + if (!child_pid) { + /* Child not up yet -- there is no way to "forward" this before child started!. */ + VS("LOADER [AppleEvent]: Child not up yet (child_pid is 0)\n"); + return errAEEventNotHandled; + } + VS("LOADER [AppleEvent]: Forwarding '%s' event.\n", CC2Str(evtCode)); + err = AECreateDesc(typeKernelProcessID, &child_pid, sizeof(child_pid), &target); + if (err != noErr) { + OTHERERROR("LOADER [AppleEvent]: Failed to create AEAddressDesc: %d\n", (int)err); + goto out; + } + VS("LOADER [AppleEvent]: Created AEAddressDesc.\n"); + err = AECreateAppleEvent(eventClass, evtID, &target, kAutoGenerateReturnID, kAnyTransactionID, + &childEvent); + if (err != noErr) { + OTHERERROR("LOADER [AppleEvent]: Failed to create event copy: %d\n", (int)err); + goto release_desc; + } + VS("LOADER [AppleEvent]: Created AppleEvent instance for child process.\n"); + + + if (!theAppleEvent) { + /* Calling code wants a new event created from scratch, we do so + * here and it will have 0 params. Assumption: caller knows that + * the event type in question normally has 0 params. */ + VS("LOADER [AppleEvent]: New AppleEvent class: '%s' code: '%s'\n", + CC2Str((FourCharCode)eventClass), CC2Str((FourCharCode)evtID)); + } else { + err = AESizeOfParam(theAppleEvent, keyDirectObject, &typeCode, &bufSize); + if (err != noErr) { + /* No params for this event */ + VS("LOADER [AppleEvent]: Failed to get size of param (error=%d) -- event '%s' may lack params.\n", + (int)err, CC2Str(evtCode)); + } else { + /* This event has a param object, copy it. */ + + VS("LOADER [AppleEvent]: Got size of param: %ld\n", (long)bufSize); + buf = malloc(bufSize); + if (!buf) { + /* Failed to allocate buffer! */ + OTHERERROR("LOADER [AppleEvent]: Failed to allocate buffer of size %ld: %s\n", + (long)bufSize, strerror(errno)); + goto release_evt; + } + VS("LOADER [AppleEvent]: Allocated buffer of size: %ld\n", (long)bufSize); + VS("LOADER [AppleEvent]: Getting param.\n"); + err = AEGetParamPtr(theAppleEvent, keyDirectObject, typeWildCard, + &actualType, buf, bufSize, &actualSize); + if (err != noErr) { + OTHERERROR("LOADER [AppleEvent]: Failed to get param data.\n"); + goto release_evt; + } + if (actualSize > bufSize) { + /* From reading the Apple API docs, this should never happen, but it pays + * to program defensively here. */ + OTHERERROR("LOADER [AppleEvent]: Got param size=%ld > bufSize=%ld, error!\n", + (long)actualSize, (long)bufSize); + goto release_evt; + } + VS("LOADER [AppleEvent]: Got param type=%x ('%s') size=%ld\n", + (UInt32)actualType, CC2Str((FourCharCode)actualType), (long)actualSize); + VS("LOADER [AppleEvent]: Putting param.\n"); + err = AEPutParamPtr(&childEvent, keyDirectObject, actualType, buf, actualSize); + if (err != noErr) { + OTHERERROR("LOADER [AppleEvent]: Failed to put param data.\n"); + goto release_evt; + } + } + } + VS("LOADER [AppleEvent]: Sending message...\n"); + err = AESendMessage(&childEvent, NULL, kAENoReply, 60 /* 60 = about 1.0 seconds timeout */); + VS("LOADER [AppleEvent]: Handler sent \"%s\" message to child pid %ld.\n", descStr, (long)child_pid); +release_evt: + free(buf); + AEDisposeDesc(&childEvent); +release_desc: + AEDisposeDesc(&target); +out: + return err; +} + +static Boolean realloc_checked(void **bufptr, Size size) +{ + void *tmp = realloc(*bufptr, size); + if (!tmp) { + OTHERERROR("LOADER [AppleEvents]: Failed to allocate a buffer of size %ld.\n", (long)size); + return false; + } + VS("LOADER [AppleEvents]: (re)allocated a buffer of size %ld\n", (long)size); + *bufptr = tmp; + return true; +} + +/* Handles apple events 'odoc' and 'GURL', both before and after the child_pid is up, Copying them to argv if child + * not up yet, or otherwise forwarding them to the child if the child is started. */ +static OSErr handle_odoc_GURL_events(const AppleEvent *theAppleEvent, const AEEventID evtID) +{ + const FourCharCode evtCode = (FourCharCode)evtID; + const Boolean apple_event_is_open_doc = evtID == kAEOpenDocuments; + const char *const descStr = apple_event_is_open_doc ? "OpenDoc" : "GetURL"; + + VS("LOADER [AppleEvent]: %s handler called.\n", descStr); + + if (!child_pid) { + /* Child process is not up yet -- so we pick up kAEOpen and/or kAEGetURL events and append them to argv. */ + + AEDescList docList; + OSErr err; + long index; + long count = 0; + char *buf = NULL; /* Dynamic buffer for URL/file path data -- gets realloc'd as we iterate */ + + VS("LOADER [AppleEvent ARGV_EMU]: Processing args for forward...\n"); + + err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, &docList); + if (err != noErr) return err; + + err = AECountItems(&docList, &count); + if (err != noErr) return err; + + for (index = 1; index <= count; ++index) /* AppleEvent lists are 1-indexed (I guess because of Pascal?) */ + { + DescType returnedType; + AEKeyword keywd; + Size actualSize = 0, bufSize = 0; + DescType typeCode = typeWildCard; + + err = AESizeOfNthItem(&docList, index, &typeCode, &bufSize); + if (err != noErr) { + OTHERERROR("LOADER [AppleEvent ARGV_EMU]: Failed to get size of Nth item %ld, error: %d\n", + index, (int)err); + continue; + } + + if (!realloc_checked((void **)&buf, bufSize+1)) { + /* Not enough memory -- very unlikely but if so keep going */ + OTHERERROR("LOADER [AppleEvent ARGV_EMU]: Not enough memory for Nth item %ld, skipping%d\n", index); + continue; + } + + err = AEGetNthPtr(&docList, index, apple_event_is_open_doc ? typeFileURL : typeUTF8Text, &keywd, + &returnedType, buf, bufSize, &actualSize); + if (err != noErr) { + VS("LOADER [AppleEvent ARGV_EMU]: err[%ld] = %d\n", index-1L, (int)err); + } else if (actualSize > bufSize) { + /* This should never happen but is here for thoroughness */ + VS("LOADER [AppleEvent ARGV_EMU]: err[%ld]: not enough space in buffer (%ld > %ld)\n", + index-1L, (long)actualSize, (long)bufSize); + } else { + /* Copied data to buf, now ensure data is a simple file path and then copy to argv_pyi[argc_pyi] */ + char *tmp_str = NULL; + Boolean ok; + + buf[actualSize] = 0; /* Ensure NUL-char termination. */ + if (apple_event_is_open_doc) { + /* Now, convert file:/// style URLs to an actual filesystem path for argv emu. */ + CFURLRef url = CFURLCreateWithBytes(NULL, (UInt8 *)buf, actualSize, kCFStringEncodingUTF8, + NULL); + if (url) { + CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); + ok = false; + if (path) { + const Size newLen = (Size)CFStringGetMaximumSizeOfFileSystemRepresentation(path); + if (realloc_checked((void **)&buf, newLen+1)) { + bufSize = newLen; + ok = CFStringGetFileSystemRepresentation(path, buf, bufSize); + buf[bufSize] = 0; /* Ensure NUL termination */ + } + CFRelease(path); /* free */ + } + CFRelease(url); /* free */ + if (!ok) { + VS("LOADER [AppleEvent ARGV_EMU]: " + "Failed to convert file:/// path to POSIX filesystem representation for arg %ld!\n", + index); + continue; + } + } + } + /* Append URL to argv_pyi array, reallocating as necessary */ + VS("LOADER [AppleEvent ARGV_EMU]: arg[%d] = %s\n", (int)argc_pyi, buf); + tmp_str = strdup(buf); + ok = realloc_checked((void **)&argv_pyi, (argc_pyi + 2) * sizeof(char *)); + if (!ok || !tmp_str) { + /* Out of memory. Extremely unlikely -- not clear what to do here. + * Attempt to silently continue. */ + OTHERERROR("LOADER [AppleEvent ARGV_EMU]: allocation for arg[%d] failed: %s\n", + argc_pyi, strerror(errno)); + free(tmp_str); /* free of possible NULL ok */ + continue; + } + argv_pyi[argc_pyi++] = tmp_str; + argv_pyi[argc_pyi] = NULL; + VS("LOADER [AppleEvent ARGV_EMU]: argv entry appended.\n"); + } + } + + free(buf); /* free of possible-NULL ok */ + + err = AEDisposeDesc(&docList); + + return err; + } /* else ... */ + + /* The child process exists.. so we forward events to it */ + return generic_forward_apple_event(theAppleEvent, + apple_event_is_open_doc ? kCoreEventClass : kInternetEventClass, + evtID, + descStr); +} + +/* This brings the child_pid's windows to the foreground when the user double-clicks the + * app's icon again in the macOS UI. 'rapp' is accepted by us only when the child is + * already running. */ +static OSErr handle_rapp_event(const AppleEvent *const theAppleEvent, const AEEventID evtID) +{ + OSErr err; + + VS("LOADER [AppleEvent]: ReopenApp handler called.\n"); + + /* First, forward the 'rapp' event to the child */ + err = generic_forward_apple_event(theAppleEvent, kCoreEventClass, evtID, "ReopenApp"); + + if (err == noErr) { + /* Next, create a new activate ('actv') event. We never receive this event because + * we have no window, but if we did this event would come next. So we synthesize an + * event that should normally come for a windowed app, so that the child process + * is brought to the foreground properly. */ + generic_forward_apple_event(NULL /* create new event with 0 params */, + kAEMiscStandards, kAEActivate, "Activate"); + } + + return err; +} + +/* Top-level event handler -- dispatches 'odoc', 'GURL', 'rapp', or 'actv' events. */ +static pascal OSErr handle_apple_event(const AppleEvent *theAppleEvent, AppleEvent *reply, SRefCon handlerRefCon) +{ + const FourCharCode evtCode = (FourCharCode)handlerRefCon; + const AEEventID evtID = (AEEventID)handlerRefCon; + (void)reply; /* unused */ + + VS("LOADER [AppleEvent]: %s called with code '%s'.\n", __FUNCTION__, CC2Str(evtCode)); + + switch(evtID) { + case kAEOpenDocuments: + case kAEGetURL: + return handle_odoc_GURL_events(theAppleEvent, evtID); + case kAEReopenApplication: + return handle_rapp_event(theAppleEvent, evtID); + case kAEActivate: + /* This is not normally reached since the bootloader process lacks a window, and it + * turns out macOS never sends this event to processes lacking a window. However, + * since the Apple API docs are very sparse, this has been left-in here just in case. */ + return generic_forward_apple_event(theAppleEvent, kAEMiscStandards, evtID, "Activate"); + default: + /* Not 'GURL', 'odoc', 'rapp', or 'actv' -- this is not reached unless there is a + * programming error in the code that sets up the handler(s) in process_apple_events. */ + OTHERERROR("LOADER [AppleEvent]: %s called with unexpected event type '%s'!", + __FUNCTION__, CC2Str(evtCode)); + return errAEEventNotHandled; + } +} + +/* This function gets installed as the process-wide UPP event handler. + * It is responsible for dequeuing events and telling Carbon to forward + * them to our installed handlers. */ +static OSStatus evt_handler_proc(EventHandlerCallRef href, EventRef eref, void *data) { + VS("LOADER [AppleEvent]: App event handler proc called.\n"); + Boolean release = false; + EventRecord eventRecord; + OSStatus err; + + /* Events of type kEventAppleEvent must be removed from the queue + * before being passed to AEProcessAppleEvent. */ + if (IsEventInQueue(GetMainEventQueue(), eref)) { + /* RemoveEventFromQueue will release the event, which will + * destroy it if we don't retain it first. */ + VS("LOADER [AppleEvent]: Event was in queue, will release.\n"); + RetainEvent(eref); + release = true; + RemoveEventFromQueue(GetMainEventQueue(), eref); + } + /* Convert the event ref to the type AEProcessAppleEvent expects. */ + ConvertEventRefToEventRecord(eref, &eventRecord); + VS("LOADER [AppleEvent]: what=%hu message=%lx ('%s') modifiers=%hu\n", + eventRecord.what, eventRecord.message, CC2Str((FourCharCode)eventRecord.message), eventRecord.modifiers); + /* This will end up calling one of the callback functions + * that we installed in process_apple_events() */ + err = AEProcessAppleEvent(&eventRecord); + if (err == errAEEventNotHandled) { + VS("LOADER [AppleEvent]: Ignored event.\n"); + } else if (err != noErr) { + VS("LOADER [AppleEvent]: Error processing event: %d\n", (int)err); + } + if (release) { + ReleaseEvent(eref); + } + return noErr; +} + +/* Apple event message pump */ +static void process_apple_events(Boolean short_timeout) +{ + static EventHandlerUPP handler; + static AEEventHandlerUPP handler_ae; + static Boolean did_install = false; + static EventHandlerRef handler_ref; + EventTypeSpec event_types[1]; /* List of event types to handle. */ + event_types[0].eventClass = kEventClassAppleEvent; + event_types[0].eventKind = kEventAppleEvent; + + VS("LOADER [AppleEvent]: Processing...\n"); + + if (!did_install) { + OSStatus err; + handler = NewEventHandlerUPP(evt_handler_proc); + handler_ae = NewAEEventHandlerUPP(handle_apple_event); + /* register 'odoc' (open document) */ + err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, handler_ae, (SRefCon)kAEOpenDocuments, false); + if (err == noErr) { + /* register 'GURL' (open url) */ + err = AEInstallEventHandler(kInternetEventClass, kAEGetURL, handler_ae, (SRefCon)kAEGetURL, false); + } + if (err == noErr) { + /* register 'rapp' (re-open application) */ + err = AEInstallEventHandler(kCoreEventClass, kAEReopenApplication, handler_ae, + (SRefCon)kAEReopenApplication, false); + } + if (err == noErr) { + /* register 'actv' (activate) */ + err = AEInstallEventHandler(kAEMiscStandards, kAEActivate, handler_ae, (SRefCon)kAEActivate, false); + } + if (err == noErr) { + err = InstallApplicationEventHandler(handler, 1, event_types, NULL, &handler_ref); + } + + if (err != noErr) { + /* App-wide handler failed. Uninstall everything. */ + AERemoveEventHandler(kAEMiscStandards, kAEActivate, handler_ae, false); + AERemoveEventHandler(kCoreEventClass, kAEReopenApplication, handler_ae, false); + AERemoveEventHandler(kInternetEventClass, kAEGetURL, handler_ae, false); + AERemoveEventHandler(kCoreEventClass, kAEOpenDocuments, handler_ae, false); + DisposeEventHandlerUPP(handler); + DisposeAEEventHandlerUPP(handler_ae); + VS("LOADER [AppleEvent]: Disposed handlers.\n"); + } else { + VS("LOADER [AppleEvent]: Installed handlers.\n"); + did_install = true; + } + } + + if (did_install) { + /* Event pump: Process events for up to 1.0 (or 0.25) seconds (or until an error is encountered) */ + const EventTimeout timeout = short_timeout ? 0.25 : 1.0; /* number of seconds */ + for (;;) { + OSStatus status; + EventRef event_ref; /* Event that caused ReceiveNextEvent to return. */ + + VS("LOADER [AppleEvent]: Calling ReceiveNextEvent\n"); + + status = ReceiveNextEvent(1, event_types, timeout, kEventRemoveFromQueue, &event_ref); + + if (status == eventLoopTimedOutErr) { + VS("LOADER [AppleEvent]: ReceiveNextEvent timed out\n"); + break; + } else if (status != 0) { + VS("LOADER [AppleEvent]: ReceiveNextEvent fetching events failed\n"); + break; + } else { + /* We actually pulled an event off the queue, so process it. + We now 'own' the event_ref and must release it. */ + VS("LOADER [AppleEvent]: ReceiveNextEvent got an EVENT\n"); + + VS("LOADER [AppleEvent]: Dispatching event...\n"); + status = SendEventToEventTarget(event_ref, GetEventDispatcherTarget()); + + ReleaseEvent(event_ref); + event_ref = NULL; + if (status != 0) { + VS("LOADER [AppleEvent]: processing events failed\n"); + break; + } + } + } + + VS("LOADER [AppleEvent]: Out of the event loop.\n"); + + } else { + static Boolean once = false; + if (!once) { + /* Log this only once since this is compiled-in even in non-debug mode and we + * want to avoid console spam, since process_apple_events may be called a lot. */ + OTHERERROR("LOADER [AppleEvent]: ERROR installing handler.\n"); + once = true; + } + } +} +#endif /* if defined(__APPLE__) && defined(WINDOWED) */ + +#endif /* WIN32 */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_utils.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..6713adc3dd862bc676d7496cfad915d49886e8c7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_utils.h @@ -0,0 +1,55 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* + * Portable wrapper for some utility functions like getenv/setenv, + * file path manipulation and other shared data types or functions. + */ + +#ifndef HEADER_PYI_UTILS_H +#define HEADER_PYI_UTILS_H + +#include "pyi_archive.h" + +// some platforms do not provide strnlen +#ifndef HAVE_STRNLEN +size_t strnlen(const char *str, size_t n); +#endif + +// some platforms do not provide strndup +#ifndef HAVE_STRNDUP +char *strndup(const char * str, size_t n); +#endif + +/* Environment variables. */ + +char *pyi_getenv(const char *variable); +int pyi_setenv(const char *variable, const char *value); +int pyi_unsetenv(const char *variable); + +/* Temporary files. */ + +int pyi_create_temp_path(ARCHIVE_STATUS *status); +void pyi_remove_temp_path(const char *dir); + +/* File manipulation. */ +FILE *pyi_open_target(const char *path, const char* name_); +int pyi_copy_file(const char *src, const char *dst, const char *filename); + +/* Other routines. */ +dylib_t pyi_utils_dlopen(const char *dllpath); +int pyi_utils_create_child(const char *thisfile, const ARCHIVE_STATUS *status, + const int argc, char *const argv[]); +int pyi_utils_set_environment(const ARCHIVE_STATUS *status); + +#endif /* HEADER_PY_UTILS_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_win32_utils.c b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_win32_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..fae06ac9b109f5e4652cf689180c6d75b8fefc2a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_win32_utils.c @@ -0,0 +1,525 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* TODO move this code to file pyi_win32.c. */ + +/* + * Functions in this file are windows specific and are mostly related to handle + * Side-by-Side assembly: + * + * https://en.wikipedia.org/wiki/Side-by-side_assembly + */ + +#ifdef _WIN32 + +/* windows.h will use API for WinServer 2003 with SP1 and WinXP with SP2 */ +#define _WIN32_WINNT 0x0502 + +#include +#include /* InitCommonControls */ +#include /* _fileno */ +#include /* _get_osfhandle */ +#include /* signal */ +#include /* ConvertStringSecurityDescriptorToSecurityDescriptorW */ + +/* PyInstaller headers. */ +#include "pyi_global.h" /* PATH_MAX */ +#include "pyi_utils.h" +#include "pyi_win32_utils.h" + +static HANDLE hCtx = INVALID_HANDLE_VALUE; +static ULONG_PTR actToken; + +#ifndef STATUS_SXS_EARLY_DEACTIVATION + #define STATUS_SXS_EARLY_DEACTIVATION 0xC015000F +#endif + +#define ERROR_STRING_MAX 4096 +static char errorString[ERROR_STRING_MAX]; + +/* GetWinErrorString + * + * Return a pointer to a null-terminated string containing a textual description of the + * given error code. If the error code is zero, the result of GetLastError() is used. + * The text is localized and ANSI-encoded. The caller is not responsible for freeing + * this pointer. + * + * Returns a pointer to statically-allocated storage. Not thread safe. + */ + +char * GetWinErrorString(DWORD error_code) { + wchar_t local_buffer[ERROR_STRING_MAX]; + DWORD result; + + if (error_code == 0) { + error_code = GetLastError(); + } + /* Note: Giving 0 to dwLanguageID means MAKELANGID(LANG_NEUTRAL, + * SUBLANG_NEUTRAL), but we should use SUBLANG_DEFAULT instead of + * SUBLANG_NEUTRAL. Please see the note written in + * "Language Identifier Constants and Strings" on MSDN. + * https://docs.microsoft.com/en-us/windows/desktop/intl/language-identifier-constants-and-strings + */ + result = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM, // dwFlags + NULL, // lpSource + error_code, // dwMessageID + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // dwLanguageID + local_buffer, // lpBuffer + ERROR_STRING_MAX, // nSize + NULL // Arguments + ); + + if (!result) { + FATAL_WINERROR("FormatMessageW", "No error messages generated.\n"); + return "PyInstaller: FormatMessageW failed."; + } + if (!pyi_win32_utils_to_utf8(errorString, + local_buffer, + ERROR_STRING_MAX)) { + return "PyInstaller: pyi_win32_utils_to_utf8 failed."; + } + return errorString; +} + +int +CreateActContext(const char *manifestpath) +{ + wchar_t * manifestpath_w; + ACTCTXW ctx; + BOOL activated; + HANDLE k32; + + HANDLE (WINAPI * CreateActCtx)(PACTCTXW pActCtx); + BOOL (WINAPI * ActivateActCtx)(HANDLE hActCtx, ULONG_PTR * lpCookie); + + /* Setup activation context */ + VS("LOADER: manifestpath: %s\n", manifestpath); + manifestpath_w = pyi_win32_utils_from_utf8(NULL, manifestpath, 0); + + k32 = LoadLibraryA("kernel32"); + CreateActCtx = (void*)GetProcAddress(k32, "CreateActCtxW"); + ActivateActCtx = (void*)GetProcAddress(k32, "ActivateActCtx"); + + if (!CreateActCtx || !ActivateActCtx) { + VS("LOADER: Cannot find CreateActCtx/ActivateActCtx exports in kernel32.dll\n"); + return 0; + } + + ZeroMemory(&ctx, sizeof(ctx)); + ctx.cbSize = sizeof(ACTCTX); + ctx.lpSource = manifestpath_w; + ctx.dwFlags = ACTCTX_FLAG_SET_PROCESS_DEFAULT; + + hCtx = CreateActCtx(&ctx); + free(manifestpath_w); + + if (hCtx != INVALID_HANDLE_VALUE) { + VS("LOADER: Activation context created\n"); + activated = ActivateActCtx(hCtx, &actToken); + + if (activated) { + VS("LOADER: Activation context activated\n"); + return 1; + } + } + + hCtx = INVALID_HANDLE_VALUE; + VS("LOADER: Error activating the context: ActivateActCtx: \n%s\n", GetWinErrorString(0)); + return 0; +} + +/* Convert a wide string to an ANSI string. + * + * Returns a newly allocated buffer containing the ANSI characters terminated by a null + * character. The caller is responsible for freeing this buffer with free(). + * + * Returns NULL and logs error reason if encoding fails. + */ + +char * +pyi_win32_wcs_to_mbs(const wchar_t *wstr) +{ + DWORD len, ret; + char * str; + + /* NOTE: setlocale hysterics are not needed on Windows - this function + * has an explicit codepage parameter. CP_ACP means "current ANSI codepage" + * which is set in the "Language for Non-Unicode Programs" control panel setting. */ + + /* Get buffer size by passing NULL and 0 for output arguments */ + len = WideCharToMultiByte(CP_ACP, /* CodePage */ + 0, /* dwFlags */ + wstr, /* lpWideCharStr */ + -1, /* cchWideChar - length in chars */ + NULL, /* lpMultiByteStr */ + 0, /* cbMultiByte - length in bytes */ + NULL, /* lpDefaultChar */ + NULL /* lpUsedDefaultChar */ + ); + + if (0 == len) { + FATAL_WINERROR("WideCharToMultiByte", "Failed to get ANSI buffer size.\n"); + return NULL; + } + + str = (char *)calloc(len + 1, sizeof(char)); + if (str == NULL) { + FATAL_WINERROR("win32_wcs_to_mbs", "Out of memory."); + return NULL; + }; + + ret = WideCharToMultiByte(CP_ACP, /* CodePage */ + 0, /* dwFlags */ + wstr, /* lpWideCharStr */ + -1, /* cchWideChar - length in chars */ + str, /* lpMultiByteStr */ + len, /* cbMultiByte - length in bytes */ + NULL, /* lpDefaultChar */ + NULL /* lpUsedDefaultChar */ + ); + + if (0 == ret) { + FATAL_WINERROR("WideCharToMultiByte", "Failed to encode filename as ANSI.\n"); + return NULL; + } + return str; +} + +/* We shouldn't need to convert ANSI to wchar_t since everything is provided as wchar_t */ + +/* The following are used to convert the UTF-16 strings provided by Windows + * into UTF-8 so we can store them in the `char *` variables and fields + * we use on Linux. Storing them like this is a wart, but storing them as `wchar_t *` + * and converting back and forth everywhere on Linux/OS X is an even bigger wart + */ + +/* Convert elements of wargv to UTF-8 */ + +char ** +pyi_win32_argv_to_utf8(int argc, wchar_t **wargv) +{ + int i, j; + char ** argv; + + argv = (char **)calloc(argc + 1, sizeof(char *)); + if (argv == NULL) { + return NULL; + }; + + for (i = 0; i < argc; i++) { + argv[i] = pyi_win32_utils_to_utf8(NULL, wargv[i], 0); + + if (NULL == argv[i]) { + goto err; + } + } + argv[argc] = NULL; + + return argv; +err: + + for (j = 0; j <= i; j++) { + free(argv[j]); + } + free(argv); + return NULL; +} + +/* Convert elements of wargv back from UTF-8. Used when calling + * PySys_SetArgv on Python 3. + */ + +wchar_t ** +pyi_win32_wargv_from_utf8(int argc, char **argv) +{ + int i, j; + wchar_t ** wargv; + + wargv = (wchar_t **)calloc(argc + 1, sizeof(wchar_t *)); + if (wargv == NULL) { + return NULL; + }; + + for (i = 0; i < argc; i++) { + wargv[i] = pyi_win32_utils_from_utf8(NULL, argv[i], 0); + + if (NULL == wargv[i]) { + goto err; + } + } + wargv[argc] = NULL; + + return wargv; +err: + + for (j = 0; j <= i; j++) { + free(wargv[j]); + } + free(wargv); + return NULL; +} + +/* + * Encode wchar_t (UTF16) into char (UTF8). + * + * `wstr` must be null-terminated. + * + * If `str` is not NULL, copies the result into the given buffer, which must hold + * at least `len` bytes. Returns the given buffer if successful. Returns NULL on + * encoding failure, or if the UTF-8 encoding requires more than `len` bytes. + * + * If `str` is NULL, allocates and returns a new buffer to store the result. The + * `len` argument is ignored. Returns NULL on encoding failure. The caller is + * responsible for freeing the returned buffer using free(). + * + */ +char * +pyi_win32_utils_to_utf8(char *str, const wchar_t *wstr, size_t len) +{ + char * output; + + if (NULL == str) { + /* Get buffer size by passing NULL and 0 for output arguments + * -1 for cchWideChar means string is null-terminated + */ + len = WideCharToMultiByte(CP_UTF8, /* CodePage */ + 0, /* dwFlags */ + wstr, /* lpWideCharStr */ + -1, /* cchWideChar - length in chars */ + NULL, /* lpMultiByteStr */ + 0, /* cbMultiByte - length in bytes */ + NULL, /* lpDefaultChar */ + NULL /* lpUsedDefaultChar */ + ); + + if (0 == len) { + FATAL_WINERROR("WideCharToMultiByte", "Failed to get UTF-8 buffer size.\n"); + return NULL; + } + + output = (char *)calloc(len + 1, sizeof(char)); + if (output == NULL) { + FATAL_WINERROR("win32_utils_to_utf8", "Out of memory."); + return NULL; + }; + } + else { + output = str; + } + + len = WideCharToMultiByte(CP_UTF8, /* CodePage */ + 0, /* dwFlags */ + wstr, /* lpWideCharStr */ + -1, /* cchWideChar - length in chars */ + output, /* lpMultiByteStr */ + (DWORD)len, /* cbMultiByte - length in bytes */ + NULL, /* lpDefaultChar */ + NULL /* lpUsedDefaultChar */ + ); + + if (len == 0) { + FATAL_WINERROR("WideCharToMultiByte", + "Failed to encode wchar_t as UTF-8.\n"); + return NULL; + } + return output; +} + +/* + * Decode char (UTF8) into wchar_t (UTF16). + * + * `str` must be null-terminated. + * + * If `wstr` is not NULL, copies the result into the given buffer, which must hold + * at least `wlen` characters. Returns the given buffer if successful. Returns NULL on + * encoding failure, or if the UTF-16 encoding requires more than `wlen` characters. + * + * If `wstr` is NULL, allocates and returns a new buffer to store the result. The + * `wlen` argument is ignored. Returns NULL on encoding failure. The caller is + * responsible for freeing the returned buffer using free(). + */ + +wchar_t * +pyi_win32_utils_from_utf8(wchar_t *wstr, const char *str, size_t wlen) +{ + wchar_t * output; + + if (NULL == wstr) { + /* Get buffer size by passing NULL and 0 for output arguments + * -1 for cbMultiByte means string is null-terminated. + */ + wlen = MultiByteToWideChar(CP_UTF8, /* CodePage */ + 0, /* dwFlags */ + str, /* lpMultiByteStr */ + -1, /* cbMultiByte - length in bytes */ + NULL, /* lpWideCharStr */ + 0 /* cchWideChar - length in chars */ + ); + + if (0 == wlen) { + FATAL_WINERROR("MultiByteToWideChar", "Failed to get wchar_t buffer size.\n"); + return NULL; + } + + output = (wchar_t *)calloc(wlen + 1, sizeof(wchar_t)); + if (output == NULL) { + FATAL_WINERROR("win32_utils_from_utf8", "Out of memory."); + return NULL; + }; + } + else { + output = wstr; + } + + wlen = MultiByteToWideChar(CP_UTF8, /* CodePage */ + 0, /* dwFlags */ + str, /* lpMultiByteStr */ + -1, /* cbMultiByte - length in bytes */ + output, /* lpWideCharStr */ + (DWORD)wlen /* cchWideChar - length in chars */ + ); + + if (wlen == 0) { + FATAL_WINERROR("MultiByteToWideChar", "Failed to decode wchar_t from UTF-8\n"); + return NULL; + } + return output; +} + +/* Convert an UTF-8 string to an ANSI string. + * + * Returns NULL if encoding fails. + */ +char * +pyi_win32_utf8_to_mbs(char * dst, const char * src, size_t max) +{ + wchar_t * wsrc; + char * mbs; + + wsrc = pyi_win32_utils_from_utf8(NULL, src, 0); + + if (NULL == wsrc) { + return NULL; + } + + mbs = pyi_win32_wcs_to_mbs(wsrc); + + free(wsrc); + + if (NULL == mbs) { + return NULL; + } + + if (dst) { + strncpy(dst, mbs, max); + free(mbs); + return dst; + } + else { + return mbs; + } +} + + +/* Retrieve the SID of the current user. + * Used in a compatibility work-around for wine, which at the time of writing + * (version 5.0.2) does not properly support SID S-1-3-4 (directory owner), + * and therefore user's actual SID must be used instead. + * + * Returns SID string on success, NULL on failure. The returned string must + * be freed using LocalFree(). + */ +static wchar_t * +_pyi_win32_get_user_sid() +{ + HANDLE process_token = INVALID_HANDLE_VALUE; + DWORD user_info_size = 0; + PTOKEN_USER user_info = NULL; + wchar_t *sid = NULL; + + // Get access token for the calling process + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &process_token)) { + goto cleanup; + } + // Get buffer size and allocate buffer + if (!GetTokenInformation(process_token, TokenUser, NULL, 0, &user_info_size)) { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + goto cleanup; + } + } + user_info = (PTOKEN_USER)calloc(1, user_info_size); + if (!user_info) { + goto cleanup; + } + // Get user information + if (!GetTokenInformation(process_token, TokenUser, user_info, user_info_size, &user_info_size)) { + goto cleanup; + } + // Convert SID to string + ConvertSidToStringSidW(user_info->User.Sid, &sid); + + // Cleanup +cleanup: + free(user_info); + if (process_token != INVALID_HANDLE_VALUE) { + CloseHandle(process_token); + } + + return sid; +} + +/* Create a directory at path with restricted permissions. + * The directory owner will be the only one with permissions on the created + * dir. Calling this function is equivalent to callin chmod(path, 0700) on + * Posix. + * Returns 0 on success, -1 on error. + */ +int +pyi_win32_mkdir(const wchar_t *path) +{ + wchar_t *sid = NULL; + wchar_t stringSecurityDesc[PATH_MAX]; + + // ACE String : + sid = _pyi_win32_get_user_sid(); // Resolve user's SID for compatibility with wine + _snwprintf(stringSecurityDesc, PATH_MAX, + L"D:" // DACL (D) : + L"(A;" // Authorize (A) + L";FA;" // FILE_ALL_ACCESS (FA) + L";;%s)", // For the current user (retrieved SID) or current directory owner (SID: S-1-3-4) + // no other permissions are granted + sid ? sid : L"S-1-3-4"); + LocalFree(sid); // Must be freed using LocalFree() + VS("LOADER: creating directory %S with security string: %S\n", path, stringSecurityDesc); + + SECURITY_ATTRIBUTES securityAttr; + PSECURITY_DESCRIPTOR *lpSecurityDesc; + securityAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttr.bInheritHandle = FALSE; + lpSecurityDesc = &securityAttr.lpSecurityDescriptor; + + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( + stringSecurityDesc, + SDDL_REVISION_1, + lpSecurityDesc, + NULL)) { + return -1; + } + if (!CreateDirectoryW(path, &securityAttr)) { + return -1; + }; + return 0; +} + +#endif /* _WIN32 */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/src/pyi_win32_utils.h b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_win32_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..6faec92eb2e844befb803cfdae9dfe7c1fa41399 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/src/pyi_win32_utils.h @@ -0,0 +1,35 @@ +/* + * **************************************************************************** + * Copyright (c) 2013-2021, 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) + * **************************************************************************** + */ + +/* TODO Convert this file to file ./common/pyi_win32_utils.h */ +#ifndef UTILS_H +#define UTILS_H + +#ifdef _WIN32 + +char * GetWinErrorString(DWORD error_code); +int CreateActContext(const char *manifestpath); + +char ** pyi_win32_argv_to_utf8(int argc, wchar_t **wargv); +wchar_t ** pyi_win32_wargv_from_utf8(int argc, char **argv); + +char * pyi_win32_utils_to_utf8(char *buffer, const wchar_t *str, size_t n); +wchar_t * pyi_win32_utils_from_utf8(wchar_t *buffer, const char *ostr, size_t n); + +char * pyi_win32_utf8_to_mbs(char * dst, const char * src, size_t max); + +int pyi_win32_mkdir(const wchar_t *path); + +#endif /* ifdef _WIN32 */ + +#endif /* UTILS_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/tests/test_launch.c b/3rdparty/pyinstaller-4.3/bootloader/tests/test_launch.c new file mode 100644 index 0000000000000000000000000000000000000000..45a4618e7d60822cb0db1dd855b9b29eb67f7f88 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/tests/test_launch.c @@ -0,0 +1,108 @@ +// ----------------------------------------------------------------------------- +// Copyright (c) 2020-2021, 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) +// ----------------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "pyi_global.h" +#include + +#include // required fo cmocka :-( +#include + +int checkFile(char *buf, const char *fmt, ...); + +static void test_checkFile(void **state) { + char result[PATH_MAX]; + + // TODO: use some mocks to determine stat() output + + errno = 0; + assert_int_equal(-1, checkFile(result, "%s%s%s.pkg", "a1", "bb", "cc", "dd")); + assert_int_not_equal(errno, 0); // formatting passed, stat failed + assert_string_equal(result, "a1bbcc.pkg"); + + errno = 0; + assert_int_equal(-1, checkFile(result, "%s", "")); + assert_int_not_equal(errno, 0); // formatting passed, stat failed + assert_string_equal(result, ""); + + char *path2 = (char *) malloc(PATH_MAX+10); + memset(path2, 'a', PATH_MAX+8); + // a few bytes more + errno = 0; + path2[PATH_MAX+8] = '\0'; + assert_int_equal(-1, checkFile(result, "%s%s%s.pkg", "a1", path2, "ccc")); + assert_int_equal(errno, 0); // formatting formatting failed + // exact length + errno = 0; + path2[PATH_MAX] = '\0'; + assert_int_equal(-1, checkFile(result, "%s", path2)); + assert_int_equal(errno, 0); // formatting formatting failed + // one byte less + errno = 0; + path2[PATH_MAX-1] = '\0'; + assert_int_equal(-1, checkFile(result, "%s", path2)); + assert_int_not_equal(errno, 0); // formatting passed, stat failed +} + +int splitName(char *path, char *filename, const char *item); + +static void test_splitName(void **state) { + char path[PATH_MAX]; + char filename[PATH_MAX]; + + // TODO: use some mocks to determine + + assert_int_equal(0, splitName(path, filename, "aaa:bbb")); + assert_string_equal(path, "aaa"); + assert_string_equal(filename, "bbb"); + + assert_int_equal(-1, splitName(path, filename, "")); + assert_int_equal(-1, splitName(path, filename, ":")); + assert_int_equal(-1, splitName(path, filename, "aaa")); + assert_int_equal(-1, splitName(path, filename, "aaa:")); + assert_int_equal(-1, splitName(path, filename, ":bbb")); + + // these cases are not expected to occur in real life + assert_int_equal(0, splitName(path, filename, "aaa:::")); + assert_string_equal(filename, "::"); + assert_int_equal(-1, splitName(path, filename, ":::bbb")); + + char *path2 = (char *) malloc(PATH_MAX+10); + memset(path2, 'a', PATH_MAX+8); + path2[10] = ':'; + // a few bytes more + path2[PATH_MAX+8] = '\0'; + assert_int_equal(-1, splitName(path, filename, path2)); + // exact length + path2[PATH_MAX] = '\0'; + assert_int_equal(-1, splitName(path, filename, path2)); + // one byte less + path2[PATH_MAX-1] = '\0'; + assert_int_equal(0, splitName(path, filename, path2)); + assert_string_equal(path, "aaaaaaaaaa"); +} + +#if defined(_WIN32) +int wmain(void) +#else +int main(void) +#endif +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_checkFile), + cmocka_unit_test(test_splitName), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/tests/test_path.c b/3rdparty/pyinstaller-4.3/bootloader/tests/test_path.c new file mode 100644 index 0000000000000000000000000000000000000000..494c4e38e1d4f01621d27fa081d8bf2ab6761407 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/tests/test_path.c @@ -0,0 +1,207 @@ +// ----------------------------------------------------------------------------- +// Copyright (c) 2020-2021, 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) +// ----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "pyi_global.h" +#include "pyi_path.h" + +#include // required fo cmocka :-( +#include + +// NOTE: path separator is platform dependent, hence the use of PYI_SEPSTR +// and string concatenation... + +static void test_dirname(void **state) { + char result[PATH_MAX]; + + // pyi_path_dirname("/a1/bb/cc/dd") -> "/a1/bb/cc" + assert_true(pyi_path_dirname(result, PYI_SEPSTR "a1" PYI_SEPSTR "bb" PYI_SEPSTR "cc" PYI_SEPSTR "dd")); + assert_string_equal(result, PYI_SEPSTR "a1" PYI_SEPSTR "bb" PYI_SEPSTR "cc"); + + // pyi_path_dirname("/a2/bb/cc/dd/") -> "/a2/bb/cc" + assert_true(pyi_path_dirname(result, PYI_SEPSTR "a2" PYI_SEPSTR "bb" PYI_SEPSTR "cc" PYI_SEPSTR "dd" PYI_SEPSTR)); + assert_string_equal(result, PYI_SEPSTR "a2" PYI_SEPSTR "bb" PYI_SEPSTR "cc"); + + // pyi_path_dirname("d3d") -> "." + assert_true(pyi_path_dirname(result, "d3d")); + assert_string_equal(result, PYI_CURDIRSTR); + + // pyi_path_dirname("d5d/") -> "." + assert_true(pyi_path_dirname(result, "d5d" PYI_SEPSTR)); + assert_string_equal(result, PYI_CURDIRSTR); + + // pyi_path_dirname("") -> "." + assert_true(pyi_path_dirname(result, "")); + assert_string_equal(result, PYI_CURDIRSTR); + + // Test correct handling of paths that exceed PATH_MAX + char *path2 = (char *) malloc(PATH_MAX+10); + memset(path2, 'a', PATH_MAX+8); + // a few bytes more + path2[PATH_MAX+8] = '\0'; + assert_false(pyi_path_dirname(result, path2)); + // exact length + path2[PATH_MAX] = '\0'; + assert_false(pyi_path_dirname(result, path2)); + // one byte less + path2[PATH_MAX-1] = '\0'; + assert_true(pyi_path_dirname(result, path2)); +} + + +static void test_basename(void **state) { + char result[PATH_MAX]; + char input[PATH_MAX]; + // basename()'s second argument is not `const`, thus using a constant + // string yields to segementation fault. + + // pyi_path_basename("/aa/bb/cc/d1d") -> "d1d" + strcpy(input, PYI_SEPSTR "aa" PYI_SEPSTR "bb" PYI_SEPSTR "cc" PYI_SEPSTR "d1d"); + pyi_path_basename(result, input); + assert_string_equal(result, "d1d"); + + // pyi_path_basename("d3dd") -> "d3dd" + strcpy(input, "d3dd"); + pyi_path_basename(result, input); + assert_string_equal(result, "d3dd"); + + /* These cases are not correctly handled by our implementation of + * basename(). But this is okay, since we use basename() only to determine + * the application path based on argv[0]. + */ +#if 0 + // pyi_path_basename("/aa/bb/cc/d2d/") -> "d2d" + strcpy(input, PYI_SEPSTR "aa" PYI_SEPSTR "bb" PYI_SEPSTR "cc" PYI_SEPSTR "d2d" PYI_SEPSTR); + pyi_path_basename(result, input); + assert_string_equal(result, "d2d"); + + // pyi_path_basename("d4dd/") -> "d4dd" + strcpy(input, "d4dd" PYI_SEPSTR); + pyi_path_basename(result, input); + assert_string_equal(result, "d4dd"); + + // pyi_path_basename("") -> "." + strcpy(input, ""); + pyi_path_basename(result, input); + assert_string_equal(result, PYI_CURDIRSTR); +#endif +} + + +static void test_join(void **state) { + char path1[PATH_MAX]; + char path2[PATH_MAX]; + char result[PATH_MAX]; + char *r; + + // pyi_path_join("lalala", "mememe") -> "lalala/mememe" + r = pyi_path_join((char *)result, "lalala", "mememe"); + assert_ptr_equal(r, &result); + assert_string_equal(result, "lalala" PYI_SEPSTR "mememe"); + + // pyi_path_join("lalala/", "mememe") -> "lalala/mememe" + r = pyi_path_join((char *)result, "lalala" PYI_SEPSTR, "mememe"); + assert_ptr_equal(r, &result); + assert_string_equal(result, "lalala" PYI_SEPSTR "mememe"); + + // pyi_path_join("lalala/", "mememe/") -> "lalala/mememe" + r = pyi_path_join((char *)result, "lalala" PYI_SEPSTR, "mememe" PYI_SEPSTR); + assert_ptr_equal(r, &result); + assert_string_equal(result, "lalala" PYI_SEPSTR "mememe"); + + // pyi_path_join("lalala", "mememe/") -> "lalala/mememe" + r = pyi_path_join((char *)result, "lalala", "mememe" PYI_SEPSTR); + assert_ptr_equal(r, &result); + assert_string_equal(result, "lalala" PYI_SEPSTR "mememe"); + + // pyi_path_join("lal/ala", "mem/eme/") -> "lal/ala/meme/me" + r = pyi_path_join((char *)result, "lal" PYI_SEPSTR "ala" PYI_SEPSTR, "mem" PYI_SEPSTR "eme" PYI_SEPSTR); + assert_ptr_equal(r, &result); + assert_string_equal(result, "lal" PYI_SEPSTR "ala" PYI_SEPSTR "mem" PYI_SEPSTR "eme"); + + // First string empty is not handled: + // pyi_path_join("", "mememe") -> "/mememe" + r = pyi_path_join((char *)result, "", "mememe"); + assert_ptr_equal(r, &result); + assert_string_equal(result, PYI_SEPSTR "mememe"); + + memset(path1, 'a', PATH_MAX); path1[PATH_MAX-1] = '\0'; + memset(path2, 'b', PATH_MAX); path2[PATH_MAX-1] = '\0'; + assert_int_equal(strlen(path1), PATH_MAX-1); + assert_int_equal(strlen(path2), PATH_MAX-1); + assert_ptr_equal(NULL, pyi_path_join(result, path1, path2)); + + // tests near max length of path1 + assert_ptr_equal(NULL, pyi_path_join(result, path1, "")); + path1[PATH_MAX-2] = '\0'; + assert_ptr_equal(NULL, pyi_path_join(result, path1, "")); + path1[PATH_MAX-3] = '\0'; + assert_ptr_equal(r, pyi_path_join(result, path1, "")); + assert_int_equal(strlen(result), PATH_MAX-2); // -2 no trailing slash in path1 + assert_ptr_equal(NULL, pyi_path_join(result, path1, "x")); + path1[PATH_MAX-4] = '\0'; + assert_ptr_equal(r, pyi_path_join(result, path1, "x")); + assert_int_equal(strlen(result), PATH_MAX-2); // -2 no trailing slash in path1 + assert_ptr_equal(NULL, pyi_path_join(result, path1, "xx")); + + // tests near max length of path2 + assert_ptr_equal(NULL, pyi_path_join(result, "", path2)); + assert_ptr_equal(NULL, pyi_path_join(result, "x", path2)); + path2[PATH_MAX-2] = '\0'; + assert_ptr_equal(NULL, pyi_path_join(result, "", path2)); // stash takes space! + assert_ptr_equal(NULL, pyi_path_join(result, "x", path2)); + path2[PATH_MAX-3] = '\0'; + assert_ptr_equal(r, pyi_path_join(result, "", path2)); + assert_ptr_equal(NULL, pyi_path_join(result, "x", path2)); + path2[PATH_MAX-4] = '\0'; + assert_ptr_equal(r, pyi_path_join(result, "", path2)); + assert_ptr_equal(r, pyi_path_join(result, "x", path2)); + // we don't count exactly if slashes are contained + assert_int_equal(strlen(result), PATH_MAX-2); + assert_ptr_equal(NULL, pyi_path_join(result, "xx", path2)); + path2[PATH_MAX-4] = PYI_SEP; + assert_int_equal(path2[strlen(path2)], 0); + assert_int_equal(path2[strlen(path2)-1], PYI_SEP); + assert_ptr_equal(r, pyi_path_join(result, "", path2)); + // we don't count exactly if slashes are contained + assert_int_equal(strlen(result), PATH_MAX-3); + assert_int_equal(result[strlen(result)-1], 'b'); // trailing slash removed + assert_ptr_equal(NULL, pyi_path_join(result, "x", path2)); +} + + +int pyi_search_path(char *result, const char *appname); + +static void test_search_path(void **state) { + char result[PATH_MAX]; + pyi_search_path(result, "my-app"); +} + + +#if defined(_WIN32) +int wmain(void) +#else +int main(void) +#endif +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_dirname), + cmocka_unit_test(test_basename), + cmocka_unit_test(test_join), + cmocka_unit_test(test_search_path), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/tests/wscript b/3rdparty/pyinstaller-4.3/bootloader/tests/wscript new file mode 100644 index 0000000000000000000000000000000000000000..607da7671298ae9c864e43e9b51f74e60fef07b6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/tests/wscript @@ -0,0 +1,42 @@ +# -*- mode: python -*- vim: filetype=python +# ----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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) +# ----------------------------------------------------------------------------- + +def configure(ctx): + ctx.check_cc(lib='cmocka', mandatory=False, uselib_store='CMOCKA') + +def build(ctx): + + def test_program(name): + if ctx.env.DEST_OS == 'win32': + # Z: inflate*() + # ADVAPI32: ConvertStringSecurityDescriptorToSecurityDescriptorW() + extra_libs=['ADVAPI32', 'Z'] + else: + extra_libs=[] + ctx.program( + source= ["test_%s.c" % name], + target="test_%s" % name, + includes='../src', + use=ctx.env.link_with_dynlibs + ["CMOCKA", "OBJECTS"] + extra_libs, + stlib=ctx.env.link_with_staticlibs, + install_path=None, + ) + + if ctx.env.DEST_OS == 'win32' and ctx.variant.endswith('w'): + # Skip building tests on Windows with windowed variants. In addition to + # requiring additional libraries, these also expect the entry point to + # be WinMain() instead of main(). + return + + if "LIB_CMOCKA" in ctx.env: + test_program("path") + test_program("launch") diff --git a/3rdparty/pyinstaller-4.3/bootloader/tools/strip.py b/3rdparty/pyinstaller-4.3/bootloader/tools/strip.py new file mode 100644 index 0000000000000000000000000000000000000000..ca4025a605e77088142815f5f0af925175915b1e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/tools/strip.py @@ -0,0 +1,59 @@ +#! /usr/bin/env python +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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) +#----------------------------------------------------------------------------- +""" +Strip a program/library after it is created. Use this tool as an example. + +Usage:: + + bld.program(features='strip', source='main.c', target='foo') + +By using:: + + @TaskGen.feature('cprogram', 'cxxprogram', 'fcprogram') + + +If stripping at installation time is preferred, use the following:: + + import shutil, os + from waflib import Build + from waflib.Tools import ccroot + def copy_fun(self, src, tgt, **kw): + shutil.copy2(src, tgt) + os.chmod(tgt, kw.get('chmod', Utils.O644)) + try: + tsk = kw['tsk'] + except KeyError: + pass + else: + if isinstance(tsk.task, ccroot.link_task): + self.cmd_and_log('strip %s' % tgt) + Build.InstallContext.copy_fun = copy_fun +""" + +def configure(conf): + conf.find_program('strip') + conf.env.append_value('STRIPFLAGS', '') + +from waflib import Task, TaskGen +class strip(Task.Task): + run_str = '${STRIP} ${STRIPFLAGS} ${SRC}' + color = 'BLUE' + after = ['cprogram', 'cxxprogram', 'cshlib', 'cxxshlib', 'fcprogram', 'fcshlib'] + +@TaskGen.feature('strip') +@TaskGen.after('apply_link') +def add_strip_task(self): + try: + link_task = self.link_task + except AttributeError: + return + self.create_task('strip', link_task.outputs[0]) diff --git a/3rdparty/pyinstaller-4.3/bootloader/uncrustify.cfg b/3rdparty/pyinstaller-4.3/bootloader/uncrustify.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e1a954f1b83ada5a1fccaaf7467e34c91871fe33 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/uncrustify.cfg @@ -0,0 +1,1759 @@ +# Uncrustify 0.62 +# +# Custom configuration for PyInstaller. Based on PEP 7 style guidelines. +# +# Invoke uncrustify with `bootloader` as the working dir as follows: +# $ uncrustify -c uncrustify.cfg --no-backup src/*.{h,c} windows/*.h +# + +# +# General options +# + +# The type of line endings +newlines = auto # auto/lf/crlf/cr + +# The original size of tabs in the input +input_tab_size = 4 # number + +# The size of tabs in the output (only used if align_with_tabs=true) +output_tab_size = 4 # number + +# The ASCII value of the string escape char, usually 92 (\) or 94 (^). (Pawn) +string_escape_char = 92 # number + +# Alternate string escape char for Pawn. Only works right before the quote char. +string_escape_char2 = 0 # number + +# Replace tab characters found in string literals with the escape sequence \t instead. +string_replace_tab_chars = false # false/true + +# Allow interpreting '>=' and '>>=' as part of a template in 'void f(list>=val);'. +# If true (default), 'assert(x<0 && y>=3)' will be broken. +# Improvements to template detection may make this option obsolete. +tok_split_gte = false # false/true + +# Override the default ' *INDENT-OFF*' in comments for disabling processing of part of the file. +disable_processing_cmt = "" # string + +# Override the default ' *INDENT-ON*' in comments for enabling processing of part of the file. +enable_processing_cmt = "" # string + +# Enable parsing of digraphs. Default=false +enable_digraphs = false # false/true + +# Control what to do with the UTF-8 BOM (recommend 'remove') +utf8_bom = ignore # ignore/add/remove/force + +# If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 +utf8_byte = false # false/true + +# Force the output encoding to UTF-8 +utf8_force = false # false/true + +# +# Indenting +# + +# The number of columns to indent per level. +# Usually 2, 3, 4, or 8. +indent_columns = 4 # number + +# The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. +# For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level +indent_continue = 0 # number + +# How to use tabs when indenting code +# 0=spaces only +# 1=indent with tabs to brace level, align with spaces +# 2=indent and align with tabs, using spaces when not on a tabstop +indent_with_tabs = 0 # number + +# Comments that are not a brace level are indented with tabs on a tabstop. +# Requires indent_with_tabs=2. If false, will use spaces. +indent_cmt_with_tabs = false # false/true + +# Whether to indent strings broken by '\' so that they line up +indent_align_string = false # false/true + +# The number of spaces to indent multi-line XML strings. +# Requires indent_align_string=True +indent_xml_string = 0 # number + +# Spaces to indent '{' from level +indent_brace = 0 # number + +# Whether braces are indented to the body level +indent_braces = false # false/true + +# Disabled indenting function braces if indent_braces is true +indent_braces_no_func = false # false/true + +# Disabled indenting class braces if indent_braces is true +indent_braces_no_class = false # false/true + +# Disabled indenting struct braces if indent_braces is true +indent_braces_no_struct = false # false/true + +# Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # false/true + +# Indent based on the paren open instead of the brace open in '({\n', default is to indent by brace. +indent_paren_open_brace = false # false/true + +# Whether the 'namespace' body is indented +indent_namespace = false # false/true + +# Only indent one namespace and no sub-namespaces. +# Requires indent_namespace=true. +indent_namespace_single_indent = false # false/true + +# The number of spaces to indent a namespace block +indent_namespace_level = 0 # number + +# If the body of the namespace is longer than this number, it won't be indented. +# Requires indent_namespace=true. Default=0 (no limit) +indent_namespace_limit = 0 # number + +# Whether the 'extern "C"' body is indented +indent_extern = false # false/true + +# Whether the 'class' body is indented +indent_class = false # false/true + +# Whether to indent the stuff after a leading base class colon +indent_class_colon = false # false/true + +# Indent based on a class colon instead of the stuff after the colon. +# Requires indent_class_colon=true. Default=false +indent_class_on_colon = false # false/true + +# Whether to indent the stuff after a leading class initializer colon +indent_constr_colon = false # false/true + +# Virtual indent from the ':' for member initializers. Default is 2 +indent_ctor_init_leading = 2 # number + +# Additional indenting for constructor initializer list +indent_ctor_init = 0 # number + +# False=treat 'else\nif' as 'else if' for indenting purposes +# True=indent the 'if' one level +indent_else_if = false # false/true + +# Amount to indent variable declarations after a open brace. neg=relative, pos=absolute +indent_var_def_blk = 0 # number + +# Indent continued variable declarations instead of aligning. +indent_var_def_cont = false # false/true + +# Indent continued shift expressions ('<<' and '>>') instead of aligning. +# Turn align_left_shift off when enabling this. +indent_shift = false # false/true + +# True: force indentation of function definition to start in column 1 +# False: use the default behavior +indent_func_def_force_col1 = false # false/true + +# True: indent continued function call parameters one indent level +# False: align parameters under the open paren +indent_func_call_param = false # false/true + +# Same as indent_func_call_param, but for function defs +indent_func_def_param = false # false/true + +# Same as indent_func_call_param, but for function protos +indent_func_proto_param = false # false/true + +# Same as indent_func_call_param, but for class declarations +indent_func_class_param = false # false/true + +# Same as indent_func_call_param, but for class variable constructors +indent_func_ctor_var_param = false # false/true + +# Same as indent_func_call_param, but for templates +indent_template_param = false # false/true + +# Double the indent for indent_func_xxx_param options +indent_func_param_double = false # false/true + +# Indentation column for standalone 'const' function decl/proto qualifier +indent_func_const = 0 # number + +# Indentation column for standalone 'throw' function decl/proto qualifier +indent_func_throw = 0 # number + +# The number of spaces to indent a continued '->' or '.' +# Usually set to 0, 1, or indent_columns. +indent_member = 0 # number + +# Spaces to indent single line ('//') comments on lines before code +indent_sing_line_comments = 0 # number + +# If set, will indent trailing single line ('//') comments relative +# to the code instead of trying to keep the same absolute column +indent_relative_single_line_comments = false # false/true + +# Spaces to indent 'case' from 'switch' +# Usually 0 or indent_columns. +indent_switch_case = 0 # number + +# Spaces to shift the 'case' line, without affecting any other lines +# Usually 0. +indent_case_shift = 0 # number + +# Spaces to indent '{' from 'case'. +# By default, the brace will appear under the 'c' in case. +# Usually set to 0 or indent_columns. +indent_case_brace = 0 # number + +# Whether to indent comments found in first column +indent_col1_comment = false # false/true + +# How to indent goto labels +# >0 : absolute column where 1 is the leftmost column +# <=0 : subtract from brace indent +indent_label = 1 # number + +# Same as indent_label, but for access specifiers that are followed by a colon +indent_access_spec = 1 # number + +# Indent the code after an access specifier by one level. +# If set, this option forces 'indent_access_spec=0' +indent_access_spec_body = false # false/true + +# If an open paren is followed by a newline, indent the next line so that it lines up after the open paren (not recommended) +indent_paren_nl = false # false/true + +# Controls the indent of a close paren after a newline. +# 0: Indent to body level +# 1: Align under the open paren +# 2: Indent to the brace level +indent_paren_close = 0 # number + +# Controls the indent of a comma when inside a paren.If TRUE, aligns under the open paren +indent_comma_paren = false # false/true + +# Controls the indent of a BOOL operator when inside a paren.If TRUE, aligns under the open paren +indent_bool_paren = false # false/true + +# If 'indent_bool_paren' is true, controls the indent of the first expression. If TRUE, aligns the first expression to the following ones +indent_first_bool_expr = false # false/true + +# If an open square is followed by a newline, indent the next line so that it lines up after the open square (not recommended) +indent_square_nl = false # false/true + +# Don't change the relative indent of ESQL/C 'EXEC SQL' bodies +indent_preserve_sql = false # false/true + +# Align continued statements at the '='. Default=True +# If FALSE or the '=' is followed by a newline, the next line is indent one tab. +indent_align_assign = true # false/true + +# Indent OC blocks at brace level instead of usual rules. +indent_oc_block = false # false/true + +# Indent OC blocks in a message relative to the parameter name. +# 0=use indent_oc_block rules, 1+=spaces to indent +indent_oc_block_msg = 0 # number + +# Minimum indent for subsequent parameters +indent_oc_msg_colon = 0 # number + +# If true, prioritize aligning with initial colon (and stripping spaces from lines, if necessary). +# Default is true. +indent_oc_msg_prioritize_first_colon = true # false/true + +# If indent_oc_block_msg and this option are on, blocks will be indented the way that Xcode does by default (from keyword if the parameter is on its own line; otherwise, from the previous indentation level). +indent_oc_block_msg_xcode_style = false # false/true + +# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg keyword. +indent_oc_block_msg_from_keyword = false # false/true + +# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is relative to a msg colon. +indent_oc_block_msg_from_colon = false # false/true + +# If indent_oc_block_msg and this option are on, blocks will be indented from where the block caret is. +indent_oc_block_msg_from_caret = false # false/true + +# If indent_oc_block_msg and this option are on, blocks will be indented from where the brace is. +indent_oc_block_msg_from_brace = false # false/true + +# When identing after virtual brace open and newline add further spaces to reach this min. indent. +indent_min_vbrace_open = 0 # number + +# TRUE: When identing after virtual brace open and newline add further spaces after regular indent to reach next tabstop. +indent_vbrace_open_on_tabstop = false # false/true + +# +# Spacing options +# + +# Add or remove space around arithmetic operator '+', '-', '/', '*', etc +sp_arith = add # ignore/add/remove/force + +# Add or remove space around assignment operator '=', '+=', etc +sp_assign = add # ignore/add/remove/force + +# Add or remove space around '=' in C++11 lambda capture specifications. Overrides sp_assign +sp_cpp_lambda_assign = ignore # ignore/add/remove/force + +# Add or remove space after the capture specification in C++11 lambda. +sp_cpp_lambda_paren = ignore # ignore/add/remove/force + +# Add or remove space around assignment operator '=' in a prototype +sp_assign_default = add # ignore/add/remove/force + +# Add or remove space before assignment operator '=', '+=', etc. Overrides sp_assign. +sp_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment operator '=', '+=', etc. Overrides sp_assign. +sp_after_assign = ignore # ignore/add/remove/force + +# Add or remove space in 'NS_ENUM (' +sp_enum_paren = ignore # ignore/add/remove/force + +# Add or remove space around assignment '=' in enum +sp_enum_assign = ignore # ignore/add/remove/force + +# Add or remove space before assignment '=' in enum. Overrides sp_enum_assign. +sp_enum_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment '=' in enum. Overrides sp_enum_assign. +sp_enum_after_assign = ignore # ignore/add/remove/force + +# Add or remove space around preprocessor '##' concatenation operator. Default=Add +sp_pp_concat = add # ignore/add/remove/force + +# Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. +sp_pp_stringify = ignore # ignore/add/remove/force + +# Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. +sp_before_pp_stringify = ignore # ignore/add/remove/force + +# Add or remove space around boolean operators '&&' and '||' +sp_bool = add # ignore/add/remove/force + +# Add or remove space around compare operator '<', '>', '==', etc +sp_compare = add # ignore/add/remove/force + +# Add or remove space inside '(' and ')' +sp_inside_paren = ignore # ignore/add/remove/force + +# Add or remove space between nested parens: '((' vs ') )' +sp_paren_paren = ignore # ignore/add/remove/force + +# Add or remove space between back-to-back parens: ')(' vs ') (' +sp_cparen_oparen = ignore # ignore/add/remove/force + +# Whether to balance spaces inside nested parens +sp_balance_nested_parens = false # false/true + +# Add or remove space between ')' and '{' +sp_paren_brace = ignore # ignore/add/remove/force + +# Add or remove space before pointer star '*' +sp_before_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space before pointer star '*' that isn't followed by a variable name +# If set to 'ignore', sp_before_ptr_star is used instead. +sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space between pointer stars '*' +sp_between_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space after pointer star '*', if followed by a word. +sp_after_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space after pointer star '*', if followed by a qualifier. +sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force + +# Add or remove space after a pointer star '*', if followed by a func proto/def. +sp_after_ptr_star_func = ignore # ignore/add/remove/force + +# Add or remove space after a pointer star '*', if followed by an open paren (function types). +sp_ptr_star_paren = ignore # ignore/add/remove/force + +# Add or remove space before a pointer star '*', if followed by a func proto/def. +sp_before_ptr_star_func = ignore # ignore/add/remove/force + +# Add or remove space before a reference sign '&' +sp_before_byref = ignore # ignore/add/remove/force + +# Add or remove space before a reference sign '&' that isn't followed by a variable name +# If set to 'ignore', sp_before_byref is used instead. +sp_before_unnamed_byref = ignore # ignore/add/remove/force + +# Add or remove space after reference sign '&', if followed by a word. +sp_after_byref = ignore # ignore/add/remove/force + +# Add or remove space after a reference sign '&', if followed by a func proto/def. +sp_after_byref_func = ignore # ignore/add/remove/force + +# Add or remove space before a reference sign '&', if followed by a func proto/def. +sp_before_byref_func = ignore # ignore/add/remove/force + +# Add or remove space between type and word. Default=Force +sp_after_type = force # ignore/add/remove/force + +# Add or remove space before the paren in the D constructs 'template Foo(' and 'class Foo('. +sp_before_template_paren = ignore # ignore/add/remove/force + +# Add or remove space in 'template <' vs 'template<'. +# If set to ignore, sp_before_angle is used. +sp_template_angle = ignore # ignore/add/remove/force + +# Add or remove space before '<>' +sp_before_angle = ignore # ignore/add/remove/force + +# Add or remove space inside '<' and '>' +sp_inside_angle = ignore # ignore/add/remove/force + +# Add or remove space after '<>' +sp_after_angle = ignore # ignore/add/remove/force + +# Add or remove space between '<>' and '(' as found in 'new List();' +sp_angle_paren = ignore # ignore/add/remove/force + +# Add or remove space between '<>' and a word as in 'List m;' +sp_angle_word = ignore # ignore/add/remove/force + +# Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add +sp_angle_shift = add # ignore/add/remove/force + +# Permit removal of the space between '>>' in 'foo >' (C++11 only). Default=False +# sp_angle_shift cannot remove the space without this option. +sp_permit_cpp11_shift = false # false/true + +# Add or remove space before '(' of 'if', 'for', 'switch', 'while', etc. +sp_before_sparen = add # ignore/add/remove/force + +# Add or remove space inside if-condition '(' and ')' +sp_inside_sparen = remove # ignore/add/remove/force + +# Add or remove space before if-condition ')'. Overrides sp_inside_sparen. +sp_inside_sparen_close = ignore # ignore/add/remove/force + +# Add or remove space after if-condition '('. Overrides sp_inside_sparen. +sp_inside_sparen_open = ignore # ignore/add/remove/force + +# Add or remove space after ')' of 'if', 'for', 'switch', and 'while', etc. +sp_after_sparen = add # ignore/add/remove/force + +# Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while', etc. +sp_sparen_brace = ignore # ignore/add/remove/force + +# Add or remove space between 'invariant' and '(' in the D language. +sp_invariant_paren = ignore # ignore/add/remove/force + +# Add or remove space after the ')' in 'invariant (C) c' in the D language. +sp_after_invariant_paren = ignore # ignore/add/remove/force + +# Add or remove space before empty statement ';' on 'if', 'for' and 'while' +sp_special_semi = ignore # ignore/add/remove/force + +# Add or remove space before ';'. Default=Remove +sp_before_semi = remove # ignore/add/remove/force + +# Add or remove space before ';' in non-empty 'for' statements +sp_before_semi_for = ignore # ignore/add/remove/force + +# Add or remove space before a semicolon of an empty part of a for statement. +sp_before_semi_for_empty = ignore # ignore/add/remove/force + +# Add or remove space after ';', except when followed by a comment. Default=Add +sp_after_semi = add # ignore/add/remove/force + +# Add or remove space after ';' in non-empty 'for' statements. Default=Force +sp_after_semi_for = force # ignore/add/remove/force + +# Add or remove space after the final semicolon of an empty part of a for statement: for ( ; ; ). +sp_after_semi_for_empty = ignore # ignore/add/remove/force + +# Add or remove space before '[' (except '[]') +sp_before_square = ignore # ignore/add/remove/force + +# Add or remove space before '[]' +sp_before_squares = ignore # ignore/add/remove/force + +# Add or remove space inside a non-empty '[' and ']' +sp_inside_square = ignore # ignore/add/remove/force + +# Add or remove space after ',' +sp_after_comma = add # ignore/add/remove/force + +# Add or remove space before ',' +sp_before_comma = remove # ignore/add/remove/force + +# Add or remove space between ',' and ']' in multidimensional array type 'int[,,]' +sp_after_mdatype_commas = ignore # ignore/add/remove/force + +# Add or remove space between '[' and ',' in multidimensional array type 'int[,,]' +sp_before_mdatype_commas = ignore # ignore/add/remove/force + +# Add or remove space between ',' in multidimensional array type 'int[,,]' +sp_between_mdatype_commas = ignore # ignore/add/remove/force + +# Add or remove space between an open paren and comma: '(,' vs '( ,' +sp_paren_comma = force # ignore/add/remove/force + +# Add or remove space before the variadic '...' when preceded by a non-punctuator +sp_before_ellipsis = ignore # ignore/add/remove/force + +# Add or remove space after class ':' +sp_after_class_colon = ignore # ignore/add/remove/force + +# Add or remove space before class ':' +sp_before_class_colon = ignore # ignore/add/remove/force + +# Add or remove space after class constructor ':' +sp_after_constr_colon = ignore # ignore/add/remove/force + +# Add or remove space before class constructor ':' +sp_before_constr_colon = ignore # ignore/add/remove/force + +# Add or remove space before case ':'. Default=Remove +sp_before_case_colon = remove # ignore/add/remove/force + +# Add or remove space between 'operator' and operator sign +sp_after_operator = ignore # ignore/add/remove/force + +# Add or remove space between the operator symbol and the open paren, as in 'operator ++(' +sp_after_operator_sym = ignore # ignore/add/remove/force + +# Add or remove space after C/D cast, i.e. 'cast(int)a' vs 'cast(int) a' or '(int)a' vs '(int) a' +sp_after_cast = ignore # ignore/add/remove/force + +# Add or remove spaces inside cast parens +sp_inside_paren_cast = ignore # ignore/add/remove/force + +# Add or remove space between the type and open paren in a C++ cast, i.e. 'int(exp)' vs 'int (exp)' +sp_cpp_cast_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'sizeof' and '(' +sp_sizeof_paren = ignore # ignore/add/remove/force + +# Add or remove space after the tag keyword (Pawn) +sp_after_tag = ignore # ignore/add/remove/force + +# Add or remove space inside enum '{' and '}' +sp_inside_braces_enum = ignore # ignore/add/remove/force + +# Add or remove space inside struct/union '{' and '}' +sp_inside_braces_struct = ignore # ignore/add/remove/force + +# Add or remove space inside '{' and '}' +sp_inside_braces = ignore # ignore/add/remove/force + +# Add or remove space inside '{}' +sp_inside_braces_empty = ignore # ignore/add/remove/force + +# Add or remove space between return type and function name +# A minimum of 1 is forced except for pointer return types. +sp_type_func = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function declaration +sp_func_proto_paren = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function definition +sp_func_def_paren = ignore # ignore/add/remove/force + +# Add or remove space inside empty function '()' +sp_inside_fparens = ignore # ignore/add/remove/force + +# Add or remove space inside function '(' and ')' +sp_inside_fparen = ignore # ignore/add/remove/force + +# Add or remove space inside the first parens in the function type: 'void (*x)(...)' +sp_inside_tparen = ignore # ignore/add/remove/force + +# Add or remove between the parens in the function type: 'void (*x)(...)' +sp_after_tparen_close = ignore # ignore/add/remove/force + +# Add or remove space between ']' and '(' when part of a function call. +sp_square_fparen = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '{' of function +sp_fparen_brace = ignore # ignore/add/remove/force + +# Java: Add or remove space between ')' and '{{' of double brace initializer. +sp_fparen_dbrace = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function calls +sp_func_call_paren = ignore # ignore/add/remove/force + +# Add or remove space between function name and '()' on function calls without parameters. +# If set to 'ignore' (the default), sp_func_call_paren is used. +sp_func_call_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between the user function name and '(' on function calls +# You need to set a keyword to be a user function, like this: 'set func_call_user _' in the config file. +sp_func_call_user_paren = ignore # ignore/add/remove/force + +# Add or remove space between a constructor/destructor and the open paren +sp_func_class_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'return' and '(' +sp_return_paren = ignore # ignore/add/remove/force + +# Add or remove space between '__attribute__' and '(' +sp_attribute_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'defined' and '(' in '#if defined (FOO)' +sp_defined_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'throw' and '(' in 'throw (something)' +sp_throw_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'throw' and anything other than '(' as in '@throw [...];' +sp_after_throw = ignore # ignore/add/remove/force + +# Add or remove space between 'catch' and '(' in 'catch (something) { }' +# If set to ignore, sp_before_sparen is used. +sp_catch_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'version' and '(' in 'version (something) { }' (D language) +# If set to ignore, sp_before_sparen is used. +sp_version_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'scope' and '(' in 'scope (something) { }' (D language) +# If set to ignore, sp_before_sparen is used. +sp_scope_paren = ignore # ignore/add/remove/force + +# Add or remove space between macro and value +sp_macro = ignore # ignore/add/remove/force + +# Add or remove space between macro function ')' and value +sp_macro_func = ignore # ignore/add/remove/force + +# Add or remove space between 'else' and '{' if on the same line +sp_else_brace = add # ignore/add/remove/force + +# Add or remove space between '}' and 'else' if on the same line +sp_brace_else = add # ignore/add/remove/force + +# Add or remove space between '}' and the name of a typedef on the same line +sp_brace_typedef = ignore # ignore/add/remove/force + +# Add or remove space between 'catch' and '{' if on the same line +sp_catch_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'catch' if on the same line +sp_brace_catch = ignore # ignore/add/remove/force + +# Add or remove space between 'finally' and '{' if on the same line +sp_finally_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'finally' if on the same line +sp_brace_finally = ignore # ignore/add/remove/force + +# Add or remove space between 'try' and '{' if on the same line +sp_try_brace = ignore # ignore/add/remove/force + +# Add or remove space between get/set and '{' if on the same line +sp_getset_brace = ignore # ignore/add/remove/force + +# Add or remove space between a variable and '{' for C++ uniform initialization +sp_word_brace = add # ignore/add/remove/force + +# Add or remove space between a variable and '{' for a namespace +sp_word_brace_ns = add # ignore/add/remove/force + +# Add or remove space before the '::' operator +sp_before_dc = ignore # ignore/add/remove/force + +# Add or remove space after the '::' operator +sp_after_dc = ignore # ignore/add/remove/force + +# Add or remove around the D named array initializer ':' operator +sp_d_array_colon = ignore # ignore/add/remove/force + +# Add or remove space after the '!' (not) operator. Default=Remove +sp_not = remove # ignore/add/remove/force + +# Add or remove space after the '~' (invert) operator. Default=Remove +sp_inv = remove # ignore/add/remove/force + +# Add or remove space after the '&' (address-of) operator. Default=Remove +# This does not affect the spacing after a '&' that is part of a type. +sp_addr = remove # ignore/add/remove/force + +# Add or remove space around the '.' or '->' operators. Default=Remove +sp_member = remove # ignore/add/remove/force + +# Add or remove space after the '*' (dereference) operator. Default=Remove +# This does not affect the spacing after a '*' that is part of a type. +sp_deref = remove # ignore/add/remove/force + +# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. Default=Remove +sp_sign = remove # ignore/add/remove/force + +# Add or remove space before or after '++' and '--', as in '(--x)' or 'y++;'. Default=Remove +sp_incdec = remove # ignore/add/remove/force + +# Add or remove space before a backslash-newline at the end of a line. Default=Add +sp_before_nl_cont = add # ignore/add/remove/force + +# Add or remove space after the scope '+' or '-', as in '-(void) foo;' or '+(int) bar;' +sp_after_oc_scope = ignore # ignore/add/remove/force + +# Add or remove space after the colon in message specs +# '-(int) f:(int) x;' vs '-(int) f: (int) x;' +sp_after_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space before the colon in message specs +# '-(int) f: (int) x;' vs '-(int) f : (int) x;' +sp_before_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space after the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};' +sp_after_oc_dict_colon = ignore # ignore/add/remove/force + +# Add or remove space before the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};' +sp_before_oc_dict_colon = ignore # ignore/add/remove/force + +# Add or remove space after the colon in message specs +# '[object setValue:1];' vs '[object setValue: 1];' +sp_after_send_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space before the colon in message specs +# '[object setValue:1];' vs '[object setValue :1];' +sp_before_send_oc_colon = ignore # ignore/add/remove/force + +# Add or remove space after the (type) in message specs +# '-(int)f: (int) x;' vs '-(int)f: (int)x;' +sp_after_oc_type = ignore # ignore/add/remove/force + +# Add or remove space after the first (type) in message specs +# '-(int) f:(int)x;' vs '-(int)f:(int)x;' +sp_after_oc_return_type = ignore # ignore/add/remove/force + +# Add or remove space between '@selector' and '(' +# '@selector(msgName)' vs '@selector (msgName)' +# Also applies to @protocol() constructs +sp_after_oc_at_sel = ignore # ignore/add/remove/force + +# Add or remove space between '@selector(x)' and the following word +# '@selector(foo) a:' vs '@selector(foo)a:' +sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force + +# Add or remove space inside '@selector' parens +# '@selector(foo)' vs '@selector( foo )' +# Also applies to @protocol() constructs +sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force + +# Add or remove space before a block pointer caret +# '^int (int arg){...}' vs. ' ^int (int arg){...}' +sp_before_oc_block_caret = ignore # ignore/add/remove/force + +# Add or remove space after a block pointer caret +# '^int (int arg){...}' vs. '^ int (int arg){...}' +sp_after_oc_block_caret = ignore # ignore/add/remove/force + +# Add or remove space between the receiver and selector in a message. +# '[receiver selector ...]' +sp_after_oc_msg_receiver = ignore # ignore/add/remove/force + +# Add or remove space after @property. +sp_after_oc_property = ignore # ignore/add/remove/force + +# Add or remove space around the ':' in 'b ? t : f' +sp_cond_colon = ignore # ignore/add/remove/force + +# Add or remove space before the ':' in 'b ? t : f'. Overrides sp_cond_colon. +sp_cond_colon_before = ignore # ignore/add/remove/force + +# Add or remove space after the ':' in 'b ? t : f'. Overrides sp_cond_colon. +sp_cond_colon_after = ignore # ignore/add/remove/force + +# Add or remove space around the '?' in 'b ? t : f' +sp_cond_question = ignore # ignore/add/remove/force + +# Add or remove space before the '?' in 'b ? t : f'. Overrides sp_cond_question. +sp_cond_question_before = ignore # ignore/add/remove/force + +# Add or remove space after the '?' in 'b ? t : f'. Overrides sp_cond_question. +sp_cond_question_after = ignore # ignore/add/remove/force + +# In the abbreviated ternary form (a ?: b), add/remove space between ? and :.'. Overrides all other sp_cond_* options. +sp_cond_ternary_short = ignore # ignore/add/remove/force + +# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make sense here. +sp_case_label = ignore # ignore/add/remove/force + +# Control the space around the D '..' operator. +sp_range = ignore # ignore/add/remove/force + +# Control the spacing after ':' in 'for (TYPE VAR : EXPR)' (Java) +sp_after_for_colon = ignore # ignore/add/remove/force + +# Control the spacing before ':' in 'for (TYPE VAR : EXPR)' (Java) +sp_before_for_colon = ignore # ignore/add/remove/force + +# Control the spacing in 'extern (C)' (D) +sp_extern_paren = ignore # ignore/add/remove/force + +# Control the space after the opening of a C++ comment '// A' vs '//A' +sp_cmt_cpp_start = ignore # ignore/add/remove/force + +# TRUE: If space is added with sp_cmt_cpp_start, do it after doxygen sequences like '///', '///<', '//!' and '//!<'. +sp_cmt_cpp_doxygen = false # false/true + +# TRUE: If space is added with sp_cmt_cpp_start, do it after Qt translator or meta-data comments like '//:', '//=', and '//~'. +sp_cmt_cpp_qttr = false # false/true + +# Controls the spaces between #else or #endif and a trailing comment +sp_endif_cmt = ignore # ignore/add/remove/force + +# Controls the spaces after 'new', 'delete', and 'delete[]' +sp_after_new = ignore # ignore/add/remove/force + +# Controls the spaces between new and '(' in 'new()' +sp_between_new_paren = ignore # ignore/add/remove/force + +# Controls the spaces before a trailing or embedded comment +sp_before_tr_emb_cmt = add # ignore/add/remove/force + +# Number of spaces before a trailing or embedded comment +sp_num_before_tr_emb_cmt = 2 # number + +# Control space between a Java annotation and the open paren. +sp_annotation_paren = ignore # ignore/add/remove/force + +# +# Code alignment (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs +align_keep_tabs = false # false/true + +# Whether to use tabs for aligning +align_with_tabs = false # false/true + +# Whether to bump out to the next tab when aligning +align_on_tabstop = false # false/true + +# Whether to left-align numbers +align_number_left = false # false/true + +# Whether to keep whitespace not required for alignment. +align_keep_extra_space = false # false/true + +# Align variable definitions in prototypes and functions +align_func_params = false # false/true + +# Align parameters in single-line functions that have the same name. +# The function names must already be aligned with each other. +align_same_func_call_params = false # false/true + +# The span for aligning variable definitions (0=don't align) +align_var_def_span = 0 # number + +# How to align the star in variable definitions. +# 0=Part of the type 'void * foo;' +# 1=Part of the variable 'void *foo;' +# 2=Dangling 'void *foo;' +align_var_def_star_style = 0 # number + +# How to align the '&' in variable definitions. +# 0=Part of the type +# 1=Part of the variable +# 2=Dangling +align_var_def_amp_style = 0 # number + +# The threshold for aligning variable definitions (0=no limit) +align_var_def_thresh = 0 # number + +# The gap for aligning variable definitions +align_var_def_gap = 0 # number + +# Whether to align the colon in struct bit fields +align_var_def_colon = false # false/true + +# Whether to align any attribute after the variable name +align_var_def_attribute = false # false/true + +# Whether to align inline struct/enum/union variable definitions +align_var_def_inline = false # false/true + +# The span for aligning on '=' in assignments (0=don't align) +align_assign_span = 0 # number + +# The threshold for aligning on '=' in assignments (0=no limit) +align_assign_thresh = 0 # number + +# The span for aligning on '=' in enums (0=don't align) +align_enum_equ_span = 0 # number + +# The threshold for aligning on '=' in enums (0=no limit) +align_enum_equ_thresh = 0 # number + +# The span for aligning struct/union (0=don't align) +align_var_struct_span = 2 # number + +# The threshold for aligning struct/union member definitions (0=no limit) +align_var_struct_thresh = 0 # number + +# The gap for aligning struct/union member definitions +align_var_struct_gap = 0 # number + +# The span for aligning struct initializer values (0=don't align) +align_struct_init_span = 0 # number + +# The minimum space between the type and the synonym of a typedef +align_typedef_gap = 0 # number + +# The span for aligning single-line typedefs (0=don't align) +align_typedef_span = 0 # number + +# How to align typedef'd functions with other typedefs +# 0: Don't mix them at all +# 1: align the open paren with the types +# 2: align the function type name with the other type names +align_typedef_func = 0 # number + +# Controls the positioning of the '*' in typedefs. Just try it. +# 0: Align on typedef type, ignore '*' +# 1: The '*' is part of type name: typedef int *pint; +# 2: The '*' is part of the type, but dangling: typedef int *pint; +align_typedef_star_style = 0 # number + +# Controls the positioning of the '&' in typedefs. Just try it. +# 0: Align on typedef type, ignore '&' +# 1: The '&' is part of type name: typedef int &pint; +# 2: The '&' is part of the type, but dangling: typedef int &pint; +align_typedef_amp_style = 0 # number + +# The span for aligning comments that end lines (0=don't align) +align_right_cmt_span = 2 # number + +# If aligning comments, mix with comments after '}' and #endif with less than 3 spaces before the comment +align_right_cmt_mix = false # false/true + +# If a trailing comment is more than this number of columns away from the text it follows, +# it will qualify for being aligned. This has to be > 0 to do anything. +align_right_cmt_gap = 1 # number + +# Align trailing comment at or beyond column N; 'pulls in' comments as a bonus side effect (0=ignore) +align_right_cmt_at_col = 0 # number + +# The span for aligning function prototypes (0=don't align) +align_func_proto_span = 0 # number + +# Minimum gap between the return type and the function name. +align_func_proto_gap = 0 # number + +# Align function protos on the 'operator' keyword instead of what follows +align_on_operator = false # false/true + +# Whether to mix aligning prototype and variable declarations. +# If true, align_var_def_XXX options are used instead of align_func_proto_XXX options. +align_mix_var_proto = false # false/true + +# Align single-line functions with function prototypes, uses align_func_proto_span +align_single_line_func = false # false/true + +# Aligning the open brace of single-line functions. +# Requires align_single_line_func=true, uses align_func_proto_span +align_single_line_brace = false # false/true + +# Gap for align_single_line_brace. +align_single_line_brace_gap = 0 # number + +# The span for aligning ObjC msg spec (0=don't align) +align_oc_msg_spec_span = 0 # number + +# Whether to align macros wrapped with a backslash and a newline. +# This will not work right if the macro contains a multi-line comment. +align_nl_cont = false # false/true + +# # Align macro functions and variables together +align_pp_define_together = false # false/true + +# The minimum space between label and value of a preprocessor define +align_pp_define_gap = 0 # number + +# The span for aligning on '#define' bodies (0=don't align, other=number of lines including comments between blocks) +align_pp_define_span = 0 # number + +# Align lines that start with '<<' with previous '<<'. Default=true +align_left_shift = true # false/true + +# Span for aligning parameters in an Obj-C message call on the ':' (0=don't align) +align_oc_msg_colon_span = 0 # number + +# If true, always align with the first parameter, even if it is too short. +align_oc_msg_colon_first = false # false/true + +# Aligning parameters in an Obj-C '+' or '-' declaration on the ':' +align_oc_decl_colon = false # false/true + +# +# Newline adding and removing options +# + +# Whether to collapse empty blocks between '{' and '}' +nl_collapse_empty_body = false # false/true + +# Don't split one-line braced assignments - 'foo_t f = { 1, 2 };' +nl_assign_leave_one_liners = false # false/true + +# Don't split one-line braced statements inside a class xx { } body +nl_class_leave_one_liners = false # false/true + +# Don't split one-line enums: 'enum foo { BAR = 15 };' +nl_enum_leave_one_liners = false # false/true + +# Don't split one-line get or set functions +nl_getset_leave_one_liners = false # false/true + +# Don't split one-line function definitions - 'int foo() { return 0; }' +nl_func_leave_one_liners = false # false/true + +# Don't split one-line C++11 lambdas - '[]() { return 0; }' +nl_cpp_lambda_leave_one_liners = false # false/true + +# Don't split one-line if/else statements - 'if(a) b++;' +nl_if_leave_one_liners = false # false/true + +# Don't split one-line while statements - 'while(a) b++;' +nl_while_leave_one_liners = false # false/true + +# Don't split one-line OC messages +nl_oc_msg_leave_one_liner = false # false/true + +# Add or remove newlines at the start of the file +nl_start_of_file = ignore # ignore/add/remove/force + +# The number of newlines at the start of the file (only used if nl_start_of_file is 'add' or 'force' +nl_start_of_file_min = 0 # number + +# Add or remove newline at the end of the file +nl_end_of_file = ignore # ignore/add/remove/force + +# The number of newlines at the end of the file (only used if nl_end_of_file is 'add' or 'force') +nl_end_of_file_min = 0 # number + +# Add or remove newline between '=' and '{' +nl_assign_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '=' and '[' (D only) +nl_assign_square = ignore # ignore/add/remove/force + +# Add or remove newline after '= [' (D only). Will also affect the newline before the ']' +nl_after_square_assign = ignore # ignore/add/remove/force + +# The number of blank lines after a block of variable definitions at the top of a function body +# 0 = No change (default) +nl_func_var_def_blk = 1 # number + +# The number of newlines before a block of typedefs +# 0 = No change (default) +nl_typedef_blk_start = 0 # number + +# The number of newlines after a block of typedefs +# 0 = No change (default) +nl_typedef_blk_end = 0 # number + +# The maximum consecutive newlines within a block of typedefs +# 0 = No change (default) +nl_typedef_blk_in = 0 # number + +# The number of newlines before a block of variable definitions not at the top of a function body +# 0 = No change (default) +nl_var_def_blk_start = 0 # number + +# The number of newlines after a block of variable definitions not at the top of a function body +# 0 = No change (default) +nl_var_def_blk_end = 0 # number + +# The maximum consecutive newlines within a block of variable definitions +# 0 = No change (default) +nl_var_def_blk_in = 0 # number + +# Add or remove newline between a function call's ')' and '{', as in: +# list_for_each(item, &list) { } +nl_fcall_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum' and '{' +nl_enum_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'struct and '{' +nl_struct_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'union' and '{' +nl_union_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'if' and '{' +nl_if_brace = remove # ignore/add/remove/force + +# Add or remove newline between '}' and 'else' +nl_brace_else = add # ignore/add/remove/force + +# Add or remove newline between 'else if' and '{' +# If set to ignore, nl_if_brace is used instead +nl_elseif_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'else' and '{' +nl_else_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'else' and 'if' +nl_else_if = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'finally' +nl_brace_finally = ignore # ignore/add/remove/force + +# Add or remove newline between 'finally' and '{' +nl_finally_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'try' and '{' +nl_try_brace = ignore # ignore/add/remove/force + +# Add or remove newline between get/set and '{' +nl_getset_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'for' and '{' +nl_for_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'catch' and '{' +nl_catch_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'catch' +nl_brace_catch = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and ']' +nl_brace_square = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and ')' in a function invocation +nl_brace_fparen = ignore # ignore/add/remove/force + +# Add or remove newline between 'while' and '{' +nl_while_brace = remove # ignore/add/remove/force + +# Add or remove newline between 'scope (x)' and '{' (D) +nl_scope_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'unittest' and '{' (D) +nl_unittest_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'version (x)' and '{' (D) +nl_version_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'using' and '{' +nl_using_brace = ignore # ignore/add/remove/force + +# Add or remove newline between two open or close braces. +# Due to general newline/brace handling, REMOVE may not work. +nl_brace_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'do' and '{' +nl_do_brace = remove # ignore/add/remove/force + +# Add or remove newline between '}' and 'while' of 'do' statement +nl_brace_while = add # ignore/add/remove/force + +# Add or remove newline between 'switch' and '{' +nl_switch_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'synchronized' and '{' +nl_synchronized_brace = ignore # ignore/add/remove/force + +# Add a newline between ')' and '{' if the ')' is on a different line than the if/for/etc. +# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch, and nl_catch_brace. +nl_multi_line_cond = false # false/true + +# Force a newline in a define after the macro name for multi-line defines. +nl_multi_line_define = false # false/true + +# Whether to put a newline before 'case' statement +nl_before_case = false # false/true + +# Add or remove newline between ')' and 'throw' +nl_before_throw = ignore # ignore/add/remove/force + +# Whether to put a newline after 'case' statement +nl_after_case = false # false/true + +# Add or remove a newline between a case ':' and '{'. Overrides nl_after_case. +nl_case_colon_brace = ignore # ignore/add/remove/force + +# Newline between namespace and { +nl_namespace_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'template<>' and whatever follows. +nl_template_class = ignore # ignore/add/remove/force + +# Add or remove newline between 'class' and '{' +nl_class_brace = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in the class base list +nl_class_init_args = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in the constructor member initialization +nl_constr_init_args = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in a function definition +nl_func_type_name = add # ignore/add/remove/force + +# Add or remove newline between return type and function name inside a class {} +# Uses nl_func_type_name or nl_func_proto_type_name if set to ignore. +nl_func_type_name_class = ignore # ignore/add/remove/force + +# Add or remove newline between function scope and name in a definition +# Controls the newline after '::' in 'void A::f() { }' +nl_func_scope_name = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in a prototype +nl_func_proto_type_name = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' +nl_func_paren = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' in the definition +nl_func_def_paren = ignore # ignore/add/remove/force + +# Add or remove newline after '(' in a function declaration +nl_func_decl_start = ignore # ignore/add/remove/force + +# Add or remove newline after '(' in a function definition +nl_func_def_start = ignore # ignore/add/remove/force + +# Overrides nl_func_decl_start when there is only one parameter. +nl_func_decl_start_single = ignore # ignore/add/remove/force + +# Overrides nl_func_def_start when there is only one parameter. +nl_func_def_start_single = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in a function declaration +nl_func_decl_args = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in a function definition +nl_func_def_args = ignore # ignore/add/remove/force + +# Add or remove newline before the ')' in a function declaration +nl_func_decl_end = ignore # ignore/add/remove/force + +# Add or remove newline before the ')' in a function definition +nl_func_def_end = ignore # ignore/add/remove/force + +# Overrides nl_func_decl_end when there is only one parameter. +nl_func_decl_end_single = ignore # ignore/add/remove/force + +# Overrides nl_func_def_end when there is only one parameter. +nl_func_def_end_single = ignore # ignore/add/remove/force + +# Add or remove newline between '()' in a function declaration. +nl_func_decl_empty = ignore # ignore/add/remove/force + +# Add or remove newline between '()' in a function definition. +nl_func_def_empty = ignore # ignore/add/remove/force + +# Whether to put each OC message parameter on a separate line +# See nl_oc_msg_leave_one_liner +nl_oc_msg_args = false # false/true + +# Add or remove newline between function signature and '{' +nl_fdef_brace = add # ignore/add/remove/force + +# Add or remove newline between C++11 lambda signature and '{' +nl_cpp_ldef_brace = ignore # ignore/add/remove/force + +# Add or remove a newline between the return keyword and return expression. +nl_return_expr = ignore # ignore/add/remove/force + +# Whether to put a newline after semicolons, except in 'for' statements +nl_after_semicolon = false # false/true + +# Java: Control the newline between the ')' and '{{' of the double brace initializer. +nl_paren_dbrace_open = ignore # ignore/add/remove/force + +# Whether to put a newline after brace open. +# This also adds a newline before the matching brace close. +nl_after_brace_open = false # false/true + +# If nl_after_brace_open and nl_after_brace_open_cmt are true, a newline is +# placed between the open brace and a trailing single-line comment. +nl_after_brace_open_cmt = false # false/true + +# Whether to put a newline after a virtual brace open with a non-empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open = false # false/true + +# Whether to put a newline after a virtual brace open with an empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open_empty = false # false/true + +# Whether to put a newline after a brace close. +# Does not apply if followed by a necessary ';'. +nl_after_brace_close = false # false/true + +# Whether to put a newline after a virtual brace close. +# Would add a newline before return in: 'if (foo) a++; return;' +nl_after_vbrace_close = false # false/true + +# Control the newline between the close brace and 'b' in: 'struct { int a; } b;' +# Affects enums, unions, and structures. If set to ignore, uses nl_after_brace_close +nl_brace_struct_var = ignore # ignore/add/remove/force + +# Whether to alter newlines in '#define' macros +nl_define_macro = false # false/true + +# Whether to not put blanks after '#ifxx', '#elxx', or before '#endif'. Does not affect the whole-file #ifdef. +nl_squeeze_ifdef = false # false/true + +# Add or remove blank line before 'if' +nl_before_if = add # ignore/add/remove/force + +# Add or remove blank line after 'if' statement +nl_after_if = ignore # ignore/add/remove/force + +# Add or remove blank line before 'for' +nl_before_for = add # ignore/add/remove/force + +# Add or remove blank line after 'for' statement +nl_after_for = ignore # ignore/add/remove/force + +# Add or remove blank line before 'while' +nl_before_while = add # ignore/add/remove/force + +# Add or remove blank line after 'while' statement +nl_after_while = ignore # ignore/add/remove/force + +# Add or remove blank line before 'switch' +nl_before_switch = add # ignore/add/remove/force + +# Add or remove blank line after 'switch' statement +nl_after_switch = ignore # ignore/add/remove/force + +# Add or remove blank line before 'synchronized' +nl_before_synchronized = ignore # ignore/add/remove/force + +# Add or remove blank line after 'synchronized' statement +nl_after_synchronized = ignore # ignore/add/remove/force + +# Add or remove blank line before 'do' +nl_before_do = add # ignore/add/remove/force + +# Add or remove blank line after 'do/while' statement +nl_after_do = ignore # ignore/add/remove/force + +# Whether to double-space commented-entries in struct/enum +nl_ds_struct_enum_cmt = false # false/true + +# Whether to double-space before the close brace of a struct/union/enum +# (lower priority than 'eat_blanks_before_close_brace') +nl_ds_struct_enum_close_brace = false # false/true + +# Add or remove a newline around a class colon. +# Related to pos_class_colon, nl_class_init_args, and pos_class_comma. +nl_class_colon = ignore # ignore/add/remove/force + +# Add or remove a newline around a class constructor colon. +# Related to pos_constr_colon, nl_constr_init_args, and pos_constr_comma. +nl_constr_colon = ignore # ignore/add/remove/force + +# Change simple unbraced if statements into a one-liner +# 'if(b)\n i++;' => 'if(b) i++;' +nl_create_if_one_liner = false # false/true + +# Change simple unbraced for statements into a one-liner +# 'for (i=0;i<5;i++)\n foo(i);' => 'for (i=0;i<5;i++) foo(i);' +nl_create_for_one_liner = false # false/true + +# Change simple unbraced while statements into a one-liner +# 'while (i<5)\n foo(i++);' => 'while (i<5) foo(i++);' +nl_create_while_one_liner = false # false/true + +# +# Positioning options +# + +# The position of arithmetic operators in wrapped expressions +pos_arith = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of assignment in wrapped expressions. +# Do not affect '=' followed by '{' +pos_assign = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of boolean operators in wrapped expressions +pos_bool = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of comparison operators in wrapped expressions +pos_compare = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of conditional (b ? t : f) operators in wrapped expressions +pos_conditional = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of the comma in wrapped expressions +pos_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of the comma in the class base list +pos_class_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of the comma in the constructor initialization list +pos_constr_comma = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of colons between class and base class list +pos_class_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# The position of colons between constructor and member initialization +pos_constr_colon = ignore # ignore/join/lead/lead_break/lead_force/trail/trail_break/trail_force + +# +# Line Splitting options +# + +# Try to limit code width to N number of columns +code_width = 90 # number + +# Whether to fully split long 'for' statements at semi-colons +ls_for_split_full = false # false/true + +# Whether to fully split long function protos/calls at commas +ls_func_split_full = false # false/true + +# Whether to split lines as close to code_width as possible and ignore some groupings +ls_code_width = false # false/true + +# +# Blank line options +# + +# The maximum consecutive newlines +nl_max = 2 # number + +# The number of newlines after a function prototype, if followed by another function prototype +nl_after_func_proto = 0 # number + +# The number of newlines after a function prototype, if not followed by another function prototype +nl_after_func_proto_group = 0 # number + +# The number of newlines after '}' of a multi-line function body +nl_after_func_body = 0 # number + +# The number of newlines after '}' of a multi-line function body in a class declaration +nl_after_func_body_class = 0 # number + +# The number of newlines after '}' of a single line function body +nl_after_func_body_one_liner = 0 # number + +# The minimum number of newlines before a multi-line comment. +# Doesn't apply if after a brace open or another multi-line comment. +nl_before_block_comment = 0 # number + +# The minimum number of newlines before a single-line C comment. +# Doesn't apply if after a brace open or other single-line C comments. +nl_before_c_comment = 0 # number + +# The minimum number of newlines before a CPP comment. +# Doesn't apply if after a brace open or other CPP comments. +nl_before_cpp_comment = 0 # number + +# Whether to force a newline after a multi-line comment. +nl_after_multiline_comment = false # false/true + +# Whether to force a newline after a label's colon. +nl_after_label_colon = false # false/true + +# The number of newlines after '}' or ';' of a struct/enum/union definition +nl_after_struct = 0 # number + +# The number of newlines after '}' or ';' of a class definition +nl_after_class = 0 # number + +# The number of newlines before a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. +# Will not change the newline count if after a brace open. +# 0 = No change. +nl_before_access_spec = 0 # number + +# The number of newlines after a 'private:', 'public:', 'protected:', 'signals:', or 'slots:' label. +# 0 = No change. +nl_after_access_spec = 0 # number + +# The number of newlines between a function def and the function comment. +# 0 = No change. +nl_comment_func_def = 0 # number + +# The number of newlines after a try-catch-finally block that isn't followed by a brace close. +# 0 = No change. +nl_after_try_catch_finally = 0 # number + +# The number of newlines before and after a property, indexer or event decl. +# 0 = No change. +nl_around_cs_property = 0 # number + +# The number of newlines between the get/set/add/remove handlers in C#. +# 0 = No change. +nl_between_get_set = 0 # number + +# Add or remove newline between C# property and the '{' +nl_property_brace = ignore # ignore/add/remove/force + +# Whether to remove blank lines after '{' +eat_blanks_after_open_brace = false # false/true + +# Whether to remove blank lines before '}' +eat_blanks_before_close_brace = false # false/true + +# How aggressively to remove extra newlines not in preproc. +# 0: No change +# 1: Remove most newlines not handled by other config +# 2: Remove all newlines and reformat completely by config +nl_remove_extra_newlines = 0 # number + +# Whether to put a blank line before 'return' statements, unless after an open brace. +nl_before_return = false # false/true + +# Whether to put a blank line after 'return' statements, unless followed by a close brace. +nl_after_return = false # false/true + +# Whether to put a newline after a Java annotation statement. +# Only affects annotations that are after a newline. +nl_after_annotation = ignore # ignore/add/remove/force + +# Controls the newline between two annotations. +nl_between_annotation = ignore # ignore/add/remove/force + +# +# Code modifying options (non-whitespace) +# + +# Add or remove braces on single-line 'do' statement +mod_full_brace_do = add # ignore/add/remove/force + +# Add or remove braces on single-line 'for' statement +mod_full_brace_for = add # ignore/add/remove/force + +# Add or remove braces on single-line function definitions. (Pawn) +mod_full_brace_function = add # ignore/add/remove/force + +# Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. +mod_full_brace_if = add # ignore/add/remove/force + +# Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. +# If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. +mod_full_brace_if_chain = false # false/true + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 0 # number + +# Add or remove braces on single-line 'while' statement +mod_full_brace_while = add # ignore/add/remove/force + +# Add or remove braces on single-line 'using ()' statement +mod_full_brace_using = ignore # ignore/add/remove/force + +# Add or remove unnecessary paren on 'return' statement +mod_paren_on_return = remove # ignore/add/remove/force + +# Whether to change optional semicolons to real semicolons +mod_pawn_semicolon = false # false/true + +# Add parens on 'while' and 'if' statement around bools +mod_full_paren_if_bool = false # false/true + +# Whether to remove superfluous semicolons +mod_remove_extra_semicolon = false # false/true + +# If a function body exceeds the specified number of newlines and doesn't have a comment after +# the close brace, a comment will be added. +mod_add_long_function_closebrace_comment = 0 # number + +# If a namespace body exceeds the specified number of newlines and doesn't have a comment after +# the close brace, a comment will be added. +mod_add_long_namespace_closebrace_comment = 0 # number + +# If a switch body exceeds the specified number of newlines and doesn't have a comment after +# the close brace, a comment will be added. +mod_add_long_switch_closebrace_comment = 0 # number + +# If an #ifdef body exceeds the specified number of newlines and doesn't have a comment after +# the #endif, a comment will be added. +mod_add_long_ifdef_endif_comment = 12 # number + +# If an #ifdef or #else body exceeds the specified number of newlines and doesn't have a comment after +# the #else, a comment will be added. +mod_add_long_ifdef_else_comment = 12 # number + +# If TRUE, will sort consecutive single-line 'import' statements [Java, D] +mod_sort_import = false # false/true + +# If TRUE, will sort consecutive single-line 'using' statements [C#] +mod_sort_using = false # false/true + +# If TRUE, will sort consecutive single-line '#include' statements [C/C++] and '#import' statements [Obj-C] +# This is generally a bad idea, as it may break your code. +mod_sort_include = false # false/true + +# If TRUE, it will move a 'break' that appears after a fully braced 'case' before the close brace. +mod_move_case_break = false # false/true + +# Will add or remove the braces around a fully braced case statement. +# Will only remove the braces if there are no variable declarations in the block. +mod_case_brace = ignore # ignore/add/remove/force + +# If TRUE, it will remove a void 'return;' that appears as the last statement in a function. +mod_remove_empty_return = false # false/true + +# +# Comment modifications +# + +# Try to wrap comments at cmt_width columns +cmt_width = 0 # number + +# Set the comment reflow mode (default: 0) +# 0: no reflowing (apart from the line wrapping due to cmt_width) +# 1: no touching at all +# 2: full reflow +cmt_reflow_mode = 0 # number + +# Whether to convert all tabs to spaces in comments. Default is to leave tabs inside comments alone, unless used for indenting. +cmt_convert_tab_to_spaces = false # false/true + +# If false, disable all multi-line comment changes, including cmt_width. keyword substitution, and leading chars. +# Default is true. +cmt_indent_multi = true # false/true + +# Whether to group c-comments that look like they are in a block +cmt_c_group = true # false/true + +# Whether to put an empty '/*' on the first line of the combined c-comment +cmt_c_nl_start = true # false/true + +# Whether to put a newline before the closing '*/' of the combined c-comment +cmt_c_nl_end = true # false/true + +# Whether to group cpp-comments that look like they are in a block +cmt_cpp_group = false # false/true + +# Whether to put an empty '/*' on the first line of the combined cpp-comment +cmt_cpp_nl_start = false # false/true + +# Whether to put a newline before the closing '*/' of the combined cpp-comment +cmt_cpp_nl_end = false # false/true + +# Whether to change cpp-comments into c-comments +cmt_cpp_to_c = true # false/true + +# Whether to put a star on subsequent comment lines +cmt_star_cont = true # false/true + +# The number of spaces to insert at the start of subsequent comment lines +cmt_sp_before_star_cont = 0 # number + +# The number of spaces to insert after the star on subsequent comment lines +cmt_sp_after_star_cont = 1 # number + +# For multi-line comments with a '*' lead, remove leading spaces if the first and last lines of +# the comment are the same length. Default=True +cmt_multi_check_last = true # false/true + +# The filename that contains text to insert at the head of a file if the file doesn't start with a C/C++ comment. +# Will substitute $(filename) with the current file's name. +cmt_insert_file_header = "" # string + +# The filename that contains text to insert at the end of a file if the file doesn't end with a C/C++ comment. +# Will substitute $(filename) with the current file's name. +cmt_insert_file_footer = "" # string + +# The filename that contains text to insert before a function implementation if the function isn't preceded with a C/C++ comment. +# Will substitute $(function) with the function name and $(javaparam) with the javadoc @param and @return stuff. +# Will also substitute $(fclass) with the class name: void CFoo::Bar() { ... } +cmt_insert_func_header = "" # string + +# The filename that contains text to insert before a class if the class isn't preceded with a C/C++ comment. +# Will substitute $(class) with the class name. +cmt_insert_class_header = "" # string + +# The filename that contains text to insert before a Obj-C message specification if the method isn't preceded with a C/C++ comment. +# Will substitute $(message) with the function name and $(javaparam) with the javadoc @param and @return stuff. +cmt_insert_oc_msg_header = "" # string + +# If a preprocessor is encountered when stepping backwards from a function name, then +# this option decides whether the comment should be inserted. +# Affects cmt_insert_oc_msg_header, cmt_insert_func_header and cmt_insert_class_header. +cmt_insert_before_preproc = false # false/true + +# +# Preprocessor options +# + +# Control indent of preprocessors inside #if blocks at brace level 0 (file-level) +pp_indent = add # ignore/add/remove/force + +# Whether to indent #if/#else/#endif at the brace level (true) or from column 1 (false) +pp_indent_at_level = false # false/true + +# Specifies the number of columns to indent preprocessors per level at brace level 0 (file-level). +# If pp_indent_at_level=false, specifies the number of columns to indent preprocessors per level at brace level > 0 (function-level). +# Default=1. +pp_indent_count = 4 # number + +# Add or remove space after # based on pp_level of #if blocks +pp_space = ignore # ignore/add/remove/force + +# Sets the number of spaces added with pp_space +pp_space_count = 0 # number + +# The indent for #region and #endregion in C# and '#pragma region' in C/C++ +pp_indent_region = 0 # number + +# Whether to indent the code between #region and #endregion +pp_region_indent_code = false # false/true + +# If pp_indent_at_level=true, sets the indent for #if, #else, and #endif when not at file-level. +# 0: indent preprocessors using output_tab_size. +# >0: column at which all preprocessors will be indented. +pp_indent_if = 0 # number + +# Control whether to indent the code between #if, #else and #endif. +pp_if_indent_code = false # false/true + +# Whether to indent '#define' at the brace level (true) or from column 1 (false) +pp_define_at_level = false # false/true + +# You can force a token to be a type with the 'type' option. +# Example: +# type myfoo1 myfoo2 +# +# You can create custom macro-based indentation using macro-open, +# macro-else and macro-close. +# Example: +# macro-open BEGIN_TEMPLATE_MESSAGE_MAP +# macro-open BEGIN_MESSAGE_MAP +# macro-close END_MESSAGE_MAP +# +# You can assign any keyword to any type with the set option. +# set func_call_user _ N_ +# +# The full syntax description of all custom definition config entries +# is shown below: +# +# define custom tokens as: +# - embed whitespace in token using '' escape character, or +# put token in quotes +# - these: ' " and ` are recognized as quote delimiters +# +# type token1 token2 token3 ... +# ^ optionally specify multiple tokens on a single line +# define def_token output_token +# ^ output_token is optional, then NULL is assumed +# macro-open token +# macro-close token +# macro-else token +# set id token1 token2 ... +# ^ optionally specify multiple tokens on a single line +# ^ id is one of the names in token_enum.h sans the CT_ prefix, +# e.g. PP_PRAGMA +# +# all tokens are separated by any mix of ',' commas, '=' equal signs +# and whitespace (space, tab) +# +# You can add support for other file extensions using the 'file_ext' command. +# The first arg is the language name used with the '-l' option. +# The remaining args are file extensions, matched with 'endswith'. +# file_ext CPP .ch .cxx .cpp.in +# +file_ext C .h \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/bootloader/update-waf.sh b/3rdparty/pyinstaller-4.3/bootloader/update-waf.sh new file mode 100644 index 0000000000000000000000000000000000000000..b2fbba0c2b928fd1fd85602fa548010ae6b29b49 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/update-waf.sh @@ -0,0 +1,69 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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) +# ----------------------------------------------------------------------------- +# +# Helper-script for updating waf + +VERSION=2.0.20 +ARCHNAME=waf-$VERSION.tar.bz2 +URL=https://waf.io/$ARCHNAME + +# Extensions to include +TOOLS=--tools=cfg_cross_gnu + +# Core tools to add: use only those we actually use +# Currently unused optimization +#c_compilers=clang,gcc,icc,msvc,suncc,xlc,ar +#c_tools=compiler_c,ccroot,c_config,c_aliases,c_preproc,c_config,c_osx,c_tests +#CORETOOLS=--coretools=$c_tools,$c_compilers + +KEYID=49B4C67C05277AAA +KEYURL=https://gitlab.com/ita1024/waf/raw/master/utils/pubkey.asc + + +# remember where we come from +BASEDIR=$(realpath $(dirname "$0")) + +function cleanup () { + cd "$BASEDIR" + echo >&2 "Removing temporary directory '$WORKDIR'" + rm -rf "$WORKDIR" +} + +WORKDIR=$(mktemp -d) +trap cleanup SIGINT SIGTERM SIGKILL EXIT + +cd $WORKDIR + +# If Thomas Nagy's key is not already present, add it +gpg --list-keys 2>/dev/null | grep -cq $KEYID +if [ $? -ne 0 ] ; then + echo "Adding Thomas Nagy's PGP key" + wget --no-verbose $KEYURL + gpg --import pubkey.asc +fi + + +echo "Downloading waf archive" +wget --no-verbose $URL +wget --no-verbose $URL.asc +echo + +echo "Verifying archive signature" +gpg --verify $ARCHNAME.asc $ARCHNAME || exit 1 + +echo "Unpacking archive" +tar xjf $ARCHNAME +cd waf-$VERSION + +echo "Building new waf file" +./waf-light $CORETOOLS $TOOLS + +cp -v ./waf "$BASEDIR" diff --git a/3rdparty/pyinstaller-4.3/bootloader/waf b/3rdparty/pyinstaller-4.3/bootloader/waf new file mode 100644 index 0000000000000000000000000000000000000000..30c6bb431f8a25c747617766c1eec1df782379e3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/waf @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# encoding: latin-1 +# Thomas Nagy, 2005-2018 +# +""" +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +""" + +import os, sys, inspect + +VERSION="2.0.20" +REVISION="a3a5949fb45bcd86794262f86d7977a7" +GIT="x" +INSTALL='' +C1='#*' +C2='#&' +C3='#$' +cwd = os.getcwd() +join = os.path.join + + +WAF='waf' +def b(x): + return x +if sys.hexversion>0x300000f: + WAF='waf3' + def b(x): + return x.encode() + +def err(m): + print(('\033[91mError: %s\033[0m' % m)) + sys.exit(1) + +def unpack_wafdir(dir, src): + f = open(src,'rb') + c = 'corrupt archive (%d)' + while 1: + line = f.readline() + if not line: err('run waf-light from a folder containing waflib') + if line == b('#==>\n'): + txt = f.readline() + if not txt: err(c % 1) + if f.readline() != b('#<==\n'): err(c % 2) + break + if not txt: err(c % 3) + txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')).replace(b(C3), b('\x00')) + + import shutil, tarfile + try: shutil.rmtree(dir) + except OSError: pass + try: + for x in ('Tools', 'extras'): + os.makedirs(join(dir, 'waflib', x)) + except OSError: + err("Cannot unpack waf lib into %s\nMove waf in a writable directory" % dir) + + os.chdir(dir) + tmp = 't.bz2' + t = open(tmp,'wb') + try: t.write(txt) + finally: t.close() + + try: + t = tarfile.open(tmp) + except: + try: + os.system('bunzip2 t.bz2') + t = tarfile.open('t') + tmp = 't' + except: + os.chdir(cwd) + try: shutil.rmtree(dir) + except OSError: pass + err("Waf cannot be unpacked, check that bzip2 support is present") + + try: + for x in t: t.extract(x) + finally: + t.close() + + for x in ('Tools', 'extras'): + os.chmod(join('waflib',x), 493) + + if sys.hexversion<0x300000f: + sys.path = [join(dir, 'waflib')] + sys.path + import fixpy2 + fixpy2.fixdir(dir) + + os.remove(tmp) + os.chdir(cwd) + + try: dir = unicode(dir, 'mbcs') + except: pass + try: + from ctypes import windll + windll.kernel32.SetFileAttributesW(dir, 2) + except: + pass + +def test(dir): + try: + os.stat(join(dir, 'waflib')) + return os.path.abspath(dir) + except OSError: + pass + +def find_lib(): + src = os.path.abspath(inspect.getfile(inspect.getmodule(err))) + base, name = os.path.split(src) + + #devs use $WAFDIR + w=test(os.environ.get('WAFDIR', '')) + if w: return w + + #waf-light + if name.endswith('waf-light'): + w = test(base) + if w: return w + for dir in sys.path: + if test(dir): + return dir + err('waf-light requires waflib -> export WAFDIR=/folder') + + dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) + for i in (INSTALL,'/usr','/usr/local','/opt'): + w = test(i + '/lib/' + dirname) + if w: return w + + #waf-local + dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) + w = test(dir) + if w: return w + + #unpack + unpack_wafdir(dir, src) + return dir + +wafdir = find_lib() +sys.path.insert(0, wafdir) + +if __name__ == '__main__': + + from waflib import Scripting + Scripting.waf_entry_point(cwd, VERSION, wafdir) + +#==> +#BZh91AY&SY¾üÊJŽÿÿÿ°DPÿÿÿÿÿÿÿÿÿÿÿm€(¬#$0E0m b#*Ü÷0#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#${^ö•&°Š^½¢©T 2Ð×ovŽ™ ­#ëU¶D @¾íÅ>©ÎÊnŒZÅPï§¼°N\•=·“£®,öÜÆa¶×A‹Ó×]wgx{z‡¤·¾ö!Q×MàdžµO°Ptëq½Ýô÷¼[_^øï¾óÕiÞ»GÀ{a'®÷¹uÜ#$z#$#$#$#$žÀà=æ§#$@#$f#$¦]ÑEÒÃF@hTQ¶#$#$Pª®ëoG å@ H l…IJRšŒ@J‚€j„¥E#*P#$M@#*ë½ ÷¾Çžõ¢C¾½³ÝÖi¢ï¼÷™QÙL«mGlºbVÙ±¯}öû¯}½}½½ÞŠ%—y(ö÷»éôúßoò»çºó·s=­žÖö÷–ößOxͼ^{Òï®§>³Ç·Ûs7zÝHXö_vö#&×›-€¤ UUR’¢ŸFÙÐîïuçç^æÛf‚¯yÈõllÕQMžàÁ@ €Rª" àòbÐ%ÙÞ7­›{ƒ{ÛÞå4e€^¾½óç;ÖôÎÇݪ*R½´ù#&S6¾›Ú÷WJÞ1§Jë½oE­ˆÊ¾mãbJ7lõîUk¶|ž·Œw¬ë%WμY0k4I÷·"z³©8àuÛ/»vâîóÇÎù}íÛeÖ:Û³Ê[]›vÞ÷eïZ›ežqs»]‹X7»Ç‘¼õ/°yŒ}Ì8zY»îõÑ}¸tñÍïi.Þ-›[ÝÝuõî«/·/¾:‰níÛwm|Ž\ùçn]•ÌÎð÷§ßw”^c¸úÔëËïNÛ×M>ÙƒDët}îö÷q§ËÌ#$8ÇzõïjõÍöí÷ƒÊ#$#$*JIH%ëW˜#& R#*ªÖÜ}m·¼+»Ñ¼°ÆÎ[•@z[ºvª…-;ÛÃ+ÞåìjÐìµ½r·wg€#$w]u®p#$ïy·´í¸æÞÓ` m´ðû»Ùè£Ñ½îÃ`úq]c£-C±ïs·»»T÷Çtz-Œ#$ò®q­WUÚû,x÷Ó¸öðW¸ê©yٮ绫ÚÚ8öí#&wÙ¾kg·ko“Á¡»îÞ^>mƒ7·ws‹·ŽÏKů»ZÖÖ6zøÙæÌouJæ®ãÐ$Š€ §y‡@턯¦»ïžÅ]ÙÀëï`íW©ð›§£ªwv§ž¹÷¾ãØÀ R_ugן@(÷Þí8C·y½VAW}ÞÅWaæ=]ϾòÝïrUÕòØo¯™§½#$ #$:@}töç[#&y¸íºÚxnW6$® Iæ½½í.»¨(#&c¾ÝuiÃÞ#$ö;Ó›u7¯ ìõ­#$#$#$#$(ÛÙÛYí»·`¯^žOš)9³Õ·'o{We¶ôëvÖƒf ( -,+JP«¦FÛ«´»ªäã­#&yÙÉboW_/w¾.Ÿ}ñ.dº÷kàÂÙ¸»k§>{Ïzîg’©^òÞ²ÌæÕÃwX|ÏŸcæ @û>·ž·Íëï‡_|ï¹NؾiñM#$#$€#$F†€#@LiM•Dõ= Ó 43SÔõ6I¦ ”Ð!#$ ¡=&šüš"zj=LMFƒj#$#$#$#$#$#$“H‘42›MÐ'ªci=)驵Aú“5‡¨m¥#&#$#$#$#$#$$õJH£F¦S4OJŸêb˜ÓTôô$dmhÓFPhb1©€@hd#&12#$‰!#*`  ™5=4ÓIŠiéSñM¦‚§¤dò5OÓASôiO(@#$#$#$#$’@#&&š&šd1OJfˆ4ʧù#*ŸŠž£õGêP~š£É©¦ƒ@#$€#$#$|?þø­¶¥qÿ5Îm¶å]Ý®åú[mNͰSåÛju!J$ÛH !Jˆ€Š¢w¬û·láâbêžÅv( ÄÍL:-JÁ+÷‰Nâ—.'éêéK“?Ä×¼ÿi›ØÌ»00ff #&–f`Ï)öOÄœœÕÕu™¹Xz|B¹¢êáëx›eQ„TLbL;ÝUÚ‰‰Ù`DŸnÚÙò!Fâ"‘#$‚‘Š*#$$AFAAŒQI#$E‘Qª„E¢„å9KHDME@ E@‘Ê(¡eB"6 B'œT¢#*XPR#*„¨-ŠÅ^Ùjª«{ÖÛkTÌ™ %š†™¦ÊcŠKh™•–2*R(¦ÚM…4 ’h%-MF¨Ø4-£4–M$F‰h„5¤M¥4#$m–404Å•6I²Z-DP”²ÒšVˆ…–Ù ˜2ŒÌÆ5$j5Q’h4›!„Pš˜ÒP&,²Ò1¦Ñ©¦È–ÒÛYjÚ4ÌIc3$ÈJ"LdÛM¶šmjJ6RZkc-KlÍ”´™&b(*"Íi‰¢ÉE2(Ù6„B¢Í#h¤Âh¨BÆÅ%L#4¨ˆ*SFÄ!#* %ˆ†Dd‘µ"d#K,Í ’$R„Œ3 ¥•– ÌH¤²4Rɬ›Ñb‰…H²C%”„JiØ’“"Œ¢J2hhɉIF‘($Y64I"‰™KH"™‚lk$3b$ÆiLØ&#ZM€Iea°"II±¨ˆ’ÒQAITBH”h"‘DÁ‰†C%!L¡2’Rj,ÍÂF“RD“ˆKJl IKbH²Ì¢‘fIDÙL‹&ÄÈŠQ›"`™TÓB4¥ÍE€²A¦š1©Y,‘±e„Ȧ’b ©lÓ XÒb’T’Ê2ƒŒ™¤m„M4Q5¨YA¤²2‹%&DÐM2‘*4f6”)1¨6ÄÊA! ŠLÆIÐDHe„Ål“*fjÌQl¦±(‰™#&HÌB‘±¡B²š*H¢ŒI’M’1±Œ•4LÑbÄe&Rf`RÈÊͦ±‚"‰)4Ôɦd†2ˆ¤6hÚlQf¥Jd͆ÈÉ2)´…HÆQ”¤E*ŠI1$šShÒj4‰41Á2£M2&ÐhÆ`Ë!4™F"Æ™³" M’”Å(&Y¦BjÍ›²X ’K$d5EPZ XR$Ècc1 Â2ƒRXÔY¤J&ŒF©M(Z˜hI™IŠ$2ɲ#)‚E,Ñ¥1„5„ÒfDHh•ZVÖŒ(MLÑ”ÈÄÒŠI™ Ô¦Ø³E˜¤Ò2„²Ê›4E©•…²ŒHQMˆÊQ4!)µù]•5Hˆ# FmccV5Re)ME*44´†¨ØFÉ5Y†¡¦c(É)€•$[" ÄÌÓ-’Ú(Ĥ¥¨ÚŠBÑ“L&¦³,,jAL´³&L©6‚£eZFŠ›(El™dU*šJ˜SfÆc3VÆ‹"+dÓ+"TË)µ’¬¶R”Ó"¶F€ÉHÚ‹ÖX’Ò)j-PUF$5´d¨(ªÉ¬”mQX¨©hš"d±b-±Z†Z6±°“& l˜IE š4c!i’iZ[lhÚbk¤‹hƒVªjÖX¨d&S5-dÑ&ÈÄ„JZFÅ’+jU›c¦Ò©e*e–©‘ÚU,‰”µ54lBÚS)¬±a²É¬¬²²ÌPPÊ$HXÑ,3Iˆ‘"A¡1ˆSZ5LÀ„¨¡’ÔVI¦“&Š,ŒM–)ÙH6DVYŠbS1(RQ³LÀÄZ-2BdfÓi1DcY#*aÓ"bBŒh¤¢ÃLI5ˆÄ†ÈA“5Q%2)%–P5„©€24i1¦•3d„ÔZ2E–£lÊI(È2ÌYMb"˜m&ˆÆ¤Ù,"‚ÉXBŒ–Š#*) Äš4Y”©””³+0ÉX¤h²FÄ1©£llÖŒÑ#&”¤Øi–6̤µŒ„d ŒƒLM‰*J“(QRšcd¤Ía)šŠÁ¶eM%“c4›FÌD¦”d´ÊÖDi¡¦bRÂbHA²ŒÁÉ¢©¤lQ¢³I²S±1RBA¨´[ QQ¨£c2Øi0¤“CK&FÆ)D±FÀš‚³M‹!™D*2%,¤©‘Ñ´V5F¨“E¦¨ ±Aš’¨Ô0Ä, Õ)CkƒJi–BŠ4IÌ’ÑU"Äe+l´¢¨Ö4˜",š"™”-J) ÉD•6%2­Dm ÑS"Å’¢ÆÅšZŒ•ŠÖ“•”Û)­CD©%2 ¥E#&’“D˜ŒFÙ)#&d™‚#&Y4¨Ò›2¦"–Ô•“Y‘¤)¤šŠ¢Š&›RV-”’ÆÉˆ£Ei0d•©¢ÒËE¢ÔIµ£mJ6“["UcRQb‘M ‘LŒÈÌi©ˆÔÍ’fÔj*’ÆÖJ–U’´&Ô‹cQIQLÛ4µFѱmŒ[Rf2”BY¬¬Mh¬Y¥FÔFÓeE)ZQ˜¨Fi²£E‚$¨ÒmbÌ©"´•±dÛ)2´hÖ3Lj2cFÑVÛ6«‰•#*‰‰(ŒT0Ñ£bšIµ0ÆI¶±m‹LÕ´cZËISC-l¥“mMM²ÔšjÄÑ”Q£#i%#&f¤Ô™›,¨Š#$¨ŒI&RLˆÈÄd#lšÑL­šOùu„G¶OùˆÔ1l[ý‘Ä»)Y@Òð£p¯å/ƒó=—ìãÝò«s /KA£I£èjÉþûbÌk?éÿ¦š“Xƒ-Z–Ÿ÷ódlÈŸÌÿ¿ÖYLâtäPCoý*ÎàTX,N܆Rv„¶Ñ¶ÁW«Gèüÿ»kDçúHÿOþ°Ïþ…­ð$”)k•Ä^ÊpVPªBÙ= ÿ<;Z|Ù¡Aæmñ–1{¨O‚QJý™OtæYÇ2ÆÛï¹à‡0l•<$öCÇ»ÃGþ;åÃÃdõq˜ˆT©¨×0Î’Å5ºÂŠ?FÁ¨Áib7-Q‹6¦æ h˜Â¤kܲþs¯>û¬Îjäl>N×2Bvû3¦q>{ÖN T¬%H¤‘#'œÇ(mß*07ºJŽRÀV_+rÈÍé¹£A²E%æàjm ß92<_ü`Û`]C×%cDh³à蹯ƒ^M9a~wk”–ÄcŸ‹ùýxö|í÷;èöïy}··QMa\B¢Éõ÷ápßû­$Y½_ËÅP5‚‘Þd‰ð¤•×öushéŒ>ˆ@¸ZA ÄÈA(·—ó>?è®Ú2ÙðO#*zŠý}©¨h ÓÞXæb™j$Löï;¯1åÑwÊæÝ,†¢®mÓÎâ$ØØŸvëá5¼m" ×âî§Ï×è«Åõ­È¦WÊä[crä?žÝjýG§‹›…ûý|0!K´øüwJ=õ»–:MxÇ-‚*Å„ü²ÈtñãÒI*È,m* ­AgÂøêº[aPë—Ä+:B²5]r»ÎìnQÒ¹s$%-kX‹èüð†$4´?öübáá)<%ƒÑK",4B´gÛãxïIî3‹E*擯ó´Þ¯¡—÷¿>Htû ‰ê¥O(|zÓ`*;Iþ¸¡ð»F“é â ’Ä{«ãt6âƒßÝÑ®{øßÀáÁÊE^ÐÆE—åç#bm|,Ž¢öÝëµÉ"ƒ2š‹R*(Q‹ƒ‰fbã³._‘üÛ‡u·Ó1:v2§»Û>‰Ó'¡<$Ž_ݲjALóÌë×1Yp}:޲Jý¿/㟋ώz4ýIôòß)îßßaR"¨â@¨OqdpeŸbyNtØ):iÑ~ôÜ*Ÿ&c\ƒ÷P¹ÕnŒ18Å•SâÕ@úÒ¬DwŸ ßâÿ6ÙòWŒ‹Òv˜[A'ðÜÈ5*2,vÙŸr`cíKÇÈgv'uíÞyü•}-ãôéÌë ÉÚ”ûéE‘Gì¡‚y|l½¿r#ƒK(ª'Mzfm‹EV ñ ‰¿}—ßîÀùr‘`Ú¤”0}¯“ø^mzk!ÄJj vÊ…é²HQ”jJŠ}™@Á:Ol¼Jœ¼Jª ¥aR#*I6ý›×«È”«%CÚîX´Wë )ÓbþT«AQžÔ뽘ƒÕ½s“o™ô·Œj#&%>öЍ¢Å#Ú~yЇéÛ÷uã#&OÔ–m…d­`{wdĪÃßàPÔF19{Ëäoʶô¥+>ÇO¿ÖÝ”%²lCåhE©/Ãέ"D‹#*ÁaÛŒ™B£RŒZ„öq"},!Òù¤áùÓí{𔬢)2ÜJüZ·ëqds÷\Síè«çí¼OwŒÎ¾Ì0°ŠŒ=¢žv!‰ÛR’½Ï…Ø%Ì ¾ÊÃÝ…IÒN31öà ë!DSçK?zÛÛQb|üŠ">èe>4³Ý<×+ÞˆõptÝœq\sذè‚Sa6¹wºæXb¡Û8|¿¾ç²x†ʨn2á(M»¸xÎlÓ»zùÙs|X°ãzÃ#$Š#$Œ™…‘¥7ÙÜÌ}Þöžiº¡PbGÿU:û)?GÒ×½#*ç‹î†¿µðÌ8”¶J"?VÄOMS»üm?Ëö{ý4ý{Oaøáx—áöó?_#* í’°gƒqêÁˆ»õ¸[›dÕ¶¡Õ§\¿vSõngE¢F-ØáY÷¦Æ1©tØ@£ß.ÈG(Ò 0âwü)áßûm{+Õ¿m_£'»ª'»OâŠI#"=6µ®ä´1(C™¥ö }ž•‹”ü1År_4ê…™ÏwÖs'ÃÍÒ>í8Íh î­ì%¤ˆÇÎ  >3‡¤KA´](Ä0?ô°=mNa™÷–®YñL ò~XW]!ÌÉŽ³©1¥òN”8žrâ÷\É~Nu{Æs°¹é*äîÔí3MÅoÜ>=nJíý³#*ÁýW\µç¶šèWçë5®S&2¡÷ݧÇëq|*¢MtD-?³÷æ}ƒ<­Å¹ØªTgœ`ðoP¤¤5ÌÜË=êõ̆œÊ¿^¸g¿ áJÁö6)Úwõ§Áœôõ¡ûòñ§”†Pþ#&Ê m5¥LlkŽ«é0Ä»0 :)ÛÊwDÏÁ1÷åü¶©œ1Šxòê䇷¼iK¸é$Ë'º!sÖµÆÐñzût¨#?{#*³´õ¥×åeGÏŸ>¡ç Î«O9ŒÆ‹þJf[á+:·¦­yB¶{pÍgÕÌ뢇Vš—èzÌ'Ýæšª7úzÃñaÏùs×Ó©Å÷ºã––Ûl2ÊL¹ «¬:é J„É0 œ}“E¹p.“qÊy,ê/ß a8nñMs;3k­xkˆgr²ªö:d¸ñpƒÏNÆ'x¨1Ù1ÛN#&xrÛc™ÆN9Õð!CÞã:k:ÿ« õ˪¡Ýè[mP„ˆw#*]¬§ÔíôE/…ò¢Rsh|½Sï^pK¦Ÿ”·¾\±Êˆd¡Òù»ü8ïí­¡ï®ŠI…¤÷d.ÉÆôh¢‚ƒðµ•#*m±÷eXŸ:AÒQMiÏÏ&}ÍÚ¶—ÿY…QXŸ~~_é~?á3´=¿.V¾ù_ L“Â'¾Œ„ØRøÑV„‚"¨(²?••ŸÌÏÌOéús?—調ñ±õÖîx ¯Ô‰„|WÇ3ÚYÆø[ÂÝ—ÚšÔ!ySÊæ–çM‡hßžAªªv w°+ÅÚ|Ý®>¨ÙL„fr˜jšj—”ò˜„£Nq7ú)“)þwÀý:§¨ /–'î¾1}!¾\‘¼¦m±-{jåTÛVóBÝ`uävÀ¤ýc¸°ì#2Óä8twk«ëëÏ(vÀ÷`SùéQQ_ñ&ŒóóæGð¥‹èåÀc‹#Sás>ÖâYÕÃôuxJ>­(gù_{À­0îß²‚ˆgxâI/WjPËJáÛCºc£³¦õ¶üžkÏEµÙ`p¦<»Í?[‰´âuÎÓ2˯Ÿy>Òe 2}Ÿ;gú/¤Ý±Oêecöl÷tó§°æ–\öÎù' Ï–ÿIÎ)ø gÒ•}-ˆa×NÙ†•ôDf`#B`B@½¨h:íÌê"(¨²°°ûÃ(cÎÚ‡ÚÏ'#$¨(öŸgWaòK÷R³Ý*“«ñÏ›í—m7áL`ЬßדÛ]ûóž^e­sÆC¤#*gèÃÙÙÝ?†yËÊR¥ÆÃÈÉŸ“O ª‘‹ð TZ_—ã¾þ÷'¶÷{§ÝêÀS¡ÆȉŒûXUã*‚±MÑ3²bçëå¡wÛ(Wؘ‚@éD;Ü~Å=øÁZY@š¿?Ûk²Y‹ÓlßÝôŒ,aï?æÐÎÊÑöÓ‘]²~ú)|œñzD!KÉŽ²#§«ÔÎÈI2-)pàÖ"Ñ®Œìz ÿë8^×;¼ëøæ[(/ËÂc#¤ÎMü~¸º»7„Œvv†kBoq ùqò©¶Ô=¤,C—ÛþÔ~Î}(Š*„/…ÝkåliÅøï5½+ÍÆ*um¦ˆ‘«ÒÂ3ÒžPºn¡¢¿oá(dŠªýÏ<É_Z‡Ã#&T‰2I1§B¥ôAqú!åö¡ìLøpéâçáÆÜÉÓw”lÄp¢þJ¥ÐÌîùN¡vAzrVUˇ³ \ ÚçîR„¥6„a|Ðôþ9¼¨æ£›Vk_­Öbý»ïçœ:¥[ã,ð碅O·Nžóž´=°:%o.6ºo¿‡1ø½,*½äão¯lJ)q»„&ˆí :!ÿ\s5S·X©²V#$¹¹Ü±R÷œ_‡;…Ý2‚{#&(æ‘W¨è7aüãÑ~칎ÏçÌÆ¸ôŒ®=ÜA"ñFHîœw$ÁÁREO@(H ‚jçpr)'¶Ölð;#&:ªÞùa&ÁèÿZþ¨gÑ 1)aâ"ISùõááÓž£¹wÕ;ã*ñ¤ñògJ–SàrFª|ïeö0$™UjÌòÙÙÃ~C¾bí Ö”^Ø<ùãq @*.¿®€;·Vú†D´¨Ê¨\™RÄ@½´ßkűù!òëì<¸ªÑ)ÈHIaÙâ¢4Oª*`ˆùC?Ÿze8T!§_ÉÄ’TGÜŽН¬?µÇUö¦lˬ8•¯¦»ùÒc? ^uWkO¤³é•v ^_kô–뉳Ìj-Ý%ç^èô‡I„©uPËJV}Ý\7T’=£ÙŽð˜Æ÷½8­â4ð‚VSZboÚ©œ9¤òÝcÑôNªóÆ÷%² L »ÈNÊK›~ùó†Ó$È„ñ×\æ')PÒg²öÎ×¹DI&üY¶‡¿O¼êŸs;‚£"Ïô|yÍ-±=÷|=ª7Ž’ÄÇGùÐiP/®°ðM"£/#$X‡d#*$‹ –E"4ÅCýäh·­ÑαÓY,I沚Y2¿G‡ÇJŠVŸßQ¤«|“„(†•«ŸËÇ lZ#&!ÐVÎSÃ~÷úà#èVÐ;ãy%*,½zH@ˆGÁœ¶¾[c­åh?KŽõ´=ÓQÛWú¬ø..lúYa§mø4£<¸¢]R¿Ëºž.нxwMOϪbt5Æ[E¹íLXŠ"ÏÉ?.‚ûòÌý­1ßçv"ÿB$Õ’(ïÆAÿsŸ¯âàzŒMç}¤1$ŽÒ-(ç%…±ä3µgÎF9QÝú¹Â\žXÕZoÖ볤¿vÓ:§;M+Ë*OŠÞJ©¸‘í^Óïâž×ü_?:‡ø_@‡”ôœÆšê2‰ˆPDbÅbÞ›¦ÙrÌÌ"¬×¢Ÿ§–~)Y¯% á:jL?èOÐ_Z¾•šc|8H˜Iµm‰é† 1Y Ý¿ XšÐèʦk1fªk³Åë$ôT™;üœýi¡9†ŽvÏàÜ•Ë4.#*Kh~¾¹O²‹žÞ•úÖêcƒ­ný¼Ä>-oÛD¿Ô?¯²Æ#&M\þ³š•ßñL•ØÍ2Y„gù¢á…FQ–7–ÃËU>,Þn"LËË/hHŒå’ŠZJ,Q™¿¿jí…Š¥"-•˜…E«@¥F”‰Ÿ $’2IQ„µIoŸ³¦Ça0^ß•ü=|6zÐ>¥CŒÛÃÔÄ“à*I%É"9Jw¾%:0)÷WþˆÝD:O°Ôéý½û™#*7cOí?±T5Uü¾>¾œàKÒÿÄlO~K¡r…B–Ýþ¿˜Kø[›”b§Š'Ë¥kè‘o-×±ŸÂ¤æ˜Ç>qR#$yDAÀ„yDn‰¾H7[,&‹{C˜¨#ÆÞêˆëãR˜Ù6nöo¦b¯§%-7›£½¸‰£<.•ŽeJÆÞfOæV{圭J¼ÂŒ£?»õÓµ­!’ÇwÍÅ.‹(åܵåo£i¸Ú(#&J¾>vJCŠúÙ#*%‚¥¥‡ȇâ–X³Š·Ê®?ÝvË~1I‡…QÞѱž&2ñb¨o³Kôä·U¸Q¡íhDQ‚¶,˜Óímz ù>ÇbãâÑF&šU=ä[¹ìWdYåN—¼×Cÿ¼n•J1j¯Ìf^EžÅ "?»Ä4KKRÈûße;˜\F¡ªÈòùÌíc€’Arî-CŸÖ¸Ä@W½ÛSq¼]sVŒéTBÞÁàòœlž4†qéÁÒÔŽ b¼ Ý;`záQ ¼‡=$cªrÚU”ÍÂü< i ,¾ßÙ¼ø–š]"VW…Dw夵f\q#&ÖÜ Lé†ÙvA„ͧ©]Ýaˆ•Ÿ­æ…%ª[ëÛM={üðé¨qOO—šj|Uø°çëÌÛ#$ªÁYÊ~#&sörÅw}–I’¿bHêû¢'£¶Ts:†q`N#*²,!»†€e'%¬ÕAÙuʨËIΉ°®¬žÝAÚõg"©9®8½¹}Üœ Ìr™‰0Êá.xŸ"!á0£yªP÷¶)s—jÛNE.vxÒ"V ¥òÏBB„™rQŒ­K5‹M‹\±¶7Ì9ÊõMbxŒ ¡Á I™!­#$N­@‘±úø_¹÷úÇ|ßéÍ×r³Ö…×ï±²t›Þøé°@G’ËsÛIAB¥©ð:÷å!Ú)†­—,¸æÚ÷áK-1bèð}H<¸¯4æqÆ=–ëÒ¸xqåcî©|ÄÿB#$÷ª„Q´mü¿Ã{é'gLã¥oe¼»[Á"Â=ïè±×[ >;:´¼0þÒ!sh¥‹‹þÜ¿Ñ\éö^¢Ž‡«[žP§â€Ù""0=ô öQÏו:c¢AøÁ#&œ¨wÈBN‰ÎXú(rïñÿ—¯·WÑãÆJ{´ÿidëf7¶ä.Ú£#$ÆI¶‰¡à:±Óð˜îƒ@MãÏQ8,Àl¾7ò£¦±•´¾ä‰¾péœÏo”¢zb¿áÈð¬#*W•g}¥ÿ2'§dëÍÌuvÅÀZ9_1X9ñ™^/²éNÞ~k»×›íÃV±nšQ¯ó^Uúk”tÕº/¶Ô = ”]8ôäì UpÖpÚ>Øa,°;„Æsƒò#Úµ;ÇsJsPÒݨ=»é¿UÔXøÓ<&15 Á*ž7/”ôÞFØhÝþ-ß¿–%΄N)¤8ªoƒ0ý,¡Ç¡/Q§®ÖE#‹#uê'Þ,*½àŒ#&Ö!ø·ß~6ðDD½ÿ‚¨l÷é 0Mßý ‘$2ÂiÿwBwm«rœìqŠðñWUQý–H‘M’HñvÛÊŽ`QåðŠ9'èN°=óòózm¢E¸æÝG‡¯W“Õí¨éõkP üŠ(†þe^ïM/ˆö”ëû5kwM†##$ëÈ7*’X…°#&-sà^gb¨gZíH0#$" óþ›y}öKg»ÎØ>£÷âéw^»å #&ƒk¢À¢ ÐM?r! ̯Ãñä܈ÿ,Æú)ˆð0ÂóGõ­ä¯ø¹©½vþ#*i™¥Ld˜oq$!!…öcš#$~:y“MIÐ:ÏshÂý߸³õ}‘¨DGTƒðç´—Ytmù\ÝÔé¶[Ê#*ÕƒÜy¿Ñ¾úIYgüÓ§Ž¥¾OÜQž´˜‹e QL Y7é/bÜ #»»»1Æ…%àŽZá³ÙAn%ãËAf:ˆe“ðïŒÀ]õ$ý´§WDi€Üï .[uz¡ïõÊ‚7ÒËÿWʈ?׃À…ö TuŠ»a‘ÛÐ÷ý2 ­ê¸âƼ6¥—Ãcj™b²ø§¹¹!2Gñÿ ýžÅò°`꿾·ÿY˜80×?¢X³¥³õG-=J|}{¶>ݽç4vWÁÎo›N±ô{¼°n¶Ì‡gõoË˵MŸ79DwÚ—fíkŽøþ›¢¤§üŽÏݘÌ:C#*k£öY¶ÁÇ£ßÇuÛo¨wÈ?”}ûù(Îóô?Ó»÷zä‚¶µè)¢ÜEÂÙàоïvºýÞT]qE"„¿aU€õ9z_{¾VdÀj04Ùï>·öÍŽÍ…¤bé#*$qæww~ÕtA{n•ÑOùÁõ_éèΧf2ï¦}X°…Ny“‚›«(ÿ²ÿ¬:§7B÷yϳ¿¯;#¯l±Æ Pâ™îCÑ'@ˆò—WÑß /wògÛâO€ý›ë¬ð—*©\6é ‚š™î¡~Ì–"0Fb>B¾Æ£)¦àßw@‚Fë¶¼8šÎ>ƒUc[õÏ©Ÿ5xÆÐG(é­heÓ&ÀÂô%±;‡ÍAÆÚ²LMmŸXªc7öV,-˜|û¼j’Bÿ²lÔ|¡°Á&ÌÏ#*8ÄÄüÿÜjè>‘æ;›3ޱ½°2 I[Ÿ'áSáÚ|±'±1tÃO‡O«ë^ym#”=l6êNqÖCXhû¨ë%…¼‚tŠVº4YDš Þº™ÆÅQbª@[#*Éoþ7Nâ€B«Ž}œî7Âôw1/°7ÅxºáXþùä –!!#¡6Ðýu¦;(JHëâÞÜ?3 àl£½/´Œ ÖÙRŽÓ—&OšÏ ³Hà _z¤ÌÌkÙŸ–vxù#vïÃg´ÝìâVÒŸÔªôm„Ð’Œ€Ê÷.È wˆÖZÿ@÷j“ÒtÓ#&,í"Œüæ%X9&)Þ<ÍqŠor`×¢ËV¨v]± ÒMu½“…|>83=_ÙÆCa½Æ©µ*©¦´-ĵEÓ©‘‡M#&Ü»êþØ.›Œ¿B6åî;E{QäIåZ&È…ÖEBh°>¼1á-õ¢`qž»åEî…"7BL>¯sP¿]5e‡³dŠ”‚]àránÁÃf¼Êƽö¹saO«Ž£ ¢Àèè#*²Jã¿€n©ÇA#°ëäïî/ º†¹Ï/ëÅìM#&¥uÔØûYåqá$ j…6Âght]«ÝG¤v@J€‚‚{pÖNc½@|{Û…m}M‚âæ‰ï¤É5 *Ý0ƒÚþ³pì÷Æûèw6Yd#°ëÕf±+aÃyŠŒ!#$½µX›g—ŸµËI#*\É+ôVÆzŽ!ìÀÌ•Œ…‰ÆfdöóÌYˆUDE!M&¢»”(‹%ÍÆ¸hšÀñïÌRwžýçÙÎaà­òWüœ¬‡ßß'ß“À¯˜Œe‚,ÂI÷…±lëN&Ìi EtÏe²Ð¶ß?ÏûÕCf@“YCÑ¡!"D-;yPo!ýy¾_š¸Ÿ?î£or‹´q#&Ù/Äêà‚™êŒ #€(‰EsHµTPÀX!n A  @/2<‚Áö„5œ;E8Ö¼¸ˆ—Hnmš®Ê†,/xypШe{D‡aÓ•EøR-¡‚Åì[U¤’ã:”·RÒ…C®TY…¥Š(÷×]‚±GeÁŽ`Nñ'¥ÐÎõWé†y0ÉžòŒ´j”†¡1¾‚sŽ©3#*´à/µÀZ¯ó#*Q»zºB/]%«ö¯‹ù.á†Û´á@¢…œ€Æf2TPPžì¡ádÑ•[ -‰”F`V‡&s^õ'òUt\ûi³Dm)eŠšIR»r³®®Ž„âܨ<ãžj(~ˆÉàû#cÈÌꆄÊ4 u»x!ª#&œ‡lÆB¤q$ê–hêC£.˜sàÐäf.rõSÛ-#*„¡çÅ?ÍÍÔ»p fîƒG9=¸K+Ò磴E) R±ÑØt}íêQÓÌ­¼Ú8‹… gPá‚Ä%Ú/ßA êˆ“K¡¢Hë´)UžqVY6ú²‹Ÿ {^¼IVxOÐpLÂÅG•#&pÙøþßa”²‹ó²Z®Ÿà/?ÒCè\Ó=Õî£Ý=k9•q™šV#$1\÷¾Ã8t¹50ç'#&’®[²/õQí—¿„LõÞÁh‡I'D$#$œi/0Á“)EŠ*(›l-̉amëO†c9ºFæÜ´ 0”%˜ôÅ`õN#ý4Ó7óÚ+ßÊøÍRôŒ÷ýºâÆ„V2˜[Ea³4ÃQ,¸Œú9ôÒlé4êÃ"TAµ#&#$P#$úP¾JlhÖuÕ9E?/‡¿;·™>}é¬Õ5³Ã…µCxÞ>Ué(¤í$ÿ'EŽÇjWµÜYö˰oTFËgf†hMy`M$.,1[vÀ°!ê-cÆ í¾MÍÚ—ŒÕ­JYv9LgÔÀÓÕ€*…dZyª¾Òp.Zô‡™tdØÊ$@Ê gŽ®]Çn˜kCB†TÂAƒé #*Dú4üù÷‹ÊYb!Éh…¼y#ßÖìeVßI6µNv­£¸ìûúyÎJZv#&r¿»d÷÷ô-yVÛ üW^o·c*L×—‡8Vpý£ê´Œ`ߣ6hp†åà¿ns¢Ãƃÿ—nDš5™Ø>ÏÊm‰œ—8Y±ºÙŠZ*šb›¦a+x¡ îy.s„Ì Û’kèä8FŒ°eƒNðÖj’°ÓðÌïKÔÙ$Éá&Ã[6B'Fsr…#&Êaä#$¿^¿Hq/‘9ó­ñžìËlá¦â[jT!ᄬ(˜…Å€|PήÛcLŒbÄÑ–¸ˆÓ&0Y8ÀËIÚêLÊòÇ9’jonB‰+1#&T…£H"†Z©Òû«>·o†fÍ/¬.[_§NË ¥ûu³#$ñZ+1b«ˆ‘„*ÂWgžPÑE1«‹âèt l †[®Tb_\xKç…‰¬€åÅ^ˆ–0ÛùÇb;ª0O#$dzg?„ðŸ]é¿m@èü=Û¢'G (#&£ÛñˆJ&Poý‡òãÜÜÒ΋DÝ‚jT·D¿ªCÎï=¶«Çn€íô+ã^Í=¬S¾!uo#*oA¤€œðŽ'üA 8:dx¦QdÇ"Ž"…™jŒ—,-sY×.Œüú–oÃãýY'…œ½¶Ä£ê˜Üž²P25f°‘õËY¾#¥ö’ZÔŸvÌì‚w¬9§¸Ýô[7ä·‚ÛHýŸÅÌÐcâmáxò–<ŸWÆs‡p}ðçé"iУï‹ÕØa:jeâœ4›DæêF¶¤ðhd™‰~Û| é[¾¶ŸálÃÎBˆyðV¾)’UÓ1ê‡D@'#&w ÉÙ9Ä’K7]-¢à~ÍV’§éœ1N#&7ã³×q¾!;µ)Š‘(hà ˆ/£ìÜHÛ>*K·cœbXvý÷êLuàcÇ/'E§/nJ1· !LZü“‚2b~ƒüçb{ (Ùyqúoûá#*G¾9fv#»³ð˜gt¸±#¢Ò5ì†)ˆRå[ƒoèOm¶™ëZ·õ-•þ½t2Vð F¤‰D‡w­¾öó- J0ã†˰ f™0ÎB‚Í·lÝ¡o—e)Â4J  snTp²ÊÒu»hAL¹>]~Üá; Ë`¤1’• ˜Ô#* ,¨NÚŽqñ°ªEñJDC¦«G¢š9á“É­ƒŠÑǃË#&JËFXkÄÓ…¢E$©3;Å’\ǿϺñ˘¦a¡7°†ýq…×@ä¡»éÍ…²4µöíâ·BÓÖ}œÅd*-K.\ææ¢JÈUƒH×h²9¹‹©Áá>Xo—>pÌpÇ6n)ɱæÇ¥È½möœ@°nP’ê$»\°ÖõGg˜§€GÔÉK#*EŒÜpäRµþû—ƒ®4ø4a2>…F#*)ÈôuâS]Â|!û¹MM¶ï¡ß/lºTùl6ÙQµí®o³Ç“qpkb8ºB—,ÜT8,Lq,£ãzÂ=i$q#Ò¦)ÏkŸÍ˜ÕQÑy?ðÇÓ¶é2c¸&#&<É a0NÏŠsfEÛ‰²Áàÿ~-Mù¿òi²Ú‡S]E/}ˆ´l°&´,ÜŒ°ŸLä¶+]1eÂÄN0ë¢>w”½v}›û6©#*Ñs£pµ¦cdä|¥C€4#*©4œ­¾MÌr®ßá$ü~ù Õ•ÏcTaFŒÿ±”æ92N+R#P¼¶J1~_+®æN¿˜ž:4î”ý/I’­ˆW#~›¯Ïžw¾Òú#*7³är£ßOv(ÀäOÃì¦Ãÿ>`Ú à!pË;Â]š´¶9õÿO;úŒ¼„dcÛV‚>©¦7;{Ô&B:dðulusXnm6„³¡Ék¢ˆ/|ç£~ºÙ¾:Y:ƒ×Êsj "Äv£ ·5k¥ˆÕͺVj’J2A‚K`óƒ »#*T5¸dÌ´÷ÊLUò˜YöNÙ©ÏÙš“¯陵¨d38 7…ÊÔ¼1Z~Šÿ‰Ù: çœ}¡IæyžA;´Š¡õ€%*†ß^A¼Ën.EÇ·0=áʼGîmûC¾Žªd>·«ÚÚøŸüwŸ4/Ð/çRì‡R‡#&@tÝ &c?eß§`ýƒ‡ìÓ££­›TÒÚõKHæO,E‡º£Ó¾j‚?›Å;ï£pÑã7¹}Vϳ°˜¹ûß;I*Ò¬QÈ-?¤¾O| ]¤NÂuÅb ¿öæ¸þÅY%ñŸyýuà‹øî?M÷>¨*®Ì…#&&5•Áö#*PLÊçÀøc¿¹þNçŽ\Wõ‚4w®d[£* #$–=ð_ãúãÍÙðÞ;ø#$(ÇÇ/xßßúvÖ±fæ²Þ®{'iøW‡WSþÝöϯ×Ó07zmÓ#&ÇøØÃËñlÚ~ï᳞µÛx²#&åê†##&§¿SZø]JÈ8°qrB;Ù$ xö#&÷z¹6‰>‰uÃ÷ÎëcëõýXˆ „©°öý×ïE›…©7-fæ {çÜà§Ðû:‡û~O£çÏ vŠ;Þ¡ÁÊÊŒ¬°v(Ä´^îaâZ,«ZUª¬}ŽÏèUCe4wÀª¾ãeE&žo«âÛ.ˆ€NtPˆJ@J]ÍßÓmЮú\ú~sóÊ?sôôþâ½’áõÇÂëwú›ÒÉ#*-ˆÏSµ ŠfÔ–Mb6T±(¿ŒÄѦÅ_¿^ŽÛµk-Õ(¦ˆA´‘ãñvD¶•þ!ûÎûyýÆý¸ðÿ#ˆh‹Ógœ6ƒdR•ûWçÅ(b‡NÉa:i€6QÔ,ùäâ *ŠVKJ#& ÔHvU­Dœ!Ÿ–ý–øíü33ŸKW(³ChJvMÉM@@öîù~ÍçÑ|Ÿ¦± ”ëãí2ø6-­üVµ¹´kê}Ê–üU#óƒ”(€nŽ’t!PÙ;…%A­êUÑ#&·¤‘k|¢ÄQ ·*Š¢ˆ¡ë„OËÏ þCëÈøËɰùÑËk‚1=¢.…'Þ c²¨h9àÊ¿g')óÓ¬%õ¤¨ßTñ¹]å\…™ýVvâIlA{Vde—öè§ìjÅ‹çƒÐþÆ— mU3X!3D3%5ñ¿ê?‘'«ÀGö¸WÊuÈβ··?œøKUÌ7>¾Êç9À?˜ãEè¡‘ÍžŸSì&ß§œk_º6†â„ˆ;Àç˜L»œ¦õ7Üdí…CWüÿ®˜£UCÐá[mÙñ­Ól÷BvƒA>-%$"\Ÿ—-n²(¨DìLôϦ˜&Wãsh|î¸7âé…†àÏßõRm†ÀQøx¤þ´'øÜHy|=RêBÃþ{'BX*ÁbT#$¤­ýr.*K'”7Ç_·ðŸ6¡öþo7Ãç? ¿/Öß©âÞñ£èúE}KãæLéë=‡-§×£ iŒ­ý#\Ç÷Ýç¢ig1êàº}ÆAÝ.?\|„jøñ¾ƒõ›0[N|áxÃç«}»•áøçÞ£  ©è°iîýqp"$`{z—MÂêVsåbÆGœ¹ÄL¯“D„OmпõIGã=7›’ô,kµÞñ=~x…ÜË´érÆnþÍ>ø7IµR6»öd:¿np4·ƒ…t0ᷱ髾ƒ#&Cy8R3‚¦Žû :€¯ÜƘ1Ý@xvÞ¥ïPÊí8jß;­¢ÜìiŒëô4 ¨P>ÇøL.íïL13ÎôJa$Pá“~RÇhsϦøºf—Ê :Ž‹ôy\ ”lã'ߎ|Òí!¯#*B«à`á1˜·"Ót©o0(â>“û¨±9öÁäQ =aÛ\â8#Š… õ€¯§àÁÀ2”/˜uüÖ]äÂcE!U%äj?KùÅ]£Àð1»\3ç¾~_®Ø)>ŒÁ!‰YXw”]ÒÅc”²7ÛûPÍþï^ÝNü_˜°#*'±²iò¤©¨V é@øÁ2e<•H…IGª‰¨MaÍEU4V`ÚˆP$à¤æ§©³Ð¿‡k™5ýnU8´Geê^CÌ“'®Ø!é8>ô”P¥?žŸÎÃM¿#}“Å\˜˜÷„‰µÎg(? !ÎêLœC¢`Ÿ¦¨ÌÓöÿ_åÆLÿ‡š%ºÂÖ$ >5^•ì±ÎÔ¬Q]r÷ØmtÄ1…‰)i´è}y¼yüô¿¢ÓÕçDø¹Ü½~=˜¨æ>3ÀÒË ‰ïÒÀY('; b•ó«uêˆh9b@j,O[ž®p#ä¸lñ`I/ Tˆ¡_´*PZ!–9b¤'¥Û¬íëžóßÇPþäaü”i`hÚOÚ¦îH}‰:¿DÈ`¦0(’°†@BgëOª’á·€@ º•õÏôšgšwl¾‚F÷X(Œ€xºCÀð˜iϯ}_Ë«¿ßàú'êùþ{O¤Ù·›®#*¨=žŽaÓé¦Z“_ËôÙexpò‘ù«êlç終½vé|“J[]çrå®ÎÇz™^ÞÿÜÞQ«†ëí±ú-ÓÝ÷wìöüRæß`…¯+Ó5øáÕW¶¿èü9»¥ÃÆ|½ËÑÆMï¾qÖàÄß³?6‡!ØíÑ_r´¦þò-nÕønïáì×ÝèÑŸÚ/òã£ûú}î;x0\ÅGHÝÙæÒúuOG?Šºh<Ôè·‰ì7tèIÅ=_Î^èƒîG!#$ËŠ@i> ~÷áìÐîž|vRSÚb,¿˜|ïMøü_ã’›$%¦CœyzY1;­”7]ζSɾ-§wµï³™:i×£Ìù±Œ-¯`ú 5ò›#&~­_aµÜz±Nl:?œíŒa¦­«åxÕ#×HslÛ¦7Zú#&Qè{ù°ÞÓÁèy’­ÝлeËN¨æ;åDж|¿ý­€7Yf©[gTMoP6r)«¿+)Q/Ž{ýAmÓŽ<Œù{ÌaL‰ŽÂêUÒÊ™Ÿ­æ5öúÜ÷¨ÑÆÌ±ã¦NáçåÖø7òÃÅ¿‡˜îÖMÒÇ{¼„KÕéáv«¯íœ·òlwlÐÐð(aÐbÀò²wqéXÈoãKNïM‘èéÕW‘!Þ 9=;ä¬R‡ôqT,4ÿÒë#&»½^ÿf·Äs•šú)ÖþîüÞ‹³ŽµwÞñËÐhÏÍçê^ÿ¼ÓÝçóc³aÏѶúN#3'p¦±‘wí¾ÈmEWÕ]øY_¯GUötéƒP¶­…³gzü¢3Ò¾Œ62}Ò»„­îÔÉu?ç™}ÆÕ•¢‡£câú$ኪjßñîŽÙK{{HŽTû¼s¿Ë¶Ü(£—îQ"•ù*œø¯ÓóoÕÃ/õ9 Ûa¿íìO'óöûã=¾ßËSkÚtãúhWu®ë¼÷öãÚÐÙîÅ1‡ÜgíœàFÌ ËÙ¬røºÿ—#& ùÝh>ý¯ŠâvaåøWìÀu0Ù¦Kì[G˯‹¾WÏaÑÒæÎWæ2«¥’¿+½íÕ„£€ñ¾æ§Ž4ÿ_½¡·ïË4p¿6EN*ñvÞá^^ÏUžïgñ­ŸM£Ÿnx'ÀþÓðùÏÜÎí=Ú6Vîh&AÂþüý^Š÷[oò*<úáú?hEoÐV¹Ý:¼“K·ÈßÇñÔÔýþïÜJp(¡Ëœ ‚P”+¯ñçxg ü‡›KÙ~®À©–ÆE#&à ÆŽH#$õ0Óþ¾o@È~_ AõX¢ÐZb˜Ã¯C¹Ÿù}:¹Ôw¤'îîÔótŒ>ÿ7¾Þkùh>wÍêìϸKð36¾þ±Ûv>iê~£»ñ7éô7n\b­¶ulÙjìÛõl¿£ÌjBíÔ‰Íø¯zòƒGWeŽÓ›Hðø>)öö~Ýw >Îÿh÷Î8ޱæÃŸÉãP¹nM³í™ž]÷ÃùØÏÕ«uˆƒüWç§î¹&ÿò–ÄÑÐ>íŸßù~Ì)ÕnŸª¹¸i>^M[¿g®øÔk= Þ;þÌÜ0vÍ]¦áÓ£Éó/Ÿîã¥>Aû5#*ñx÷‰hP#÷ˆiøtrÛÄxý_qõ¥©Þ<>#—49U¢}|/`:ÁOá!±Ÿ·ƒL?`EP€5ý±Åë·›Q¦‰@DÜüÉÇ™†vy©Ûûûú»×ˆ»o?Oñ‘çÝ”±_ÐÜ]ˆ>[;ù.Žg~Ùï¬É’ý'Ã;iãš(úA?åûð²4$n?)ïUé8x¦/»ë؆ëçǯ™#©ûâ5²${e…E=z$#&…QÌï*3¯žñªû(¶:ºd¹ É.&d‚ouJÙˆEÆ«ÅÐ ¸X $™œŽ’»%c:è)ƒèšW£g\o×r÷\^ìóÂ7sñuÈT1Ú¤Du³¥õÛ¤G¯ßÆil(™r£ˆ®åàì¸z¦gI`C¨ —‘ÈtÂZ€€‡Ö›O#&;|“Óóz‚pÜÿVÿÐ/ö÷i‡;  ïwK‚Ült¶àûÃA§h2ñ9f#&wÊ•¥\9Ï'Œx5Öµƒ}§ê×Ç:Ôcr_(ÆÓ¶ŒbÞO Š'ìnîZåe5¬mŒKÌPŠÔÇø§µæïêûÔ_GÏNµtiÃh_g<ØüϬ§ª.†cºœáQ´ÕWmTÖûÒ¤A(;+RѦÒOßéxÓŽÔ•sÆä„µO· ºu Èg¨]¸[y½7Q¢Xá¢qxй4yp¯sÆŸp£ÇÀ3xí¾çYîL‡!! );ˆ¡DŽ½ÏˆæF³¬ ôŒ#&¬ÚåiRŽ~¾2Š>f4WèaÄ xk¶AXü›+Òÿ¨oçÖ:ý–‹="‰êÐñß#&ºVÈ®ÐÝÖR¯™ª_Î>ŠÂN”œyÈçzb#&ht$ßòO±×ÈaŠ(ëÓ¤#*ߪÍ<ú;m•õÕ_êì;q¹Ëô~v•#—k¶ÐiݦS-ÀóæL™ŸÞXùofÃqë|\í>߇ãíTÃÁåBõ> 'òókml<‡“¼ŸÃjíu¼¯ëåègü˜ü¹¦Û¹_ç»Í°zÔíI'n×ÓÙŠEýTŒ„NÒ{£÷VÇ[1äÊðœº?C«g§$ïñ~¯]PÌ7ˆ Ðo’õ½„:T’W›ŽÃú6Í*?§îdªS›!‰Fûñ¨ƒÙ%<£äù½#ÁzÆ#$åÍ—´ÆÂøëoŒçþ›àFÑçdø#”ÿŸøæd7w7jË•r—(Ê6­¨aûþ™#&g(Ó t¤Â¶tŒ¸L0‘s,°§êÙ¥¤ÉŒ”jÐÂa ®õúÌ‹!¢³·ËÄíœg-M†”ª†R¹eÌ®PÚ@ðMf¡Ä·#&#*Q™l`Áp)…²å`s,̤11­¨)9‡WΓU„‹«äƒUI¯×WuÅ3BªI’DøËD É{eeõ~Èᮎ%Øýs™”hfqœZþÝ:èÒ~¢ åêeÁTŽ)i¯÷È€šq;ãÌÏ^}Ê{#*!›eE‘2• D‹ªýíÿê~ßã?E¹ G#$çëÈìQMØZàá€Tæìöá9”D¢“L’ä²%R”æd ±QŠšfܖйU(Z,Ûgí:>Í ”ãoÃ/›ÇË3Nzh¿°OóaÛ.“}ìý8Òø$2p´iŸY²Í2ÆsªÝu×àÏ›}¾_‰ã£ÉfÊó{+¯_ìñú¶‰yGVÏÍöú3xìþΘtóëßö Sx¦)Ýú?Ž·;>|,䇞AÜ5â*Øï¶üþÍ&1s»¼Ë*ÙU“\uÙ[þiìŽc#&pÌ-£º¡ÂÂŒ2Ÿr#*#Ò$pèwØ]uGÞ<ÝžïËò_¯W³N¯WyDµ ñ+÷\h5U6ü\¶^®qL<,á¿£¯(tà¼,‰:×öWûŽr¹ÊöWÉÍ`ahaäéµü¿Ÿ^#*šÔ·S¾/àÞïë_“gñ·_Wq&Ÿz&ì¬ý“ü4÷Ì[% û"ûBßæ<ïά#*)oH¼6´?Õeö¯mŸó}qMˆ4¯·Î\—ªI%™UU­íQõA#¸ BiÉbFËÏü@HGÄyPüÿÒräú¥ù|˜D?åõÝcø*L·UŒ:¹y¶¢&²I$L8&ÿCœAÔwÊq>_”îó=s’‰vlÔ=A—Ç(Д#& F•E ÉÐL”þ¡ÉY‹Rëêb«¹Ï$›D„¿`§N½0¾«×##$îܹ„FÁ>®©[ï÷©ö˜‰lÌ…Ÿçî[ÿEÇŠÌ*‰i[­&X2Éþ”™” ÿks‹½0¹áÀC** –TVPÅ‘­³Íë—óùq÷øìøº‡Wáài^<ÎzýßwíËåƒ"xV– !’2$#¹ßñNHŠ<%0ÏΗ ¿ÄèMðë‚IÁ¥«'g-ÕÓ#*°Ãt‰£Î`gz7r¡D,#ËMGsúá1#&Ñ)ˆQ+&;jÞJ<C#&Ý7002$K Èe'àÞæÝÏ]©úmÚXu°Ýv´03§·éáˆlsd-¬OpЄÐ@_ÌH’htÕÃŽËs2#°²oY›ÙÑI“Dm-K==‚fÖä0ñ“ƒß26ž©MÜ[Q¦[žrÄÁ–‚’‚‰²x08&±a¬e£‡,±¨à‹dl™ŸN\ 28°q̹E§¶dОp¦Jd­y‡ýo÷páÀ÷GΛ­îg)EEa®ã¢”µSâÓh-ÖJHôŽ)"×1D¸øbå!ÆpHé$‚,ˆXKŸÚ=¯0T¥#$!#*TzîÝÍÌø¸Cwp—æOù/´TéÒãúttD”!KL¦Hq‘™oÛô& y0‚QûJTâqæú~Ÿ-1õ^>ÝòâlÃ9I<ü6ŒLü¾ O ûÇ‚ylÓͫϗJt‚zAí þ}/¬ÎP?3šë¶2™i’T›4Ì2d¿Ë ™#*1¨OÙlêÙœ…‡-p³¼Ô3¶ÎJŒ…Èáü×ñäÒdÛsŽHVËBÔ²®D)ÞèçéÊÌ^@¾lÀKi¡4’¿-9Eþ~!ÐÖÎK*Œ^8ä#*åv–g¹ñxsC€“†ÉÂŒ)0#a‘À颋ºÝÓ#“àܪªÒѳßÇßþ¿‡Ï¿™Q¾<óTâ• Aaf¹m‚D†CðÕ¿üÃ÷¼þ®»öú¼£§öór€ñy±Åß^‘vZ»ÆIób=_ËÃvÑ Pó(^Ȭ9È#* cö^ÝoÙá“0­d¨àæ?q¿GS&xB¤P¦Œ£Z‹>KQÉë#&-#*;¢Vµ®+Uõ“ÂûS'¤£ûw ¡e«etJ¢ŸÄØfWrd0¬¢±°Ç­¥0Ï> &®´™hr#&fùëwÞ‡y ü3{¸Í:.™ò˜°Ëy¡–á¹v ˜ªA|ÁX„oª$e*QM5D;ΓGÂçþ †™<%3Nô˜Š>-8CÑåÞ»1VO‡eåìg6Û´ÁÂѧžP˜œ’tOY5‘º(=dö`f–=­°¾ÙìÀáÊq¨b1=•6ÔJ³Øònq,몹–eR†ŽÛ-ܶ¢0ÛÉ—¬Ç?Yg~f™nÄ÷G{í#3¿ÀkyB`\4=ÍG|Aµ^s™¬:o€ð¶Ó^õ:r=$üWv0›l êãì›·åLâ,s!_y¼µ!kÅaTQà‡HNî®$kb—ðw[m¿{mßkàí‡ |)’yóöãìû ª¤>(Ú E“/¾ï#*òÙù9£.f³wÏêN•…–{‡[ß¿^S•[z¶Ì2yÃ3Q²¬ÆégZR‰lTyÌm¾Ç° ¦Ùò€ÚÚfhׯ‹o/|ƺ^(‰ v+;3xog†O+%CÇ7‘H,X ‘ôyªwŒá5FwÄ(_©Bð/ƒcþ1±f¬)ÎÃÚbÍ×ÝwyDe÷6ØÍ9›j#&¬:M³±¸Œ-µÉ°ã7²aL8Ó£‚ox%,7žö˜)+¡âÃÐvV âýãÊÃ÷ ÛŽ6òÅ@ ’wS¼ÆÎGTb·Í·ÃlÃ竇”œì&!’¹ƒhŸ­X8™¡·/¡© QÃphfo nõ¹LxßZBUFí9Õg E) g’<ÞÍ,X‘òÜè´÷ª«{“UA}笲SKÑsÞ{ýX)Ý\îvÓ.#K~Kl#`‡sPÈÜ<&Tm)E¼[çÉ·mk¥r¢>Ù%·‚!Dwx(èÈØ6)w¸ïÆÓ½»Š6ÚŽ£®Îê#*ɼIJV;…FçÞ[;¦ß‘K®.2€Ÿm±a¸mkz‡F=‹¤V¬v„NŒSuÕÑÆs'&vl©”4µr5ˆñÙª]:lÄç‹' lŠÃ,Àa9ÉhTØr3}\’sJ) ssZ+­Ý°,+N¡Œ»m±“–O$ÚÔša´ó69Œ¾ÏqÊÆÄ÷è4Ø·éŒìͳC‰¾¥ö® ÝÐÐ!(nç8ü}“kÅ:jLœ:N¹SB—ˆr¦ BÚ‰ñ¨í;¥éóÞ[iËë\QÀœŒ=ÑîDcB1’ª7òÅ*j¶Û¡¤¯ü—q9‰"êÇË'êaMm×M?"˜‡‹wýTW»mˆ”©È‹™‡ydïÛuÂîê# 9r¤š÷(ÂÝqÔC©Y|˜†"˜ËeMº^ð-ai#xS‚*!N 7Å!eí`øms.ù§…gJE¶`Š¡öR$xÔG›ê$&I7‡7E½ÒÛ¢\õßݺxŒŠ׬ö2šÇãôžoËÿ—+m/®+#*÷Ç„_†hù†ËKݦq…rÅôŽ«­kªÒÚvO„óÐô"nYÛœV~5#í“_ÐŒo(ˆ|>óJ–^7ÁàN[Pqìô˜¦òÁšÛǼzã§Vûø\¨ã'5AŒ\2M¹|a÷·KŠÏ¹Ü¦™s…í;ft^'§†;Lô˜eé¡Kʉ!;¢—0Þý¢m°äG Òt˜Aùú–aúÕ‡’!öåÕº••œË¾>¶Ç¥_y[g:(FÈ˼ŠSM¸RgÀúuÎLw)Ó̱ˆÁQb¬>ÏÓ~Ïüv\F¾õåÈ–Y¾bq'²g³w„è³w˜ËyAhâL;5×7!x™Ë5¯<¿¼b`âUoå¸MÓmÅîÉø6—¤‹Þ¯fÞh7Ñá*¿òߘgB5 ¼ÊÉÎШÝåjÞQQÛD¡’‰ÆE¬p¹îLKqF|Õx>ïÑþü=gÏÒOdpëY¸0]iáó}!Òü#&=€BiUSÈ(,ÈrzÂ’»Kv~¤µÇëû\î¤mP1b¼rÇ7ü#Éq5g&†µwTg{Õþz{‚}ô=Ižá©â2~ëbãCÊutsx˜Kž2ïÇ÷/ˆS,|ÊP ˆTË2Ù9C Á”#$OÃ;«újÙeé}çÈïƒ ƒ‚x·¸F#HX(Ö#$ÃéïµEUâ çcÛ¶‘˜ªLZe\‰kÞÄ `λ`ÐÂÀ%â½rÜ-ÊÈw(9]}aËÙ.˜ (ÁÖ¦Ä)œt‡FÒqAÔ':§ô#$s«ŸÈ.Ø1x"]m GääGÃ$\¬›NmAq$Û0;cV²¥R=çߣ,îñ{‘”ì*ç.ïñÜ9Ž [ÎÐ5¿., MN¡G^&LÅSå1ºùeÃxç²)IVì–\—.B„m0J°þ¶ãÖÉ‹aæGhZ+RÁ\¤Ä»Zqàgž§³…ö<_K>›º…&Iˆ—ƒŽø¹€‘6øÕ™ŠÎúOâHÕ[*ÃcÁT;µ_I)T©“ee“¢_Ò*›caWnµݺnÌÊïp öÑRÅO.­³Ý¥bÖQä,·‚;_m¦tGIáRÄN4$D ÅA¢iüÑ ©ì˜ æ³r¼ÓLʪ\Î}$¢ qÒt=ÆWu}=!{1”M ¡AÎ ;Þ$š›Ï#$Z˲ã¢(“qìoGÝ;ɯPzd¤žš¯DlQÙœX[z Ž®Ã¡&aØÎMôŸëÌëwïfíMt“ù z6ô#v(|_èe´÷޳˜¾ƒÈ¡¦¦ž]†jR5Ÿ*Šå²½i¬“ý™é”ЩŸ=ˤAUw#* ‹6¢FRSÒ›”’†“T€Ž÷C‰>þ|XÍtx~³ALC!¸vêKDÁž‡Omb׆dãùû{ƒýc†Hs‚ð‚G<Ê'3€Oî\æOlËzα9Ë&Ÿ†ÚZLHæØPRiLx¨>Vg6U‡V¦ØsåᚴâДå® Ø<ˆŽ:óïà5î]zhè¯B(‚K'æ·3lqþÚ2¦rØ>ß#*ÂôÄfBÚeâ Ñ\÷¡“!¯jaì°Y$ Œh#áñ³Æ’$ЊÝR÷¢ønÃ`ˆ4†ûìM…®G†kØÌX8&¼²na‰)ĪÄB…Ó­Þ«Ëb_Êv1ÊÉ'ù²>ëÌí=—F‡0o³(rgs§Ûï²Íc܆›Zö›ô_ǦLøÆæ²#*ÖTW½pˆЕQe OQ<VÌØÛ׿ÂÑ(û¿×j:9‰9l#­H%˜n+©î€ð”~ôöjèºè‡´åH#Iòó ¡ã­-ñÕõ×E+#*S«NRŸû"ªH„žgÂud¾‰Û1/5¿<’k«æ‡U«xd›õV^ö:Û{Ó:0Ÿ<›NVÐÌýšÎf§w(ð\+S’:.™|®:¥­ÑèãT»,¾UÆE‹2ãe,Öjå7ÒºéM…б“àDî„îaF)/'¡Lî/OMY'Ç·³¯Q#$ †f ˆ#&H9¡ÉɬŒ”+ad}pŒ ¸ÖÜËÞàih"à¶fç8^•±!)Uœš]¬ÁÄ­)ƒ#*LªçÙÎf'å×DN#&–йZ¹w¨w™‡nÝLŽé­c'±5Eò-h%û³ïÛ¡Œx4'n®ïÉR³–¥ð6‚£f\ò#òV#&¡g¼-ð`…—³¸Œ’8Õ„¦Æ¤ñxŒðQç1Œžnk]öæKtÏœáž7¸‰jmcÌëe¶Ï“ k»;Zèv¨9§A€U#*Â×\l}J: òJP• Â)KïY=÷{é;t[9³á!‡”rnŠšmîGdã#&L¢\‡i¨Ñ}¢íŽz¹îcý a%K•£Sh»6–Gð[RM'µ^}˜‡‰^KÖU9BÃÎÏü'Înç)|wáI¾}øNumÐHâ‡âÜèØ÷¸3…{f‰(žËóŠÀú†ò¹Ôg,Œ…¢®vúôx–ALëM«cC8â¦ÏE#š"œOÄ<¥ðélmB_Z1›aòZnpg}›;q;qê?‹Üî˧²]6Ø{C:é]èÎQëL»bÕEEp;9Ý4ø7ŸÌ’KÑÏö|qÜ…Õ£¯;Lþk¶{tußht(#;C:PüKµ"’Ô_™#*XÊ"||¼ÍŠÛ;ædM,žûHðämå-!–¶Oá3áŠo#*¯#&ûtÆúl˜D¹€äáR·tK22¶ÜÚßC—–!&ô¡×_Ê×õ`‡ý*"xã¦üÕ·,AºŽñ„M@u÷”Û¡÷»D:÷·“Ó¡ls%“LÐ\u÷õ£ª"Æ|¾xwÎQ+9ÌNm?Bsåe®$Ò¶|™º×¼¼kg‡X3s­S§¶‡–„ûtc˜¼/+ÉìF!äöøŸv2xvr¬®Ò!&€tv|©j{`‹‰b9£WÔÙüQ8ø6¬HÑÐsý®q‚’-÷Ñ ¹°a÷y"GôÑ£fÅÓv:®²`u#'êŒo¾ùç8I‚ß{^®$¹Å°3z¸¼‡‘mŽÓÁ¸O§9ûqµØÌ϶`ŸïYâü5Såaó»±2“Úüuñã8Ï#*sÄuôÓäI#&|¬QqµÙègõë2âE°3SO5½B*[W6»uá÷ZV‚iT“#*3X»¯±W (ñ]‚Ju«‹äñ(+CÍ‹;¥ôáà×k0Ã+†š‹#*.wêÝ©“¬À¹çBÁúm#&X²UÔd÷c‹3Λê\ûžŠž%F£û ܆õ×^ZKH•sæâ݃Ê膭+®2“”re]tݲŒr™/zØCŠ“ÔNmÝ•/£I—nd'ö¬/aC+fP·RìÆÁGEâJ—AL'Áëêˆè\7L¾™<ít“½D[߯âúÏK:c= l–^6뙚Vwyäô¹Ž‹uÄÖß"]jµW<=[/£ï£é`kÉ[Üå#& Z¶0ƒÑÐ/ý3Æk(ZPñ—ö힘Ú[¸UcuX Íå4GŒ)<Üû.{Í…î·9죥FÀçjË?$Ü],©ak¯t^Â@¡ h,¡Ì¶¾G1á"3+MZ+#$mšìt¿CÁ¥Œ7n+äúºî§Y7й¦öÙ•‚PSŒÕÅÓeµÏrÙ7¶•‹bª’!yy©yׄ¢÷æY÷¨ÓÂ\ñ˜6x›\ ÇР@§&˜jÕQI¤–;21Ö1å¢UUãñ]4Á#¸prHç8Ê™O:Î ·µ-ìëŒøÊT`ªmƒ ¢åÈÑFòx†Æ¾yJ²¤=²5õÎËêø`ÄQ²G¼Ûp›äíØFÅ-fž啃#$µ¥cdÙ4•qP÷»¥h(ë}‘¨R"G‘(’²¾#*ÛJëp|–JBÖNaàÎÇ_Q”j^0¢Ôñ¹nšâôÍ¢åÇ{J‘j Ð[:)ÈI-¥_‡G>ܬvÑ¥¦ö¬°»]×hrÕÌr˜YaXGI#®oœ´s«cŸ¯n[uå}tÆE¯U¡T:‹ë˜¬?|{)¹v „‹xÊùëq,¤D‘##&ð9å}#*åÐй ; Mæ<‹¨j Æe#*ˆ¡B9Ý#&.†ùa©Í¿Xñ…©ŠÁÂ5 ÙL·=ÖÇ‘»Œ6Ä]¾ý—>È^g-9¢ïPÎR  íÌ`Ý965 ÛZk¦¨Å¡ž€÷B°q6BLìXÂuµüÇ=3ÕGNXZ^˜¦ü` Ôék3ŒlɉÒŒ¢ëót/•³†KjÛ#&ûµ9‚=#`êóR sÎdÁÓ߉5gŽ··—V83ð–ÑþÙ*X:‡˜‹Æf µ…-Ì´#$ÁÚöLǃÛí‹KÈuùsòQ–+sx‹Ð’dÁ•‘ÀO¨Nà’³Ïij¬ ôç›´JÈT†I*ƒxzÊÔ¢ˆŽTæp¤µ†#&ýä¹`ô*¡BŠ }¯g¼ßã¿(@AL Ù/ šÙEODm-c-M´zBÏbH!àv?U\%`ÛH5ð§"83È#Åüd@’:Å-?tæYEí1#&ríÙñ]um*zCS™ü1 ‚q¿ryº~NKû4øÌCïsGÉêtø½º?,=Þp­Zî«®ZxPÌj¡gÒ̈臈;G›–êS…;TÇ—>{͸ëN;x¿cRfpõ Jýnä3ûw–›ÙÄ¢3̦ۤìàåq^X—ùðN¥gJ86oëbÖ_^í‹eÂ×Ê6OxÊ"DYH]ãnŠ)¬sä½6ë\9Œ†_¾Öžµž¿eÍÑä¤tÄ0ÛäBiBú^¬úk#&ŸJ,4z¯z®;ƪ_¤Z^‚iJ­áÝtQaæ[›9ñQÍïàzm&:Ĉ¯ëd¸h¿'6t…##&EMP\.‚¦gZçÆ+ié{^þwsŒ¸’o`£Rb£EE¢®¬6—nîz_L›ÜÂGMãšk}¾´ì uü*7Öq‚¡2ôaÇf©3¶u-IDlÛ7Ñ ršE˜ËKÞV"šÎ Bç¾rÂZš•“,YaË…8lzÁI Úäº#*ñÌ+n/ÓØÕœELvQ6Äbw4½oX”ÞÂ#°¸‡Ã ×CM¯-(r"„ÇF.¥¥pܱ³ ÕGKøc{¾JÍ»{ððaÓ0zåÌIß®[¡×ZŽËøˆÐ|ÄhšŽ:U:ó±²'d#$<%2<ØÊΗæ@p!#$@gu÷tc¶2NE>;”s%éÑΗõØ®êsnä5>|×[&äjžw54M¢ð» Ùz—¸z²Ê#HT¥ç÷LY5TÞ¾PGC‚ÍÍ{>á°ý˜£*k0.x;ö›Ë'QJ5³ì¡ÍZ©µ•åóÏ—ïU˜¿ tTJ_O”—aƒ£Ïûþ-NÂÚ ‰£Q¥/ê|ðÎÔ͙ةB’°º#«TÑ(í¼m`7$´:àãÞ®¡N Çë–ã-ð룴IoöõÔ¾™5ß¿&~™Ýó¢¦#&Ø6×IÅÓKŽé5œ«4´ÚÓ6 ýÕºÁh„‚7WB¨ÞFÖZ ~w;+` ~3t¬D0WEi:ËX)µâNô{m»§*@ò×ìuÊñ#&ƒ|fáÝ)9æKÀ«ðuñ:­hEdØ/·(Øû Î®R+F;Hy³Þ­I0jm¹=¼`Ùycí…+è]®d³šw:$ƒ†Áå½À<ÃÚÿ~æp’a†–‚E`[^·¼VŽw2Ë¡•³r‡ïæ¤!9¹­0rË ™îõ¬ meIž²Ü, /¢%k^µR0~ï#{µ,¡„,Äç#*G;‡¬óK§ÓÏ!C¼îІ¨:n‹.€M¥ˆ#*UT í6;÷wk¾s€BW‚äÎ#&¯Ôë¹uR2%ëz·Hôº¹Ý·É-&´™R¾÷9&{˜(Dz,-(xÁ€©@÷ýŸ#*€È”!#$DZx!£iK,’#à,Ï8@tž^‡é/ Ķ*¡#$—»cPŒ—K—'_kå¬IXèÔ×tÃÁr=®.ë«Y@=Ë [SdІ*„¼¡÷ͺ5¨œ÷4¯RÔ˜xÁ5¦¬þWÔ†¢¨)¸éÍļIó{ å¹®ˆG|Ʊ¢ÿ'6Ö퉼²k‰¶^ëù„,Ý›©£M:Nù#yËç–"é#F°§Fõtf̳#*¥ÜH$L¾û é(n.˜j’ôÉ]x5YÆBj/Ée%×A5Þær:j@¶ï«åxîdž»ÇsžÜï]¤„‰öâIš<¡âøÂK¶2覚‰ÉIŽ­Vd¸Æ‘J®'@)Y­²s±£JmÊ7˜!¡±|gÕEÔnº6Ýùm·ÃiuîüccižÝ¨â‚¡ð™¥îd§ú"zP\îc`ø_Mcå‡Óg­çóÐüëQÇ…Ï•R#ݶ`|£ã¯O~ÚóÖŽy²Î|¦ïs“*Ð'÷ÁŠœžøÀ¶Gk…3Ò´yQÌž±_³<ž©×H¹YòM_vªÝWI¬øMÛµÃÑŠå{ùKz{òE÷ôn­tœwšæh´t2*:ÌñëmáÑ|àáNìäzc‰-º¾7”Α¬j+‹‡[ËŸ5ÓÌs=o¼[DØÐ‚ unÄ2¨æƒúM‹`Ü1˜ÛNAówâßâIgm7pðxÃf‹÷ã³¼ä¹Ò÷øášÍm‚]d!{›§¿H‘!28BVî~†Â1:ózÇCž)âçMj¥ý{NÇ[úÁ½?E#*õ¹WÉââŸ$lSÆQ¾xA)³NTþøÃöAБ?Œ>?gNŠá_ŸX#*ãû±7˜¨õh1^—…ò®6ǧ“«½¢QK~0a5úúf~àg˜ ÷£ˆó€ÚêÙ6èűïvó!›<»_ÏžÜñG¡ÑЃd„}#ÓãÆÓ܃›ãë¥yu¿g«ÑªP͹­®|žù©I™Êý ?TV2e*¤XxæÞÚ‚7Ol’ÈeêŸ'féW¿+ú7å9íù¡Yçƒz;l²rHµDÁz£ VϨêvoD áÅôÕ2;lÐç#&¸»g€ñÐgÇ f5C3Í̆Z7óÓ™#Ëx†‰ƒyƒØ¡HÖÉÄl˜~û·BYŽ¢Ë§H”oÖåúUÅ@•/G® .bÕÓÓbLoEå}yχ÷:ñáÐC¹¯˜ ôB2ž½µ JÓ!Ú”clUÛ·¢kÙc‚KrUDC)ŒÃ4K~ïæÞÜ@b20¡+#$›ðÔŒ‚ñ#&,DÌ€Ëf0Tèt;o s8ë̸yµk˾ òÔmô¯*} F¨j#$ŧ†y@àâ¿.´«sÙ÷'—ùuðû8Ûô°¢¯ªÎ±Lº•Єì7Šh#æE--#&i=Þ'Ë‚öîõH>¶NØóMï#$ <¡_2©â©ÙPÒIh·-³{ö|˜Ø5é^èuszO×ñšçV©ï· ðä)Ü{øüؼíí^e¶ÍïÕÿ½QVþÉñº[1Š©¶+jRtüZÓÇÊŽŠ˜hÄN°)µi`(f¢Ã²ìùÙÆª/…ŒåYU‚(#¼æ]à[ÅÎh«ÌÙÕWÛmÂ.[2FWA­(2Í›Šýö8DŒØá-»ÏŽ ûõ/æºh| –®&Ñ7É$‹ „!˜¢b0Ž»z‘…Ó©±÷ç#¤ÂÒd÷ùü Ç;VqlåÙhýiæÌëC6lGt•˨±þöø;X#&éîóïïûB‰‹P'¾è^û úëƒÃòD?3û“–c³ž"O2\û¿¢¤Z‘T=’*B¡d[‡îMß®¢ÏöEA±ÊßZ Q›BÛþ¹&½Ïù#$¼µÓàçJ’'$ä²QK|¾w íOd¾Ø†'åâ«,Yd ‘ˆTxT^;õoÏ×£‚ë¶°ø»‚7L˜n?µ…߇•“ó}#$pÚèZ§®öâLui£¦¤dÔ–ž”¦ÅÚÒþvpqK•@/•„aH^CÔä53A³}ÊBA§N=å•È|s“²HŸ£ô*hJˆbƒRê]ÚÆÔÀÅÊŒQÐæ{ˆ€!O¦vq;„Ó©h¯P#*ªÌ·Š Ѭ:ýJýÚ}¿*æx.%Zn›Â5άœËÐõúÝ–qõn‹Œ6… ü]±Íh ž£Q“d.q—G0uDCËH…™¸C@~Ïñsxtɇ­øNy„m·°Ï.#& moϹ;#&B a(áEE‘Lý)Ût âŸt÷ÃãõC¨†ëP°7ü¿ÆÛ T;Léz@Z€ 媔ùÛÃ#$Ä+7Yg81ñ‹!‰~r‹SîeHy2É4CÂj ðs Ú‘g`”–z– ÉgZ7×â~Óf—ß g×^3Âud(#* „;{#$.uš…;!]tnÈ¿V­ÚõÑ$W2ñÀDåØekÈCH„Þ… 5#*Ê#&ñºhˆ>?©ƒù¨ˆ•°¨æÁ¿ & ]|8uÝó;¦n /“’÷Ö1ÇÄ„Â=?*LGHîœ>ù%$î¤*zâ!PXíÙ®i¶2`¢ÃÙêLË~zç¤Ùo·žòøLB0™ø]=¸éœ@þ ºG¡À‘0ÔRgmè‡ô~ñÑ´Ïõ62½ØGÉí$dìùZ °÷N¼S•ønáPq™ú1oœG}ûnéÅ+« *ž¢ÂÜ`ë±!#$™”@Ä2…Ðimìºú#e䄊¨@ȱŽq`æá×#&ÇðäÄž@zlÑàý<}4wCHÆoI‡g›äA‘·S¤jÆbÄŸžçÜ®"†ÕçšR˜^¡{ h †³ií*·oü³uuI_šВD"|˜Ø¤Ü‹z¶Ú3ÀÂÓ“ƒêTÆií’Ï(Þ¤‚Þ<çFQ?WãnsõÍrÕ£v|K@Œ+j\N‘^âÍrÈåÇÌ1G‰Á”?BZpúäBË{8Î ¨ñ¼Zïµ'fl”ã¢ÿ\æL퀑`wdÃ@à’†ƒ¥´¤MO#Yê~¿\ô. 6ŒÖ漎é¼y (!eRòäweÏØ¼²YíÁÙ#&Û'þÁ‰QëiÆ;vñ°×Ì€çÇYÁÝ£XaÒØÏ+t½d‚ÿ›”(8±f«bZ¡ Ô0&·· ¥)AžÐ3žˆ³ß\–Þ÷ºƒ2Ú[ƒüº3$ç%ã›%:1}Ù…È’Zák®1æ°ƒ·Å@:J½e{ì¨[±â÷a§Ç%–&â’’ÑþdWw,/òmê¥z9€Nãõ‡5LJƒ¶Œ®~8«³´$"½—QI™Kºü®dmU·ª!Ô¦0ˆÈQŸk>_-áÔãñíÚ]X{Ôlmc#&[9—'çr•XÔ«yµ*)¬%CI¼ð…¸>ã¬wvpºÆj«yùÀ™[WÞÜ#$k¶ùb{¹Ä¼Ñ#ô¥†•`œt„Ww7r §´p`uðŠ8‘KÔ<×°(¸§'E¤;•Z ÙÇwXéžWÑñoƲµ>}/åd8ÚÊÒ—ÇâQÆa‚\P)–kyª´%¼uŽf­÷íH#Ñs[9+÷ß·©(8ȉ'‹#&ã^JP0•‹u+@‡a³V£)ê囜{yOg‚ú«“dFê~ôÇ­>öÕMËj}ùÚO›j“¥Ao£Ÿ#&øÛû®4Ò¬˜1C¬ÜËaÝ;ìÒÉ#*žî5óliI¤\éÀHg<Ó=±ÆO{¬3Bnû—Ûú¨€¶µæ $vìѲ>æÐР#&öÓÎÌ(%oÊUôa˜yFÜSûïƒ%õ'ÈÓ¡Ê—Ç5¦pM^™>™vdä QkA'²(lä,w>#*rðåÒÆÏÑ ³Ý˜yœ´­]/hk¥í£ÙÆU‹«¹¦:°«¶DƒiÊì:L™$e:BN†BuaÔ#§|}[ŠùgLa7^#&rgqžÜŽÅ}‚“tÔÆã‚Üç£#&p¤GnÎËæ” ”ö3TBºˆ°—@…™í,±¦%1Ÿ# üä+‰[CB1Ýןãk¥íäÖoqUªHýÌþúN2=“ØÃÇvnd3ƒyý°´þXT#&YÂ!˜´¥nÝÛñ§nÕ™‘Š%a*«þöÿÙýOÚKZϽ(êç6bg{wÕ'êHϰ’HFB0¼ƒ$üýžU–p™ùPUS×EPÿdCÚÞNß_#Ï)Ç¡ù}Ÿ¡^•ù–ûûoo³ÔŒ 5^–þ2¾ˆ‚‡î".ß@Ž@¬ÿGúW#$ßù?¯ëú"€ßÕÝCçœûòPoÙäì1½ZeùM¬7òõìâ@&¹UÎ^4ºç#$¥-LªH€ä"By1Ý-rܨ¸2l@„=ÕІ¸ºn[¥Ÿ~ÿc£O#&ÇW–«ã^Â"o…ç¿ãÕ,_¾©ÊœHõóîÆ¤ÇC;ãå`Ýj…ݤ$3Â?iiç«:Cè{}ŸUEÏéœÅM“•¹Õ_VDÒXyþ[ýÔ=×™-c]II>ôk@øaòø}j£~ߥœÛîîñÇÎp#&Á{#*aE¸†Ù䟿3Kwx,lþmÖ$WÇö&ƒÕÝé#&Êú!#&ðK÷aeÍs0y!lÊt2Tj HáW±@ Ñîq 0ób¢tA¨ŽMö6(sJ#$ ®`E vBÀÀÕºB¦#$Se,ꌲiDSQIc"*HÁÒå#¢ÒÈR(9”” peA3[l/ÅÀab(¶MR7 ¼],fV3wA‚1,8H–1·%)f°#*2 Ðd‚=–P$½[ñÀ¢KGÖóg”“íïU?Ý·_‹Ò=+‡@5˜À”XRâÁ€Qž˜9šU_øæ(Áô68#*ý˜ÆjÞk•ƒƒR㎃‘#&dTÔÆÏ‹#$‡]x“‹·.Vý3öÒÇ6`g¬ÁL\ãvZfc‡¿\Á­ú¢ŠséCíå¬b³¨ïHJïQO½Õ= ð{þG6ÎuÓ[Çïᜀl mÍÚ—¶Û¥sæHèäß“ i©ÜÓOh@ÂôŠÆ#*#*VJKpAbå ágùõdâA!‡D¢‘¶%Î]¼!ûw†À¼Çïßn"BN±©“ûíî}ÐÃ=ú¡þ×Ñû­Äò—ä˜{(èyÙáÁQöšÜˆq…òR_-8‚rû;Œ¡©RÛǃöŒËaå“ìgLí%V uŸ×/ãU;¥FLÉÂ#&ŽÛØ9¼«ß{9š6/U]¶^`V¹XFt„&tËÈ9ˆ#*på¥A;í°Ñ#*&潦Áü¨ûlfнޠí5¥sð!! ƒóÄ<òåÁ¸Ä±Ô°>¥hÉ¢¡A‰0Y)šT³ZfJbÇÔóó¯£Ô2䢪ª2¢ÈÑTyçêÍÀmny;ÎpÎk¸ÝfŽ¡°Æó+Å.膺°·LÛo¡X"@ÍðÉ#*Â0E!ðOjYB•a®ÒÕM»©q{¯³NzæI§u¬dJ]c©¯#Cm¶3éC¦£€è“w!Àb¶àb“ $eÛ&‹3ðÜȹ¸¸;’lˆŽ Ö†JÜ!I…½$ò€(,#*}ø‘ æH(£$¨¢ê@Š 6#O'R%:ˆ#*LŸç¼f98r¦î2VIÆt±®uÈŽ§(òrÒ'±ß! <ãw#ÝÎ!aÔ'MdC‰Êgöh4,ªÓ=û«da̬·æÑA­oº}‰ö£õzÂ'²ÕŠD±#$”JØÕoe¹­’µ[äu•dµ±V¹m\Û&Õr«¥µz²,¥ûÜ·v©$÷DŠ Œ*†!‹¢“ef •ÄÈ›ä÷¯‰ :G9¨LÅxó¦¡DµAõT¦ÚÐ!‡1@ƒš\!£Ž{í!Êxt™â¦h2v'Ì× *È‘ƒ'D2L“‡©“ç,ÿ=¡n0æÙ^t”G‘Rtï—m!îjüéë1©òê²BCrï´´š &Ñ­™¶Š¿?[å#&ÁÎ ¯™lt`¨ç/›ú² <噬ü¼H?›eÀ†Yã¬ÛÀ0ÊûìPDIã)˜Gfl|/5„ ?.嬣R Ô¢ˆ@Z,в%FÎë[Ûm¯?ϾA±cAPèJ °Kd%‘!ög|ý^…õûé=s¯Õ§Ê|þ }zTbÔ-½úùhF"¬1t§®B)º-ˆãyªåa#&8Ö×ùôåoc+ðÛÆ,Ù!!$DS@,Svsû7q†¹øí¯(ÆÀ×n¯-ŸŠçÞÅ›ÜJ?…ûêbQfT…ƒŒ4#$XþÁöý›±Ó=T:z¨o§QãÄ’|ݤë(NC#$0rÎñ¥LU…O—|_\¡ÏÑM{!dE¬*É1‰AƒÏ?|4â$d‹ >ÒÐBdoߵȪËãß²õºz°É =Ëkx&ôo«F¤ âsÙ£ Ä#*4¿D·XöKíƒ~[áÝ#HBHÂI2ào?’„>èP·ñ7p Q¡A*±Ü2S®ÍWwJIb ¡¹Œ¼I°$À¯Å°%ê ÀuèªQÃ#*»y‘S>Ǫ(ëx’iåT§:ê9½™lL¶×!Û.JÂxk§]Bˆ¤R‚;e =s¶9AjHäȰ¾XÊÖi‡›ÐÛç‚dš‡BÖFª€©Dó…û3Ú:sÐ;'C›zô fàLÙØFt¸à+iƒ˜Ï|ºÇ«Ì-·KÜ;hüш‰þËô“¦#°$A÷°ª†2±QBЫƱ7+‚ÄÓ‹pô>{ÏœÛy#¶™ÙÜT[§Ï$@ëè÷`P‚!Çõöb&ÑHgÙˆpèª]+LC•\Ÿ]9ßC•5A´%4¢…¡v<Ü' e‚þ“ÒìûP§sîlšÌôG¬Áf8:ë}ù£÷i¡æ™i–î N´p«õÐÇ4¹U¬;*ÕL8 ÖÉ2¡4F/›EháËt|-%±½…ðK¾&ûRž”¡CKbÁx2(³ÓJ¨ u5×”>Ë-voYºTY¹ì®ã®|wñ›ì¾#&¢Ÿ}s´X÷ßc.Wß ½÷–6ãà”]?#©üÔ4ò´mê{BfN†|冖A"LKgD€Ç²$–#ªf&Þ¶ú$ð‚cuj„çcEŽàwïP…ÉŸ½þ7»ûbºríkªsI_Ý0HĤ¾¨¯=þœz(RÝ™ýB¹Ü<s'dmc’†a¤È0ë7ù„~”´U¾ë30£^è‘d“ŽÊÖFñ£·„âoª†„ãÑ5Ǿø–vNºWfÀÛ8›Åmb'J.H°‰žt&pôÌî/–%è‘6£³Ò„©ñä‘ßïs>> š³q2_;‡&/ãy]²årîÄm[Þ™°¹¾Û#«í±¥¹Ôc.»1 [N7Û¹ÌèGVH‘!T›Í’ÅÉöOŒw›H벫­°tCõté0òâ~³Ì›µy[)^::=YôŽŠm ê¶IJÏ)hì/eC°š+€¦ì!7Þµ#$i‰¹Çn2:Ìø×W[O2!2ÌÖt.â)Áøžã}×`QÂr¢¯L ¢ê‰¶tœD¥ºÊ×S»éÂúÂtÂ{ #*9õèSvÏn§ƒ'¡Ià¶À©b#*0PÆ»li1”ØÍoq‹^5¾ν¦\­R…ªo}Á:Ãë“Éü‰ŠCá§±¨³td')CP#&Ž´ôÌ'«w¦9eY³ƒ;âÔVM°ëÝz ‡ÂºlQÜ=[€9Âs ]ÑI¶Q7Ø´;)ì‘IrÖi:U~$Ï‘u‰Eê©á(ˆ» Ô§‰À,< lë>Êã°ÁŽ>­õÞ`‘wŠõª8o)#$6ÛÂÄÈýƒ&$«œg¥/æùa ¦È cêíû¼1ÔT>ìËCý3٩ñ±ˆq=P $S)´*"Gdµâý”|Q“P€>y#$ËáËF¿¶`SµÏH?Ï'qPß‚Çjf>rãX'ÒBô©²«ÜK;ÿOá feDøØŸv¯²§­T2ÉT6=ö_T3‚\yQksóUücƒNuY“X˜ýTÜ%á»áoЍ_©Ö².rÎÇÆÅY%^åÁù{&á˜ù»Döu¾&y”•ÝÿKñımfŸˆ>–ý¾°,ê&ú&JØ®=Ëc@ )XfªÃõ~ï·í¢Öüòžb¢¿åýiÑ#&"T (ˆ  Iy@Åû׿û63Zá:Lö#IiŸK±¸c“â˜ðŸ6³ä¡çåa¡ª×únJŠorÁÙèQéÎ o›ÄÀþªôBž–À„ñÐâBöpÙÏ¢ò⢺ñÓ #*Šý¿¿ýîýß³û¿vŸ¿Oé˜è«ÿµÿ [Üí¶xÌzLFx‰•¾njðí¿ù.Ë[ëhö~5mYÚæ<ÏoQôõº±$ùÕB()~Ì^=†R|1a‚~-œÃÙŸÑ÷ù¾â¥PbX3šŠ‡¥E!ÿ¸ïçU¯Ãë™—éw¼¨Chú!úÿŸ™é˜ƒœSçßš¯Œ»a´Ü’Q*°¥å@qgêùñë›úööKµkU±k[¶;×lJŠ?¼‹DEBMP/jl± f‰*NÎvé/xþñ—Åÿ/ÙÑsì‚l#¥Qþ›–?ª—ù)vŽ¿B¥©6­ß»”_Ä&C/éwI’¨‘bÄ2ðÑVÞ :¾2g¦0½Õñ }Áе]Q×@™t¿°jزJ À¨èë·#&!›ˆüÜëËýþ~ˆ€Þ.ÏrH]Q.ª*ÏgWc¯²–çÖì,¤¯·±ÉT(áÛXiPuÊø4¸º@Ê(§/èÒ«¯œ©‹_õZþ¶ôn›¯¶Aîyé¤vT;”Έ=‰çÎ#ÑìoQ†Â;UF2&žÏ¶–Ÿ] ô*¤-/ŸÈ.<~u6ýGM+çn¨ÙHÍWc]hOž cý¾<ºnÍÔøäÀ oP'©Ert~—2¹žçã—¾vz9Bóœ?~V*Í1ü`ü¬h×ÂÖ/AK>^ŒpëÓ„…l!3‘éµ™†®Ž—Â4‹t¬¹QA™˜7úãUVgÂdM¬(/húøÌ Œ¶V]Í”Ó>¦à¸w\ÉJq#*ï·_³à_œàt L‘w±J<ùl#ÈãÂUøÝT-\qYLe»ÓåÍ£Xjø|•²J«SìÝŒÑ#$ÙˆÆR¶®+W«""#$Ï7sõêAì©Á=#Ëìшˆrzé4Õ.К=ÈŸˆLaýÉÇ.AÏxs;|`ðj)Fy6ÜÐr²±m«Z¯5`Ñ5(‹V¢âwÇ|ÕUx­O?½XÍÿL™âi)•å¨4bºÛ׸÷Á·ÕD$¶Bóáö—E$$t½^quIúû^Þöõ¬åú¾7˜Ì¢ûKCHî!Ñ¥Ýh´Ö¬y/­{c?Ï!èg1³¡ÖϹå|MÜÚb+Áý¶âL¦DU½2wÞÇIJ÷I©-.jˆë–Ö½–òm±½Ÿ”Š7ˆÇ½ß¥j¶aŠ{áÅ÷»}“è‡:ÆÕf$b¨`åa LBª2#&dB¼¯IVî¦2ÿæsçµ(»õÁ\•íMèvˆpÃôóJ1ÊáŸCð?//5…Í ®²¥_€¦ÓW¿ž*v–<H/O$‚Ê­,¿šþüTxÔF‚¿£ôIð#7_7xlÈ/Q÷š#*WĆX5…¹3+\-𫍽¸óÙËOú^+t²·÷=5eò±\r}’:KöBªiÙ¶-÷6wêì”EÌØ‡§yKA—×NJ†ÀÊÄ„Rʤ”ó± Ûe“­”ÏyÑlu§Xp¡:GI'wÒ^)ËûÝÅ6ÑîˆÁU—tîŠs [“ÙŸÄۜ۰> *ˆôSïzÝ3Ñ¥^(¶”ˆâˆ¢‘¦Ö|òõÐq§0°ï (¹†éDU¿s*ºD(äÚý”ÎÐPåFOÙ¬Ïç9ý6ùe-v£×^¥ÎIŒ|h~œ´EMIÚ±z¹.W€ý·®È`õÏeΟ9Í“EPvR áA ºùºš÷Ø&¨ê=QˆØ­W¾$¦M#H™q&ù~ǨÝ6KOâˆ÷*åÿ¡(”J‘uÝ;•(eC2qpª¸йQJˆ'îdTþÄRžÿ}̒㶉ÛÇrëìljYñû!ã6îù˜òòx,þ¨|äÙh%¦”¸Ê!Ì<%ÑÏ%k0ðÄcÐQÑß'º ÝýµT|ê͹ñ"-?k$åßÚøÀu£} éE7#EŸ"äSS'"½ÊnÅ>Ú=3Æ“äô³íÛ‚&Æ ù]^Ñ]s}§GPWà{#“kò¶»qþ—q4ƒö­¢T+öDyzEžC³Ã$ƒóM‚& MüƾgªvËü>ÏÚóÒäd)} ¬( ÷ý^øbqå™|j'ÞÌóšt>ïŒÈyÏÙi1TnWçöa.8û5½@þ»¸dË&ÇœCí™;Y!ikò”~â~ÿ›©hè>Rƒä;à»A^…Me?ØÒ=`ˆ”yÚ«Eì`º ÚÒ'#$Å9´{#ímSQÀ#¡þÊ#¸\ØÇÒgÚü|ÞX.!¶œÔ„#Šá\üÎ eþäÇ þ\ƒ|}úA7C˺¶r£"sˆÔ*!‰¡>ÙÆ<Ó>LS˪_ÞÊýœ¸3‡ìï>#&1µ§Ÿ—æWNÎC²~¤Dq¨èûC%—q2ñî³Öc—m±ÙÀci.G…þrrSiuýlçP¢\B£ƒhoÍc÷ªžå#*£ŠR@›J!Ù» •ŸÃÖ$´˜”Ñ(NžWEüÞç#*]ÝÛ×Ú æ]B2ï3ÏǵõÁžª'E?¸ƒÛáÉo)Í&Ô+žvŽS¡¾×s—gÊdþ6Œñx_ž>»!É\ ãÁy}£hŸ‹Œrš²ìÔ™·KÞ—ðŠý!׸‰ 'du„ןÝ|áÏ.<àv~ç¶,(XgW¼ž8_TJpËì5ÏÞB” °\ zÆÉàƒˆî·¥Ý\þži&7ŒÍ¥\¡êºH0çYRÑPM*ªß$й~5éô–ƒ#ˆ~•ºCàE9iMCt=±Òn¯ä¯üãäóy“ü9ÓWp‡PÄ|M·Ô¼¸ý°K^‘#ö8wK¡³±\û~ÏÍòvôf;#*I="÷1!‚»N‘âŠ7àu>®|à´ÎQ)ú×n2œàk‹¿ôR{ÿB7¸¬Â&ê xë½=˜¶Þ’ÆQ·Zr®‘ì?†gq$•1emÿOâ õõwG޼ù9  3#& ~p½¢#$ºiØB¾N}¾¹â@8i`‹êpY¸–$§……÷¿è÷=Yß*õƒ¯õwêÓ«³oaø¹WT<$3Ðõë8Às•‘X¢A-M‚í'…ëaòˆñ#$|„™@9‚"7}Ætó`\0`û®ÕµHÄ®FÝ+LùÂqªæô!è#Ø%~|±y?<ƒŠr¨;Ù5*µx‡(.Þ¡œ¨R3ŽÉhÿn!J€Bf3¢Úå S ‰ ª ¬Æ4˜h ÷ÿu*ñ8똻È(#$%#$ ß6Œ)$#&ûý—’èšÙËSÛÏnôÔèxaÁÂYþQ™@€^¥#*PcØX2‚/­p¾Bûj¿%IóŸ_oxÖ²l‰ºÉŠšàäAÝ/pUŒÃñÂ[,ï¯lÇnóÃ;{3ª3ås³™D5K±'ï§ Ëa8›Öx“_~Š)¶¦<è(œz»›‚@'+ª×»w”VW®!Nçt´žUAY€Y‚º©Kâ°]<¦¢Jð&ºûÞìø%’ÙJ5¯B©¬jEL/TK#*ʶ’ºuÁ"X‰ 8g&øw†å!£??Ë(4©g7½ªøn‰¦m–W]Ôžg»0Êv8Î;+„©ø˜Øâb=zNGPP:1ãÃÂG„@¥‰Ñðµ@àN§2¼í¼ÌÌž»¶ße«z Çíøí”P•'@î|ÑÎÞZâñk6×nçì§OŠLŽ8(mD¥#$võªŽ·Ó>°隦ÐÚQ/òfðê¨iô¦®zNcÔÜÖp{…¸îÜì‘A¯?o<øj ªW‘ÊøE³±ú¢ù9Bƒº²•Y·™ …òÓŽˆzjç°êØHIç?#*hrŽF>ˆ²£òÛM{;'ï죶"ÝÀG¹Æ®$Ô¥z#*´üÇC¼”a B‡…èÚ©GsúaX]ò÷…ÈoX¢(u®›ëÉã˜ÿg8à #*K[cUüß‚ –cÄ*ÒpÁþ'Ä=wykÁ•·1/÷~,®x ©?\ýq€g”‰Ô[Äu1!4Ð>¬å±™ù33˜9e¥£#_ò:èû„!9®3‘vt…Œ¯Ï%ñtιOËïMaî­z´•„ÃCŸü¤ýSý¸~öÜŽwæN­®±]Ê»KS!+®ÑK½$[•ÁæS¸Œ ET’!逤]&M•!~þüpVí±îNtÞe°Ø²”Ó¦p„9¤YQŠ EζëP@açÎÄ»Áy,áMÞŽÑäÂ.ýÃÁ¼™Mu?Ra#Ž—ÒäágÒ¥²:&ãF­¥šqÒ‰™5ò¸ðZY-ÏŸ¾½Ö–ë„鿏ÎñgU£q·2@·A4ÃIX¬ÌÄôÄo0i¸(øÞ?‹$D¾#*‹+å#&BMÕ¿T/ÙAu¯õŽq˜gÀ]ßìÏ¥§²#&=@iX(–Öv—³–ék1f˜´#!ªSÏ\šE¥Òg*îmå³Þê—Ñ÷>8…ZYkÀ¼Üco%ºÕ¡Í@á8ˆÓùç£OVŒ&ó‚çU ÝO÷AÃ×#)j¬pÙž™Ý*Zµu*I»ÞqE$­; VÝóéîÒ“ÏϬ•X®‰¾>øæ‹íí[χ„’LÕÙuDuÏh{ÙüxË{«¿Nr,aû÷„«q?nD‰Ž°ñP÷âðwˆ ]‹rÅpNy…/¢[m2)B˜apûÍ¥€tzQªXÖhëÔTj¥sMî+¯ qŒí³G„Éo®aüTD¢“g÷_ƒn±…€áC€‡€lgû(Ùjëï,z‡3ï¢Ú‡‰ò‰…leXp,ÊÀ ÄZž®Ç²ß—µ¥Qî·ØY6¥Pe”Ô¬ VÒר*£Ý£’,¬½ˆç)œ‘¿öëg7:³9˜¼>0¿¿øf/‹/‹^Ë)Ê4ý¿;7ËTŸé÷þëkHmiÛm.•3ÌxC¥Ónd³Wâo#*£¦éïncì–53àúŸF8<n:‡_1ÇCaT¼»uQпTlü»³ø«ÞF¢Hé%&i½º"™rÿbä'_QaRΘõ!³üUÐÛ%åu5©o>®ALí׈oå„!.SîÐ@÷z)¦›æ–·ÉÖâ€A°AÜó\.Ìi´4Û{WÕ[Ç«uN"Ū.¹m¸Yñüž=Y_Jd \‹h½Í { ‹õ@+”ýŽi(sÕ#Fn½4ìÔ<µŸ¥h¤ A)ÂS˜ifK’žªw'˪íRJ°¾°*÷ð€Mq¼¿^¢¬#$Û <þÏŠÁâZŠ"Q€PžsËÏÎcXl}52GZ,Go½©$$É$—ÏŽ`[|6´wI<}^#&›oó n]»I8ªŽC~‡3…*ü5à,rØ5‘ª#&/¾¾ËÞŽ 4W‘i•Z¢‰ Gœê­Ÿ)uá&(¨®¾â@›dÏ!nL‘žÙ„ë³ÀX®›@uï ²C'Ù 7R‘Üxëo†YHI ²BÙ¦+½|#&?†Ö@úw^ÛC´Œ˜zqÚŽ®ÆNÉxeÐZˆ±wo¿v\/‹ð›Tx¡ëpáŠ#*™…Ï·½ªH’F>KoMߌýš¼ç;:#&a³Í>?¥vÇß×TMªØ¿Ÿ{Ÿ’PG<òuø`06 ß`ì²vqµp•lïÃ#&&s\]ÛSÚx–hÁÕ´xÇ÷¨ÛáØStP±–BÑŠŒå(tceäFz¥¡ÚíXî¼#&C÷f=í¦Wç§sIb0Ï'5™™D¬–ÍD¨2Áù‰Œ#$Ue ¢EDT ȶSh50u2o;©˜Ý¨îÕ÷†µ\rž°­¤ëÚÕ."K!È¡A¥Cl¤Œ½C„Jbb/ÛÜ)¸m ȶº6Ìqv‰I¯|ƒ_àÂ2GOIÒ0z Syè{RI'ë•üòÉl€¸X#&É!Ía±ÄN‰’{AçÛÖ¥uÓtŸÕlânPyÇ0&tïóFÓ“©°»7.ÕÉ%§bEèýýîf°Âýâ>kMÏå~’H©;6Ч﫯®!¯ÉFŒ}#*±/†]¸jÚ;]§¨Yn@-*à#&‘×YŽÈ‹ãw}Á«l,W$#*™ugðí†ðëM TÍÚVö±žAÅ‘C[úDWåw΂{»GŠÈu!ÊÄé`BP¦â­U×»SÚ0–ª¹.´X‚‚½“êǾÊ#è·bØIÎ~’‚*Ø#¢E¡#&¡¨ˆ‰êTã~\SAÔ!nNt‰Lœœ\œÅÈDƒ žœѶ*œÍÈ$†ÇŸž ,#&h´#&¹ìêuC TÙ;Ũ@Væ"V˩ܼرk#&·-½`1#&FDtu>Äm‹^û%a¬QOn;t½¬QÈÛ@ˆøš¡Z–ëó‰Šè¾”ò+AŽ­u~Üã5î{ñ~ñçz‘mèCªA¸Ë|;&÷„bU“8®H’ Z䟒9l4(ÉÙÛÂ+Á§ÂÒ„MýCÇëüîwTÞY ±_²’µÀmÍQ:Jt- Ç=µÃHéÑÃ,mÌ\ ÁùAÂ×UåÆ#*9÷M¶ß™¬mŠQÆ´çhÇ”8]¾ðTæ.ÎÓ¬ŽLÒèÚߨ^0L¿Œ±n-ÓËð‡K~´Ÿr­ôÀC¬t1¬Ù}B8‘ä–@]”µ)ÞkÍ´ ¯ÀEëWÚ-°Ð.Ét¸h BÜ‹m£OrÎv@0¼ðIqÆÄ¥¤á[q­©kÅÛb))#&_WQ5Ž#*/Û®á~¤aý}dŸ1zäÓO©är°7TÇ83{ã#ÜcÚô´vñùx{}%ý¾çR+UÀh†ø`HßáH5G†  Ð(ÛÇ+ðšŒOb¢Ü#$ñTL>w´ˆPŸŸŸ­øO' .ȃ2-숞ùÝHÇ.¾«wÙ§Ú¶(”вâ¸ÁÁ}7¹HNç9<7¨›”#$x`áÁÑÐ\¬«‹˜S_óüus~>ÿ`÷ùG»êÐ/#¡;}_¤:8!#&}Z  'üZyÙÒ|Œ(•±këõÓ™{§Oá3–‹G–øe*R” IOª@ˆ†ñ0=ŸHGBç߯–‹^-}‡pà'òùkjxÜç¯PÛÉ€„>ìܼ¼af‡jÎѧè2wæÏÑûW«ÁwgW7/È|Sü¼CÆv‰b2ÊÀ ·Œ ân|-#&ì[],ðg  ±õ#&~£†X= 4˜¨dpýcöv:sø}UX·× ¿Zåëw­ÀCUN²©D'•8cYk{=+ò¬±ý¼6ýáþ§üÓBOÞ‡î#*Сd #$¹|w^¡œ’¯ÿ ’*b¤ÿKUP©þ÷õ’êXE·ùµ+ôAèa#¬]áhØ”XáМ‹#$4u¡ 0:ú³üûKzóÔööXÞ~(]¹ã¡a·û“‡[¦Å¸² QüᛘQÌptïFßÛÔà*j ™¡´ðKxÃ.ìÚB»¶šTÄïê„f ú¼ÂDê»D’.9ô£{ê*ˆWT_‚”`¨k5Ð-˜×EGˆÈguTë.öºTzǵM‰Ö #&Ï/putÆÿO ù/·ë4Aˆd|üÑÙ´Wý±T²/¨<`]ÃæNñ5K-#£ ܹý·¬3MFC˜ ÓÚÛX\ÿ;íÉU255#&RN£Ÿá­¹š}î^C©ù2qöê;û}Vº›’‚Ýá}ìþêëª?Dµõå· 3iÑî½#$\À%ñ+Ðz-ÆeÉ$#}3`¯W² $_Ûxˆâ—, #$R°#&e#*Êò:B5Ãîþëô|>b¿}·—¡ƒ[k lÕÊ€”Q³ëå¨qìáµAîÖòv–]7éüS—%ISH–.ú!püPfãèjÝùÒÔ°­oþZw’wžà,ÀÊä/Cýc`Âx¥#&•÷†î¨¸;9›µ5QhÌ,BÌ—Ž6oÂMTÞ‘`ù#&@ÑÎÁšP:Hk À×ÏA¹— ´„‘“A‚]ÒJOØó)#&]¥=\$õˤ ?,Cò¦«]›@ú ’{~=aÔÙÌÍëN³¤ ¸éìmÚ ±h þÒM#&c«Z6¨ßÚ†ÿ#[Gõt6[G¾ÁhË %©°6ÅÙE ³#— ໞ+JQ $o´¶l„§HtÜÈN+ÈõسÅnÌö3NËÔC¶úˆiéÜbJ @«l ôƒú’#*Ô²}kÎõtÒ’~_P½É×þgQó˜í#*« m»¯×TߦA€¨*æ¢ÿ› õ€aRÔa$I˜6x?*Ïø;îúŸl„‘û±#ÚžÌ~87:ƒ‰×õÃ8©Ê‡šœLÃÈêþ_úNÖ¯ ƒÃÃl3âßY‚.ɉ00`LÌ’Cµ~^ïF%Ÿ¬ÈB¿ˆþØIÌÙ/B?oöòü]'Ì}§¬…HÁüñmoi`´ #j[ªOÎè'†nÁhŠöDbõð?1“ëÁUê6ûS'»˜–‚x#$V$ ,Ab0daŸöüÞ/«ëBºhøÀç>'™-£ÄÌã„ÄÑ„tí)#&d$xƒÁychðEù7#$ÚAÂÆAñ³‰ÇþÉìíù.ôÕA"$`Ò››ÍË›âø%\&ë÷#&‡.ƒ`Ý@"Máµ ÑùpÛ¸?­&G7GÇÖ&„CEL„ØüD(3ü¿/÷:!·ô?^ß ŠdW§©M†ÔûxfƒÔ038¡ÕØF¥—9-Eö V#*„+‡xd’€êòàÖ@¨Q?4³ä\6¬†×…»,ROº$.Kçp?Á͹Á~’p1‡Ú$È{œg”+Zt3#*a"’9jSFja‡@&ff¨´Ø90®à«lJèZ¤“çEÈ…#ç—y’’üýk«x¢¥÷þÏÇ¢>ÿÐ'*xñÑ%¡õ!ÓÊЖÈ`oåM‡5lFÝÓa;vgvÕøÏ wý¦F >!ˆìD-5#&¤&”喝DèUw«,Ö D@9#*[()6Œ2NŸöíÆ±œš=ºÎ±Ug£ \ô\pF…H‚ Â6QF!ÔcßCŽ|Þ¼kI³N2ùtØoeùú(Äç´öE%)ûï᫬_#B°|åÀËÑë’›„ô ßÞ±fǵõ£™¾éÇm)ƒ³jnb’H’,dŠFI«~*éÆnÔ¦åE.nS@€Hª·Ç‡+Xáð·À°q4ù>óãl&:œ?Oæe‘ñÓÃÁ •áó+Z1Ä2 ŸÒ¶‚Kì_`º}6äÅ©¿)èlHæû@…|N'»›þò©¢–¤*׊d&Ý {&¬·bl¸…³Q#&ñ#$/vlK*Šƒ”ŠYÒ•#áò6gÅ®µ¥vh4~ÕFmܾ؛Ì/Ôoó„W¬€X…¯Š÷y•¯³o\W‹øÍ¯µõÑCz-„$ dn›¼|ÂÙ¬L È3r°»ÃÀù\ù Q¨.§÷Ä0#ô ®,þ;xáS·"#˜@#$„ ÷‡åïúÏ[ŸÞï*ÅnñüGÓe ‚ŽÏãÖÂeÅRO…Þ"µäÞǨ,ý ´DQ/°Ø‰ác‰ƒé‘rÏò éÓô;|d÷£´æ/À-ƒÚ6ËqôÑ5á‘B ˆ~ã’Cñ4Ìå¹ò8Ö|!ý°‹Ó{ÿ¡ùçâ×Tç[¾~P<Ýzû“î7rNàú…;©A5tw±ïìI”É(Ô?hzìH"ŽîžuÛÞî<ƒµ£#$Øú=†œv+PÎ<@éå:ò)´•?ån䃫DCe§>Œ²D‘YdR2=çX™ùJÿÒ#$ꀞó"€M¾zÜꜟWéÐÞðäajúÀì‚­o8wòAø5OÌ#*‡Ï»°¿Ù—™d+â?ÉGÚ×üdücäùI¨÷V6ÌUQX•²4=CõåwÇ-Nï¤Ëå_Ä«ën@dSbÕ3P,Ä$=1$’{.*ž±ò|A6ÿ— B¬V@º`hÉ#$|}™_Ô‘I>}ê'|v…jkÐ|a OyÏé(îö4@cÁž}™4é­Ê¶± ¯ìÄ`ok’eÍô© “»A;€+×øü·#¬20®Uó=µ%`pš‡O¼4ááX„ìmcÞU4gS±Œ’|©;—htØCÌâVp)Óö Ð,XzKPçÈvw>Ë™‘G9CM5Ì-s¯ª‡ñ8\"Ú¶"5Qûä§£‡’ 3òÁ2'‹kTO^¼Âå&ÈÑ+3ü–HC^“C:J%Ñ©ßßðS éÙ$„#‚É!#*RƼæý»fš&%f-u‹r6Ñä‹cs sÔó+¥6¾[ü“_O2)˜E]ãúNþ‰ž0È’ "²ØP’Z-hU£w$ì<#&틱)Ч#¡væ«Z X–*¤‰+£S·hŒ#$ôåìz$Nð9'¨Ð>Y½Áî#$ÁáÃŒì#&„óUñ7<#/âKhñ À)ÞSj©Eîwú~'ƒþGÝ_†^œÂ¥ó ÷â¤ce¨²{¤·˜¾~Èø¥D¦#mÿ]È`îKp1ÿ{H(xcšÕAѬgÇÄ õ¹$r3ÅT²#&94=|û{¼g°ñ“ÂyÚØ?Qñ£¹Lq9 f”ý/X|OJád«[ßÙUVèHåsñLüݨî:¤Co¾P©öC’ÇÝxÝ´Ûø|ßoµ}T_~Fn≥ʷf®~]Rs> eùd‹˜EÀð¥;þ„Í=#~Í$í+Ë[Ûødu»ÈÓº¿‡·åºÆŸ§9]^(˜–F>Üfþ®Cƒé …Ë‹#*8…‚ÈÑ…¡ˆý¤ˆGÉá0&¢"€9:±v…ãõ‡‡±Îz?«wfèûÎD !çû‰g%‡Bø½$D$ÛÄÂ’éÚêÐͽ‰“#&ñq”#$Y$@¹bö†:@C°û@ûY¶‚U*Ü&mc@)BydSÄdØ"ŠEÐØŒ7#$Š@ðˆ¸‚HÅH‚8‰ERG5Ô ™¾!ü¹ ¡Äƒ¢lµ-~‡\;Cñôæiï€ÐŒŽï^(þyëÌÆedHÈÍ,¼‘’@ 6#$ƒ­”’)ÎûØžÀüâS»q¢™…ÍKÉAIØAèÐYƒˆGó~§ót-‚×IîIRDN0´Çö#*Ho×úÓæå¡Ð{¥Yd(Ñ¢S%›áÞm|k•^•Ãtµt†Ê„Ä›KnëÏ2íiåÛÆ¸Ö0œÚšpÇãóÒ_Ì Ó€ñƒ,û|'¬=¯ŽÓÚ AA¹{æ`Ȩ kÒzK9¾^$a©Ö‘È4´ÇY±ü]#$Sd?84‚zžð0HrÙÂ,"0(² €K8.o© ‡#$C¦4Rˆ}(ॅ¬ÄïRò„ :뻋¯pBJ<2:M÷ùÓÐ^#-ûÑi^c¹#*@×Fî ݵ?4ùÑI8Vø,ÝØ˜é¡å#$Y½!¸£¨È ëØQ³²![G‹IË:Üôg¯Êv[¦ß;mssEÜdFÍ…´…@Ðaë¾¶Qì=¿qdë¹÷¶K->°ü„¼µIR^¢"}OWùÕ~ïì¿Qøc‡WÞü?]õz¹îô|®ŸŠ?åQ!Zæ¿ë~{{žÚ•¼…øB)þ˜^ÚN8î¯é¼´Îæ•#*f#{#*=…’U’I[žŒMòn†gýšÔ…œÐ9v@ànäœEJPóâI!þ#wãŠ_‘lõQË$ADsË'$/ŸWë^ÈȃÑ#$ÛµH&T!Å5ÀˆõCÞyÙåôþs*KH͡׳RAÍ ž®?ÔoOôÂG„¸Òád##*æ>ýÀĬƒqƒxv0"É1$#Öà&4Ú,ùüúÁN Pèì÷$? ¾'Ôk?¨5WOÍ×ÓŠ#$7Þ¡,†dž?ãúÿ–s OÎBŸÝæ{37§3^!š¢Š#*#*ª´´üÌc=PàOïÕ=éüsðð¨2/ì_ð§æŸEü8]#&¥Ý2‘´ýHavçÖ4Êjwoo)lÜÝ™! B)ïWïH‹‡é Aö8Ó‘Ïâêí{¹én?!4Mj´ŠRÁd¸¸1>àv…‚Çq™¶`\zN‘ ÓÌ@ÐÏè<.'ÊN{üM¼è@Ç>#*øÕè#*FÀÿÒÉpHª˜LôW ú:Î9)Ì2|¶xjµ:ƒ¨:Ã{ÎÎ58ÚHÉ/#$¸«AýAÀŽÀ luyë¼îm°›.dݨ Í u½Í©„„ Ì=#*}h-Ýñ¢¼Ênòöú¹¡SáÄÙÄöa’ ²#$ª¡(BÓÕ‰qfú¸4„¹¢Âpœ°3û:S·F½úÍgì†3ù`Û…öÁ8ܪßRÅX ª#PmµíÒÙ+$‚Ã*J‰¾ u†ŒªP$&AÇY!yLC#*¹Ì D—òÖçêʃšÎ™Äm“F<·w#*¬ê¬PåR©?96GWDùU”èÔ±=jh6ËéÜ#ˆ)QÌKg[CŸ€zu.žñ{~ð|ò°y<9z»px›Ìdø{ÒE]ÆîVæŸ&ÎDíÁJ!`èI&ÙTo2ߨ„rs©3:‰ÀWÒPvÝyåÓ´NqYu¥Ã3ñiÎÒ Pî¶õ íüy\ßÜ[. ü'09n#$Ìy‡#ŠðÛm¼í3G]¡°Õ#&(~dT؇’t87F$#¡y¯D°Äa#$ `q7^ºmÀøB{CÁä!ÛêâNhYéÌåÜŒ‰ªÌª¢ZE³˜qxwr9r#&§yª0‹¸ ”#¸þ8}çÕ³-•A³$¤ð}î´(Îxg]ÛÕ2‡òï;†/ý` |Rï):œ³Ù Z@‘$$,ÛKˆIßvïA´ÏdPÈŸ_Ùìœ>¿ì¥Œ‡ä]ëíÿQ‘™ócÝ\„W×ò£þÝé÷ƒô†øÙÏñ »G{ôm)À“ÌWЧ•QPrë¢ØÄ´‰"BùÑo²x/ø¨÷>W”ûLclæ«{š3—û×ú0lìë+ìáõŒà9aú«¶wþ©Öü~½é¯ƒv—oçf72‘ÎÎ¥ÛV#* Ë$ LèÄÙ€°ö@ÐfôH#*Ä9nß[#&LèÆpI Ò†@Õ‚BÈXôåT¡û#îõ*÷gK0?±#&ˆ,~Ȭ™ïŠ"Æ "?¯˜ ê .¨½©±x§cÏÅ2ëC˜á2BÐWÄH€Ø6zÎヷ§£6‚ íù~v8.²{U‡*Åá ¾/™Ð¯K’Î µU2H?ç¡‘?b=åyî+PíC¶ÈضU6a#*s‘‚8Ã`ÖÜoØ—ë¤ 5”…êÆ#$É$„# ÿŸÏêôèØýv¶=j^,€^àB$!$KK2Ê#*âË)$ã4æA(jqþ.:ÿ6>0ø„8@éï20Ë"ÃË™±óœ`Å¥U`-m£$üáåà[Äù°JöFˆ§„-ht0Ö]¯)@†äVÆšK½f*Š1¡š(¯£\úzä nî¡zgžxDeýºÛÖø˜¡éö÷ýôCÂW;X|oC᧸.f>1 €º |RÉÍ;g°ùóz3 #$Ô>øÂj¾þý¾ì#Àò8¦`ÛÀlà?›Òv• 60)$+§ºûÇPqÕ=pn¬…´ÿºï¾›á>š¾×^<+ôtˆH"Q~M…-¶!ûì½e#$ÆØ¿ßɳ”C¤üÿÞp'5iüû.="Þ^üÙ°BsJq‚pþîhtÇ–J ,P̳"©KD½RB1#& 'A$ÆIø¸Ÿ×‹NÀ<{ªš°Bêxþl4HˆüBãñ=Y²/Ù6ÏIæj¢U3®¸3–J•å@Z#$jnw)¨ØÜ}¦ï9Éáøò¼ŸY@§;|ÿœ//ð Úÿ~7ùâ%½ÛÕ$ þØò‹#&À`íéªäÈ_ú!#ÉðE¹ÔËÇ¿SŒ¼ãWý]óȦ³T #&3*Ò¯¡ítýf¨äªtRèùCÚ•ÑÊÁhÄšuZòð¬ƒÎ’ÁLýœ3iY,cQ }¥,ŸÀ°Á@Y#$"X ê,'UYŒ©íP_Ém®ÀªëÄ]þþÛdHàgé?M¿+çßü†¸k!„2®ý-`×£Y†}à”+h„ !7„ñÖLCH(Ôd‰#öj˜—º°•E{>X_»ËÁ‘ÁAåÐÒZzT2…¸'qÚª iÀ+üe)}Œfšêç0x鈶ÊÀ"š8i½; Š^>¦UÐÉ!àpâþÌN›â@>;¹­â~CFn‰–I—.éŒ=÷ב6ɾ>/#$CºŽÎ?W?t›z¹ŠqóûÃÚErØÁ‡î/µ6n³I½±fØ- Ùýüg†§C?°ÛÕÕ¿Þ°š¯¬Â'xl"™ŽΧlÅ‚¼;8ÆhÒX±‡èà·Ì«š2ÑÂl:µT‹²eÈ(tÜŠ]ÍAzÙ0Çu cýY¿ÐŸä¾#*Üö¬ðˆ©MÑŠ±²!îñUüÒ{ Ä]œGzîC]ÁDÅVƒù'„/'‚':=èRá‚;Öp•ßä†Oéw¤éa* îº4ú?«±È¤6ʨã§Ò¨€9‡Ž)ÊNü]»>}­‘Óú}uEãsª0”£¤¡±fv5§‹‡A± ÐQj2I¸v³ xÚ&>8øfh‹’ÊÔL#*s/ö YÛùu]C8½ÚÀäP(›mr¨Ueì#&†}²üê)m¥u¼Oíî>XœÜ,O¡ª¬äã{1¾ÀcÉØtÁWýb ÅïXxMáð…pp¹QG?)7÷AGùôpæøÞ›é`É£nQ³V[nÞñ3ëqDâÈrJ‚U麨ƒ{ŸæG3ä.òu¶ÎßzÚ¡þçyNèH±èøË#Ìí0¢(º;Ä·º'æŸÁ¿”’ŽŽ7(Ê$﨧Ýø¯Ÿ¶#*æÊàÖ Ä #&¬¡ù_G#&SPÁíóÈ4•ïT9TéX^l|'-Y,uUꛫsþfò–šºü¤5Žœ†pö(cX—µ×ëº'× d¿+ÆÇX÷2؂݊˜˜«6@ã–•LÝo–à÷ MG̽\+ Ù×E0®W‡J3#*3‰ž×g÷m®…™ô|2^SÆ!¸ukIšSaÙ²™z»g»®¦q+ÚµÙœ4-Ðòà–[Â8´YqƒBårž¶¹Ñiˆm/ò¼O äÙ¾/[ÍÇ—Âæ¶d œ à£eŠ#É“`\ƒá”D3ãÛŽ06àNîb„к;›»l±=?ŒéûôËôÎ,Óf^Z:MîSÊ{6woj‡#*’¥š*f¯—«¦ÌB mf÷Ãn|5Ó99V°ÒëQ*Žœ·Xiù8ü;½s¡Äm~?åvÉøDÿ/d§íA™×i00·WüÒ@"@"Çú<ùË¿4½pGA‘ˆ¨³-¤å­cn޲CœÐÔ®mj®ê.Ä?募#&ïÆExGÝ]:gùŸÇ5NÇÚŽ»>4ÚËí¨Œ¢)ÝOÕ¯%_üŽ™Œ¿m“Ìùsyü®ç:áéõ#*Œõ›3PÿK~´ì—ûÓØ§Ég—8qÀ~Ñsã]¦iÝçË+hd#$Ä*‰©DMº”D‹N~‹m˜ºžÍtirÛªŒ<‹Ê)ã(•!¤`xÃGR¢6bD{tÎ,'ß#*ƒx”sî£ÙŠ1ÊßÑêSÓþ=4Æþ¯µ:»aK{Õü<€qÙp—°ôÜo ·¾ž{mF#³Ê &½œ|t;«Ùc(,Å(ýÑä~*Àz x™éÆ´0æ|´r»Ðs4õ:z¹œ¡Ô‰™¾þôD¡S tÀ:ƒo—åü‚;Ÿäk³ÇµžŸÄÇ•®|ldDJ‰Ü’ƒµŠ&)vð«½öà~Î<«|…†ÝóÍÉíš–æ÷ø¸è•Ƚ¨Ê1ØÁñ¹À–ŠÈp¶¶õôîŵЌNÙD™ò½80¦Ytœ“¤_×-ìç¾ÅÊÏTäVï÷,ì–š óÌÈK¸yîÒCuLÍqÃÌøíé“¡¾±òÂÄJ+åùÁÎü›D±œ»x¢-Úš€äLQ” 5"Æ’’‘u3#*±50§á={5eˆÒ/Ca%Brâ<ª)»ž£±V„Œ•H°­É…Õd.q‚õè蘫‚o×!ƒbÖ™&¦Òê`Áª9£‡A’2ð‚Ó!jÿºLôÆCcr,Q`‡¨ð ‚|˜ÿ/û#ë{§Ù÷xu?ð­8l@‘ ãþß}ÿì­íË„ÒóöF˜}ñTN›þß·ÙÌßÑ¡¡À¥ÿš#$qxÉöû뛌âÈ“( ÷–+ójÿ7®çïÿ8øÃùÿÐúÆâÔw& ÓèKÁËúñ‘o¢ªªÃüÔÁ„þLÆ×f#$Ý©Öl p+nâf¸ŸÚ†Í£ÈÜ›Úî#*.ÿ;ƒÄ¢4ÒR$b„F?ÖìOÛeÞk¡ÔTª5u™gIM‰„ú:jÂ`Ø`£8è°¡ñ©$ØÓ¹<Áá¡t4Ž dZ;Þ€Ö\‰½¸7\¶V⨪ïñM™@…Ó‡©ÿY½¸s;€;ßÒsà<ƒ`<¯¿²ô7­R¤%K¾R@Öª,*PJÃà0¦f$±ÍÐì6¤ðuˆ0~?nb¹s*Ú·)rè}$ðŽôCyɰ Æï–íäÈÕQäÜ¡83ôRW×ô[­Zr2Áb›Á\ Ž! Òrvkõ2HÀëùŽó2W-:š*6O2ÒÞ>…4Ã.G\¡ßC±ñ°× 'ذgDž½‡¤ž&ž¿`Ÿ•—%,fF͵Äôü»=ùÑÄÔ8 PÖi°4&ÆØl(Xi† ¢rp 4`ø˜#&Hg€"ˆ4^¬Jq:ÙÉÃŽBéhX…íE¥6—Ü;ÓŠØØ½x;ŒèGŽ´:ãåÐsÃj¬H •„8m¶š;I5nÐ"ßIëN#xO*U#$'Ï·Ëâvèü #*uçÝÌÖNÅËðöõ^énOÛÃÓÃTF¡ÇæÖîïME™è³»p:ƒ#$4âT1PþÁ?“ñÙXOH[aŒ:;d³›h£*°3L1ˆ©w0“Œ#*|‰™#*njeýá&ÀÏñˆµ’¨ËUþ¯mÉ‘y_µüÏç{[ÚY¬ÍÑbýÞ÷ºÂò±µŸµçCMçFëƒó¥Q,Œîv'¼)_j!ÆB0’¢!DÑè 'ˆ`Q¿Å@Àr„ HcÙ»8`Éë{{ZÀ$„X"<{˜…ýú>‰Ã¤›#*¦H›-¶t3ðxò,y‡É²­.eò!±Á‘© i`’@*âYike>³íÙ™™6ëD§ÄÍ?ìÏoÉÓmPHœašB“Y¢ëXß*IPlm’q2s©m }®:–ð„bMÖ€ÉmÝÜiÔAa¶{“ŸšÛ#&?pÓˆÀê-#*ª?1Myšh0È<Æf¾¬ùZZÙ‡šÐÆuÛ‡‡Žƒ7 "j^ €ˆ”'{D\DEƒgÌÃÃ:XÀi±%Ã;xT—eé#*àCe¤`så.\>£ƒÀ¶Î–o¶@¸Y8?áËw¥…U¯Ï#*›ÎP†‚›Œî²dC"È]×J¼‚=)s›µÄ‰JöûÜI›C_¤wë¨l®S1T$m•ˆ–L„§»Sto&ò ØØê“£0x›yçÚ¢äxwì©OiˆµÓf\"” ÔÙgiÎÑ´©N8¥Óµ®æMåÓ-´é<åÒõÞxÞµr¶6æ/Q7!#*„=½†µ’CÞlá¡ «_øÌe*Zkµlxº­™7«¸¡àˆ=«{¹ßÓòŸPôý}Hvg]‰Êc.Ø¥~[ŽYìír0[ChlrÀKÔò.¯{™¹…„Р깪OíªhÕÛí÷tóØï,ÊgX6y™%ÌçׅP”¤†|÷q˳»Níš¶•Eo;Ìì(ƒÜ‰ia½Ùàõɯ¹Z0ÌDZÌR’THåaÞáâ˜2}¸y‡<ý9D°1)^Jƒ ¯by³¬ºHÀƒ!i4z#&h$²íå;<ö`Mî®zÂ:û‚Ù½ËyïLøp¥è̦÷Ûw0ñ¦£HN&£gLä'päQ.´KÙW3P÷ûáèöy…‰4a©µé°SÀÚøL<‘Á”+oäã››º†¡ÆéÒÁD=~aÑÎç#&Œ4 ôIYàÞ!Æ<éÓÄ-œšOLýMëÕöv™lJ ðññ©¼9™•^ù™Ø˜(×Á!ÌmR3Ø;ŠŽç^Ø#&ØÁ!à $.[œ(‰‡aAUÒê#&û·¡•¡×µUÙ6`#ç³øUAw"àS¹ð7Ã%(›Îmcr×'S5Œ”Ñ´„’røµÞ>‹¯®öú~}|þé`äáÍÄ#&‚¼=9©˜%Цf%L̾æîîîUMfäÝË»mÉ®eߌæÏJoÓ1½$×;[}èÖÆ©È*H>ËTY3WÁÆ\£6qû¾]}/2ã½p;`/A#&“ÆêËssƒ3»¼*÷º9¦t‚ˆËK<ú ¤°}òýÝÞrËIíÞsc:ðN·OW#&°5‰¹3=jFC®hYCS8A¶\ÂÎÃâ\Ö‡nÌÌ=Üœ#&2nÝ ,BBŽÀ¬ôß…¢$æJÞÞƒk,X( iD™jÌ–(ƒчÆÚW~Çψ´Œ»‚Ý25g¹ÊÔJ,!25.!bÌÎ’[úsHƶg6|kë‡På§Ãö§Ï&E!“2Ø™IéÇG4æÞ6Úk!tщbÀÒ´’€ÛcûBÚéÆBI(Ói]„5ótU5ÚÑZï¸|²7\ÿ:±¨*óд”]ƒ„nt-åȪ†# &#&ªx¥5B'§²\ñÏeUm|‰Ñðëϼ“Õ¶HÇOš€o)à#G˜–XxÃÓ¾¯Q·r5¸‘º2ÎÁÅœï&uÑ-ÈL!ñZ£VÛ-Ó>7ÛAµ#&¥! -¾·Œ¦üD¦ÏFµMÇdX„€Dè'½eoHî\ÜÛ7,Óñ~¹Û?#$¥?QäÄ'G¥¢ˆ¿Ø!Þ‰Ã#&¶eUªíë„Ñ4ë„™¤V8¶­eÑÜ@“ ¦z,K9$úþ¿ÆN@χgx„hOű–a=áè€}Ú~‘F|5Ówm’éX™â:Bz"Ëè'“© };ÝýÈŸ+fÇøõ³ÿ¡î ÑŒ‰¸$éÌg¥ñ\W£Œ÷+ƒ+#¦Ó§wÒ÷|e7–Mã{îðïÏbAh)ZJC»ËhaÖóÙw¡'¢vX´h€Xb»eÈ©b0FÍëHöí!D(¿ºp!ÂUJJœ—#* A,¥”Ùçç8m‡d¹NµÈŽíÎ`ç§ùÈôÿ‚ôû&Y‡ú¿Õ8t­Ed·sw ]*ÑIKñÂ@›[ñ~µ©øªz—iJ-™²eÖ¯Úm²ŽÎÏñõ2P5#&~$wuYû¾gH-:_§~ãöÚ¿-)5Ki“T¤LJت Gùˆ-$cˆœFúÌ‚P¹ÊPE.wúÏ® † «FÜ.‹¢š$ü?òÕ‹b³ä¥ÈùH¯u­¢©¡)·ôé¿oÙðø’ú°#&Ÿ™$$$¥¯j¸Îô”xYSü‡Ø¿Ñ$µJ) Ð÷ë @ Þ/‹ßÀã`r¼N§×‚²ÿ\í$F£pó‡8µ#*›™ RfÅx}„ƒÃk„"•¢¹#$äªËCØB@! #™ñžRå4Î,b¥”Dv)Þ©À:#$uO¶ÜFQ B#$ó#&„ù½gõòó¬ëùúø©âHH•hŒR¿L!V1´"ÑDb¾‘²ÎHy,#&dŠ,D†aIêƒÿÍj¢˜ÃTŠM-˜ë$®õ7©¹®Ä£wv‚Kî£U%ÿÊÌ“²Œ[j-¡YR" `µ<Ѥ¢8+N±Q •ßñû}Û[z¾K-egí_6A M¡$‰@¿Huv;ÀÀ î™!pÓýR†Â–6¼ÖD$’‘œ¾u†Ñ>^×ý>ŸÄ¾í¥Ig¬Jfc š|²8ÐågúâðŸLqÝj¡ìûŠ©#&Y¬ï=+æ[Û‡3îÉçBæ\¨Úî ®! \hÍ•µ‰AH¦ óBÉß#*ƒ’"³”0¡¥´4ÛJ› ÒÂ’ã`,?ÊÅ#$ĈeÕX웋凖V«uÔbå¿p`§*ðš?ÇÒoåÇÀ¥Êo#&"Boôõ›-Üžóù<â(XmÔØâ«ï«UƒÄá{a .#$½Bï{;øZHK<üZltPü ¾AåÀøÎºôËË6B€ÿw—-nwµÛ˜co33`|Nx_øÿ¿4çÆª6L>8ú!âw£ËÉqÎ iVH#ÐgòÉ1,;næ)›¡G*‰FéÚ¢{Î"ü½Ý#*! U*¥ ç¹7íPJ:…u§²“¢—³¦x9½P €©#$=!#´Y#*È#$ÄŠŠ§$yJø1±z©$M¥£¾dQãHî¹%ÉLZ b¾þ”¶–Ã2Ã2©öoïÄøu¦êm«=C'y01Þ½Ùn­ß§ÃÓ² ¥‘”Ĩ閈ŽèhzúÖ”¨äp¸>’Œl[Q¶Ù6Ô[&#&𱤛(’£Ee¦µbi¬–’mcjJÖFTR™ @Š£‚ȉí?aùòùt\KIj-ô”wUvó€‰è‡8ƒ""EQA«É|#$°“#$‚`;:¾ýW/ÔF<Ú¸±Úá¡Æ Ô8=°ŸôÄL¹ ¤à£™#*2›)¦Òo p‡éÁVY¿tU +6#)Ó$Ó:Îüxˆsõ^‘ýûQTÞÄg6Iö+'G Ó¤d/1ñ0êðd•,à Ș$ÄX^` L•xrœö­ÐrŠ{ #$‹¯b¥¢ŽÀW‡³0#&šn‡ u;ï1A"ãfW\(%Ø\.ä @³o}ƒ¤Ýæ<ùÐk”eHÀ*TS`Ž­›©ƒ)#*„ í#$°.ìjH•¬E¦†ÒËo£5r±‰*ŠÕ͹µÍnS6©”[3¢¤ÖÆÒì’ŒI#ïÃñyʹ‘„³– Ô6.øÈŠÀ‚È#$9*ž«ª;ìâúûë{ïöŸÉíŸÓM¥¤20âáoÅ@3 ž'"úð;„íÕv) Û#$Ô'x|¡C}§ÂCÚÔíÿ—ñ}«ðú?×ýúÁC„ߺˆAňH­Ž_†jfÐPv±U–ÔŸF‰$Æ@zçB0gâê„h DB+#*">ºª†GÄÝ{ßyàvvs±’ëYP§F”/5Ïö@Êù‰#&=4ÀŠ}LþX>àøVSGª•†$ŸÖ‡Úüù(BfÝ}ܹ8ôÃy`s!oè¤ãÊuî?¼I?E"»°Cc#*`#$çÃí<†>‘ r‡}e׆ƒæj­»ˆS X~r„¥#&Tàl¾,‚>ÍCSÝ`‚JŸU°º»»]ÊXe‰Déñý#&À ™ŠE`{3@[:—½z|oþŒ ÆGŠ‹0§«0Àí¤!ôzŽñ;€è؃îé#&;Pƒp€Ú\é`ðކ8ƒâ#&ª°:VA3€dŒ@Ó˜8úY3uwg¶¼e«á_ÓjË˾Ûúgô×&î»Þj‡Y†e Cˆª»â‹­$ k v-W³¯º#$Œ•,7‚ •ɇ½ùpL¢S[è,pW„!Ú¡×d+n‰°tLM‰ƒB›Ù« L—Òy6“¨˜Æ:gði¾”oº¯Âމ›˜;Ö°:<Ž.‚YvÀÝ•—¶¶Ób‹,±â8N7T-Á*i"¡@—(íÃíž:G!;jyÍÑÓoc4=£˜Á°Øn½1Ô]{sÇ®2¥°ÜóŽdé àMå-ƒ‚únu³¯&ÙÚƒM«‡ 3†÷ìèî#*vl`ÝúbØ› «-­êU™T³V†¢žÝÂØ—Ö ö†w6%‰8 Õ&„Kë¸-y4ž#&h]É®WCŽÞ5öP#&ÍÃMÅk X…SŸÍušŒ5Q³|°Øàç·‰´-¸ë(’ƒ¦é;†Jªi±!Ë—s÷o×Nly4ê•þ¹÷,ˆÁøkKÑ…®Z'黳õ~¯Fþ'䊔üýyÉ9Á D¼Hßı\Îìeç^'ÒtáÏ. ¡§2Ö$3!~,I$áÃÐmˆi[zdÕ!9Øîðœ õÙ™·ò1.ÃØQÙ=~15çÑ$H&#þ§ÛÉÔØ{uŸ¨ºŠqÅêÎãB(ô¨z_4:¤:Qíšš‘´~VDxšó$IÅõÒø’b«~ÛX’\ï[ñ^ZòѪ¬Œ., ´Úrdäò×°B#"–òç©yl¨_Xs'?<šêØ v}ºÓwwPœÀé­'L¢ yV ªu4µ*·m¹ŠY²[].l£ºº›9%Š´¦¡gå…Ik)i`bT(ÊL™f#[œ’XY¦BÉî}? ³±„âæ6Q› ‡]¹’È€6„V Öùwªß2ö¾:*šÅ3 ­*$‰±ý´ÁÈ<8kaMœºÓVv¼÷ì5¾ïËÓ}zóÅÓÐÔ:aP!%”i/­],ƒ&³mõÝp~RÛõÊáû0)&@,#**Që¦M³ï.CãgÑìOS­^ˆô|"*ìÐa}u2$JŠÞǹÖ`±—Í'LŽKáÞ?<‚áXÇK#**|¬È&z›ÙÊ@Êj,5Ëã]©{/¸#*ôEh)ÛÐÌ ¶WëŠ×#$b žÈo*F¢[•føUŽ×iïÓMyn<÷ Õ œÐŸŽÅvvOؑê*²qc‹- °#*†D¹IaBXBÙ0¢ ¬aj×[!) •–¢Ç´è•#&²KBCœÔàzRÇSq-¨RëÕ}»îL¬h6î»U¨­<¥´E&Í@ôÖ˜‰ˆ_ðÝ“×\Ë»*>ÀÛ5í„ 4‚Ï{OêJ&ÇL7pDÁÜ×È‘"޲ÈH% ê:†ý)Ò&¹kw˜‚ •úd›JXÖØ™µUU>Mê0z‘I$|qÈ+Ž­øGÃük'|¢>Y~aóeq?Ž;—ņ#*!±HŸbBÄ;KÐÙ “C³[Ôšdì–pôºƒp„‰¸ÐÚê͵š)"fþvüöšu²ØÑ’÷÷óð6„àž#$©¥²"ÅQÒ<[5¦pÌ*ó#$.æn’è¡þ=úyßa¤S(ibFD~T¢$Yð?âÆ3·ár¥÷³m¬øy¢c>¯¡ÇA$„`ÖZ°Ôl€ø ÄJêû¹?‘¦ëNs¼zk ÃÕ¬; ~Õ¸¼`ý‰ñ{þyçÑêÇÊ ÚéVU­Í£;-;5¦d¤vpú²ùQ18c‚>Ü”hLS0‡@”#&µaNÊ6Ÿ¦ú¶´“?Žºá–³©Cm'Okƒ)$’câ-£}ntm€V¡áËBbÓKÔžƒ£~tUC#$Qc~*ÎVhLœ”¸_¦”E# “¨A¡tàÈ2 p†øP"pà–j£ ô (@kÅZt%Î^E†A K dN/ìs{²½ øÑYžjÄ‚WX‘ýFÁ Ãy€ÌÉ]\ÊÍAM@ï`¶£m›Ë±‰©!O4†‚SC®mHc‘ A,CIJ³~g'8V—ågBÓ”»Ü[•-0Ì.} GTœ#*sCBQ;òòõ•zÖ­Ô&¢M•·’…$…ÂŒ4,«#&}x\Â>ÿf×WeÍ·ãÌ*'”Âq'ã'å8H=´8;hJÙ”fråjÝ;àg¢ý"õo“îÃzXÉ瘘U›B±GmO‘¹4b’ò?¯£a¯Á*‡hœP@|ø³™Æ|T6§ºkG(§†”Á¤gÏÑ™yIS'Ù#*Pµ<ÐÏŒÔìJËJAâ9ÑÉÖXÙ#*b*T›^½ûÚž|•gèø!˜,AL Âi@5Ü1¬ƒÙbC¯kÕ«5¹@>hc‡¬ž#&Y˜[ï¿èöÞzG¼ÃçX¾×Ç:tÇïIk»t%X‰§U04úq"LYq…†?ÔÚÍÉ{×ÔÒ¢KŽéöt‚aãiŒÑ×gƒ‹]a‘ÎÂBTïºts˜‹X±boó&Èa&éßLJÉ»r#&±K@v„EÞE#&‚6Š#&µ°H–$쥄9CÑ­1™ñÌ3fEð™· s&:JHz2«#à|0ìáŸÜU {%¶N= “¦‡ÌÓ'C<°áꇈ'X‰P<^œ#&IØÑD<§H°†0“±ð“Dl¨•rÓ,·@ç#*0¢#*“™²”b[IÛ<›q‘¢X%±KÀ†¦$à…`bDåKTÄ<¡Óé‡C'LYVãËqð==D‘êÔõŸŸðÓë¶{Só‡Ç9ÝML™Í5#›Ô’#|¼™[A‹Úæèu˜‰^äkØAÆ:œQB3³eÀÔ/ï0ÎÊF ws+¡$®²qùDJ°%†Èh%mK‡ƒOΨfö¬¾J!…jkü¹ç Í‚Ç$L¤¥*#D^qN‚!"ŽÎú5÷³PR:ÄåÆð_mf@NW ›#&Ž¥Á5é44Ó/¿e“|Ào÷ØF†œŒÊa¥ZÕœ©'¯#*‰dMuSŒ[xIÖ:+*_ÇÃŽ6Ìt¨xÃó'5£š|ž­ømªX|ÛVèÔµ;¸"i–Ù¿Ìå"C}'k%Ȱ÷’ÀB~#$¸#&ÂñhJzHÒKF€„wÚ-–²oÌ"ˆ6 €)ÅcÄÛñöwš>›JÙ°ïv”2#$Ò˜I°¦È9 ±¿ ÀDO.v,UØØ@ ÍÌèos#&Êl—cBŽ…âkzª©WÅŒʱu°XkNÌÐSe€!€JyÌk²ƒɲb” ""að "a0¡·Wã„ú‚Ù15k̸è-!îÁÑîzlDŽòsò‹âÝvµ‹{”$ éŒë«„k·'£áÕºMQ¢ÙSÚã÷’Àðob˜s’7<ÒB%²úCå'ûŽfmYãVs B¥ËüÌ0Ù’5{Lb±×4?ßÞ#ÕD ¹Û:ì#»ÂžÀµúí$”©«_bç‹n °6£kQmx¨ÕÍVØÛkP#*j*È€2(A!CBë>Z®\N„/ÕÆ#*)«ª;$ï7[ª®U#L€?ž33ÙÄèr òšjl®=‹dƒZ×Þ“4I#B ”É’™¥©šd”¤Óš¢Òe£ò:“ kH`ÅDÒ¥ ”Ñ#*+K÷íÊ5¤Å’Q)•²ÌɉšfHÌŠ4"¨¡‰J&—Ǻˆ¤²(PS’’Ì0ª “F¢Ò¢Š ™L"ŒÑŒd­1Mb¤&Q)2S)¨2•ˆ4Ði›HÆ“4R7¿»áÜ[M•ÝÛqpÒ#Þ†÷¶ø°LƒÚüÙZª¾¦0…»1¯ïÍ™üGd6ü²m3vHPÃ×âq,Tåa€–•9cƒ³q$¶˜K#&ûxm”(^Ä”e²l¹Æä¸as9ö»¿€{(’5ùÑèÄ¿và›±pñ´ßD¹cr'“™…œµj=×]ºZWÊI‘B+]3°ÉŠÅo¹¶6"“ Üt&KùÄâcËÃÚ‘ù\ Ï®ÌÝJà)CZhmL—hébÊ1è\½LÌ+Òpd!#"@@²6k%b±&Ó,ÂI B,;õçÃO>³#&ÛÑnúÅ€™°¢,#&Ña‡o¹žÌû2rªo78Ã^‚Ì!ZDàÓkHy°la¥së·þuÇ{|åìx*/`sø Œ ¿Ì‡e#àÕÔíÖ}Óá±~´‡tóÏS™á’íP*‹+¡|Ï#$É ÒV_Ñð³rêS>z^mË –pyD6d|êIöøž¨‰÷Ûrb™Y–A!E !"H^ÄÉ:Ü—úº¾w 9”oÛ_~uZeßaçŸ8£¤B)x2ûŸ]UWŽäI*#˜ðËn+‹wåÞyˆ•‰=ûÿÉ’(–SSy¼æp2[s3œÈÓû>©ìÏ,£:øÒ.j‹jš3p¢¬qPÃ)†ØÙÝU…õ²ëyÎ×(aù7Ú`íÒÃ4û&ÖœÞH6[M|Ö4j¾#&o“¨%Ñe á1ÓmGNܯlÆ\·êD¼n™gNb²‡`ê[šiÜOy£]ÒèÐÆÈ3k¹žJÂ#*áøL@Š”õ/¹íyÚœ·„”­=Ê vÅnvsÖ7%¢4Ot¿¢88¼ ×q ¥šŸhÄ÷/Ó2óÈ[¹äóbz‚C·½ŸZ-) #Œ&Q{»Øo“zžHaDþlÖÈôôÙ¬õîáâÝy€s6Ò@B÷üIÜÕ¾Zº‰öW·ÄàÝö>^ß1ˆ%Æ‚qú|ë•ïýe¤íHTÏ^±áRqW,ð‡×Òàgn¿Ÿ?–ZÌ~‹Y~®t üC§+Ox©b6%Ú‡kÓ–éôi©Æž Ð Z¼#*“Ó¯m¼¦ò:†ŠHH|2 ´IU­ ¨<ÓYÉ8#$Ùm¸«³UI_ŽÒÇP3z$ˆ©£³a”%œùìLÈéš“´ŽàÞlË#ÊffB޵ÄM¾¬¶ÂI–ÿÈ÷bÂX°ˆiEoT­@'Zþ3Ü£,/fß(ÖI  çUwsœáÀ…eÿ—²¤²;S#*à•š†5œµ»‰¹œà©·˜J<Ó #*;0Kñ,óÆ‹Õæè±>þ÷tëq¿¶©³Vò®nøåw”ânb©ÇsÆTâxs„ÛMšow‡Yf¸2á³½ç6ÍË4Í4ÌÌ3|ã¼É´T;Ó˜¶µ<ÛâV”¥µ3¾'òš©x—Ä’*Ä92ÑØ÷v…h¿[É«i Y}æâ^Ì;7]÷¹¾ÂñÜë›»<^a˜šJ%¡$<¿•LY‡6‡”m°üã3½è©ÄY4ö Gæé:CÓܼ£iÊÉw™Ôj#*6É 4ºÁ,0&À†´32w„Á˜du©é1|lH‡—#MúÅ“¢fqovaIÃÌ&*_¨€ÊÐúŽ®ÎÚ|ñ¦i7vÔŸw:CàÝ l…SUeÍ£ç‰!Þi‚Luž”Ù€r»£H‹‚T4’ä'å@&L @š&æZGd>æÆöщ“µI—0C¤žŒüo¾±vÖ9à°ë¾à…Û iâ5­zY̳Ú~s‡[i©(Xðrk.–·Û\ÃýÎÛÄÀÆ" wÝØï´™/!Ú„Û,›)N¦Õ4bц› I¨Š?3^³{ѱ±Æœ«ËÆ$Ë.6¢Þˆ•›½³Yœ™ÅèÅt|TëzÜR3§:Óº¶6´=V_ä¶Í”#|O›kÆÒ?›á·‡àFv$„ýn$é=àJL%‡Ùfµ©ÏLxâ‚3¶48$„Ñ©Ìg·ÁšF£°Æ÷sÖ)qÏ+ß/žq˜nWd/W¨J‡n2NŸaö61#&ƒÂêŒ>_Š˜Ùöiy$‚pô©( ‘¤0¥®.ëˆRë§ÐÌÓ¶u#0DNެÕ#*8K.Ù#&›8€–‘˜Ý3#$û<S'’ÆwX–NsöÐ NÙ0Îd ÁúJ†¶S¼4Êûq”ÛG5N’N›J¥ûÖ90Ä¢¥b¬+|ÜEY7þ궽‘”À„&XŦ²FÆÌõåzÆeí߈sˆ˜u)B´ì,½‚JH˼¾f‡ 'tÌÚ1$1uâÒd¶‰%DmdÓÂÞ!šP9h‘HS”\ªBGyËU,5ÓLİýØ{°Ìë0ËÏö$úOjzU¾&b¯{ôлI&µ‡!]¨)¸[½ ¢LŠÊnSêÌ]©¾Ä€P$Åøä‚б›ÈðùUˆ5;a#*@Ò-²h•J™S³;hÈ{6pû¬#$¥l’qtœ~L±§‚9‘œm°ZPLÒ;Ì7CN]ƒ #*\Ãiè`³·=ò³M’r q°‘p#&p ìT[c)b£hp¤¨HªÏ«rÊÖCYé† `šùaå.ýr'L<¤;øÛ´ðÉß(|g/¦jqm'<3È'lœNÎPÆdD#&buBc%g‹T‹ÇË:øÞú®‰(¡‚=R³Ë¨.?¤Fb!Œ'û×,ð’¤5bõ|'ºLH¸•‡ž_ ÷p³FVOfvúg¤ÖC¶cŒéŒ˜|Pû¿)´™…N)H=]¸ÈO‡5ŒªÄð¹ˆ0ã__‡.)˜t0ò‚ ˜yibv]2XOFà'} ÛÊŸ=ÜÜ7©NÊtP±‚Ée éKÓ‡Œm‘,îÝ×ô ãg…–Èþγƒòz3»éÇÝö!ð©³&‰‚ ª.P÷"¦vÜv“g#5•½¶³Òb˜¼«…„Xî8¬Cí{Ñ0:æ#:rÐâž]}ç£Ðx6w":'`MFK!2L÷:†ý¤¶Ál±Á#&‹T8¦Ýê¡§+€yl7–ÔþÉHT™¾ï”‰Dv¡½Cü,겈#$PÓDãN湩–×ç¦[m&òRj@€!c4@¡².îø%e@àm0°1H_Ÿw4‰˜#&ON…HI¥ ÚÈZÅš„ÞjÇB%4¹CÏÏÔ¡ êJlåðÂw*þ!UÛðÚ¤À?bTþÔ¥ h1a)lbVRŒ *À¬ˆ‡ú 8l¶"ž ~NÃÒQhó%B“÷=üHî”ÇX¨î@v¨nyù° ÄHgÅà‡!÷‡2† àE«QD™…Oq€ó, ú@û Oñwuälbüd\>óú Õ ñre|ðkÿ‡ÒÇ-ƒ‘lödOmÜxcÉ”'ãÁ“¨™l/NdY Wg¹p3¡éñ꓇?»©:à"“l…âIfιC%Ã+°înÆ—JLé2ã| ™<<§,‡8³«B PÔ4wk1ó#&AÍøÛ¿¼.\±ypÚ —CÆFòK vg¶”NÈŠBILÖ6Ʊ£Qµ£j$Æ¢&FÁšfZ5H Ⱥô$B•]¢}#»ÍÇ~•¢§€"•ܧ½½W$È2^ÑÔð€&Ã_Àûd~Ø2Œ噂"w}$0DAˆâk &B°•¢ñ :`LG†Ù€ÉÂz^'W¿WPˆÀ–粋/ä÷'÷f@úš Ï«âP÷'ºm²ŒŸ×”´D~8t°ÞIíUzé1T²ŽhcY¦f8•u…3hͺeÌÒ×}@õþ¼!ŒˆÉ®2C-*G«µ5„#*DË&‰¬š¨°!1!Rtne ¤0Ä"Ô1“¶o#*Tš€ÊßøÎ÷ ¡'xæä!‰øÏ}#*‰ý'¬3#*úµH°DŠ«»I–„ªÈ‚“?Nžþû¢rÚx‚eK¾ü'ŸîQßÒá¡T`™7³T]ÄEZ"L‚1ýüûs¢x…¡Ä£(ÛLíç0Ã6¼áLg¬*ÈzS‰¾GÒr™«Õ$ CpŒ-)¨Ró‡l9#*F× &RDE1Ö%œ„vHÍr¼å‹eßù|Ô€zþaÛë)¹EÙ$‚cádC#&…Ù&±“ñ•Ò4i> k#*D Åß`6@ÌE #&I,Ç/º#$hÜü*­UîmßkC^¦e{蘼Z ;¤Ò Íáˆ@š„~w`w+u¯6xÉÕëÁàÅ…J…Û;ýs­¥çê ‚“»³Èî#*#&–ÆVïlZF¶ÕóÏ·en¹˜‹Ò#Q_6ÃÍ*5 •¬`2&-†¶Ž0ÔÔÑ#&RâØE6á&ö•)àóÙdǘðÝõêh‚;{{è²ú3¡ Íf&bCJ€„Z‚Ä­ànj¤%ÜN @ï„î"rïÊÀ+tï¾ÖU¸ehÃå ¨ýúN¦¿­‘œ¤ÉŽnÄîìãèÙìApÖg(¤ƒ€|ªUd¥Q&‡:!áèå§½1Á%,´ÆxÊcœÊ{˜‘¦ÙâÔ<•˜å¯\ bF”ž]ââ¬{!S­¦‘fùgjÞ˜Ð.ÌP@`vçÆ%6aÉÀ²3fÉ=¢d‰ ©Ð²Ð;ÆàHB0¦bt %‰'‚Pðvp"†ü@ôŠ *‚˜6|6xˆ{O?9‡~A(Õ™Zû‚ Þ¨GŽÍ`3ö]wÇÖUoÑ{Õü›^Æ%5M7fE€¹1SÚÚØhäP{jÀX¹àOÕ#&Šúiël!šá޶µ²û-CŸö6DÄÄ(‰Gî°1ÄßpØ€Oœ>3ô¥²Ä"[wwrî[·ÚÚ¼ký¦µ‹¢ÖEA‘!ïP‚D$HHx¾¥°ÁúƒÃRܯ‹Ô¹ïT®\­ðŸ5_Cø@ikE/ŸÄÖøélêV¯ ü]Y[ QÕY[Ç-‡*ÒJäPÙ7¾špÖw‰[)—“*ÕLì©Râ–Z̧ZËÞm´›(m §ÔÄ ÚëcUꨉÑÀñ°Óã$9 âaÞ4Û:tÂ…€«ƒÙ™»ÖÛ¦áÞ›ÒTéC+2ô¡ËU%mÓ7¹"ebšr"Ló.“tDÜÉt8˜qM§/|(pL=b‘¨†P– <»‚-‹v}žUM›ói Œ}hˆ6ƒRöÖ:xs‹x«Mºpšg´,Õ&.nÀI™Ž‘®$Ì»²1©‡™ÓíCC ’¼áSÛ&Ã2l¾aeéj`‚r{s}ÒïWÍ™©mÛŒ ˜á‹=o¸ÐÝF·H‡¼ª—ÑѲlQLÖÔ9¦ õ3÷5n`,%¨Â2&£¸¾Ô ëYÏ8º±¬[nËeRÜÛ³fkS¨$”÷‘§ówã÷Ç28˜2*•*Œ*èc#*ƒ’ƒ ¢fŒ$3)c]nm´ëvÕ#&4ØÛz¾ãš\(F•\‚$"­â4H”–“ãµÿ5±æ‡Óz0Z4Y#$†j•¨…¶I,­,aFE"ÛCQîÌ©–äKV”²å£ƒ›¸ »[¥šnjèUÖÝmÓ»vFb!¶l”¹0dQ+u+pØ®ÍFѤ©Y”ÿ#& ”‰8RŠm5 ajDÓc)ZeRHYJ&ÍclkM‘L×R×J²Ó&´³6[lùµðóÂj-P`±S(­­ Q¶Í)*’ú/?Oð™¼ÐtQüccR-)®¨¬@C’YÈ&A@P¨›#&­ AK±(‰H ‡8ˆ ƒØ,@Ûðˆì"'iç`ög‰í!]Í?¡…,;øá@²™†f¾óoÓ0ë°âÄv›ÙàHHx„=° Fð×¥œÑ™¨!ìÃìçÇ“T]›:¡l*…T)ì±Àƒ…§2ÄN©U–™:ñQ’C¸é]Ѹ:`^¤Úb•9'ÕüìˆO*\b•*©Fˆ„‚ÔêE,þJ@1ŠÃMÀ ‚9@+$OocáGáqJYF¼k¡,6S# Î^‹ë´û0ÙAËÏé@õå#$H…ol²>à #*A÷¾±iÄ0Îä;!)p©"@ŠÑ¬6pÓÛýo#&Üö×{Ò<ƒ€ñ1ÈÏP³V,»ööhì6ŸšÅûä¿ÏÉ»íÉ>9)´³Á#$³æµlK²ÍZ}w˜,o˜‚_,X @ÄT;â£l†[(RìçäÙ#&•¯{³ìQ6à¶Ã:‰A–41£¡Mô­Ñ¬È x) ûðHy>Ó¼txÕ³âOå™LÉ»"€Ì&’kÃÅCè¯ï‘#$€)H€@Нñ=þÏ“é}ÇÏ™­¼“¸ßa”D]ñ™F¯/Ó‘p¢:ˆ¤’AVv`mxì–¢´‡ó;ª6ÒEâêQ-$Ê£$›šÚäYBZ&²Lk{îÕ®M6›FRÚM”ljDSL²YL”­1K-@¥÷×XSjÊf6”Ƙ“h¢µKmš'§VEjWuÓjû[]­Ú½ºìh©ˆ È)•¶”e©MjKX#&MÙUúê«­|îD±«ß»d›"T–µ¶D’›e­n]ihÒfÙ&¦¶óÎð¦ÄÚm6P™…MX[ilÚMìí¶©µŒbÑãnŠÍ–ó«¯;’R­2hkšêÍRW‚ë5xÝbSÛBD“abŠ ÅgûÓ49ñþ§—%˜àzï•%`ucË9`ê+K#*gyä|>õéÛãàE|ž-ôQp/Ù›.í·K)çõs Ë 0 öôô>Ȱé¾bªd¾-¥JzOŒÄfô#$í ŠŠ(,H "eˆ¨È9Ê`1%…AY=ZC’Q"Y$FAT®‚R*f )¥6×qJ)’TÔX-@#$$A66b–™˜–Ò(J-)5J[kM³LÚÉ­†”ÑJ”Ûå[”0ÀD™j+6Ä´”Œ£M)!F†Ól¥4‰©3FÃLÆQŠ †Œ­”ScdÉ%‘¡cRjŪR¨**SeJS%RZŠJ¤´‘µ(†Ôjͦ…¡%&,™I„¦I¦J–U›b5DU‘#j)bfÔ™$Y¶µ,ÖLš4¤š”Ûe•µ%U¯z­wm­+-i´–ZÒ[ÞVµÓfʵ)€"0 #$ã$x€VØÀ!­´j”¶ j-ZDÕª#$T‘‘$£NÂx÷Po¾¬æËU±*G@hO»y£Ñ^ømùÁj½º#$fþ†pLÉß°ºrÊQ52zÃ^…ÀÞ@D»¢žYT©0y:®¡Hò…ÈÞ'—uKTI~qßÓÜ"µžÕEô‰á{§„7G\yÂCÞÖà2K#÷øwc]:é»Í›k(&cÎmO”ó2/Àða‚æÝ´†IqÌ6OOrëzõæ7þ­8>[…õÎñqDøH°“5Zu´á!î/‹Í-¨ÄAžô¢1TASKŸ˜ÐO¡ìI_ÒP[Ð¥ºP—Þ ú5¹¾ŽÉ°6Á8ñ#*T¡#*üÞ¢„»VpMqÊš ‹¼ýTÓNÿÅÂ'õ:(‘M³bÕŠœqݶӳ\xo½t.“J¡´CÊç0à¸`Lä7CX‘e?B>¿"±.{c%*Ü6P -ß¡û>‰¡¾½þVD%-=AÙªR@’/öXL‘ƒû-zJµþ2ãØoî¢Gîzñ‚tiž<Á/9§—R~#&в[÷d€Qí¾s?ê3. J؇^19¤?Yl21ªš††e­Câ©+°ˆ^Å‚:_CßÖÛÛ´à  @òî#$#$‹lj¨Á¶ö-,•Ѫñ°Ÿ Àø”òÆàžÀ‚"2! ÖDêåK¢ð5ZÅtÕZQ] ûg[ïà{²+ H¼+S¸º¾³Ö®”²Z4< 믤ž™íß~îÊ”¦–Ùß­jýæ:‰OáÓnï¹@Ù r.œ<Þ½¾ °åoGhÞtû[ºîÝåJ`ÈËôØšI#$#$Ë0>|maép$Ò"ÝþßÖÉ`*Î ³á°¢Š #*•v܈ÿÎ6T†² ‹2‹&aAÿgü„1uq3,¨¬ H3ÆL#ÆÚ%Bª08=l)Ëo:Èf4Ñ`,ŒæRk6$êV9#Té‡|¦Æs[1€s-JFá 1í¬¢#*æ’ÙSÆX†Ø(+ÄÇw0v¬Tf=!ºÕ l‹"øåH4³£ì‘Iª›þ#*‡{QÏÆ—8$­À†ëeÓAG78ÞŽ‰Gz Hü@C]BùÊ!â›- žÝO ÷gçãïhx(œo‘êËXs ˆÝ ¸>0dB'´‰ÑÛpæT®ÏnÓ= NG¶ÍÎä$€fô3 \ÝEñOsÞîºjržÄ—‰pÔ ÂV *I )áù‡øj­¢÷è×d±~Ÿ º Øv 7víÚÛˤù‡@úhÖyT šDù_ÂãRÞ=Á)Œ^ «~wÍ"HRð’v¥ˆª†–¼ÞG›’BòNнª£Ce¥‡7Þô׳vc&(hLp˜ Vú’Íô’²¦TF#*߯º†ñ?Ÿ&øÐüýÏ\=ûXñC#&A#*¾î‚ò Áå{<À,¶¹Âù¯#$ª"½¬CÊEò…Ú#*C‰:@Ä•$œ?®›-¥~ö.¸¾wñHp¥ô¥9@6A-*:XÜIón;!rN0-ª{=h ëU-Y« ‰Ž- l u€[AŒ¸\/Ň˖”à´¼ `em‡˜Óo8 N-¡÷0ëÓ:õ}™› ³g¤“Ê‚4ÈL‚Y¸T÷ŽƒÊÛ9íÖ£»²Ì*ÆÆ˜ òû,y¸ѯž_7…Ñ–Òíí½ðÝPð6"v¾:=bÞáñtu>èøŒ$„ÈGcÙÀJÚ2ÂÚ¿&X‚POgß’äg>V½{f}òïì(|H€À !;:)>a™ê4ɲ÷%Œáy,b<àà d¢&0#$¶×lôc‰϶÷¼Ž$²ˆ#*êkÁ°ÞCÃh##¢ï(}¼#* ÐÀ§ûG±#$0ˆ2Úó†\m~½Ù±šý¿_9o47eÏ¿M°Ö¹z¼.гÏ#*RÇ0‡š0rÏlÆ©Ãpàgn~ìB9 Ž[tcvÂúƺÑqÈbkÂÓ’'„ ô›]G'QŒ[Qr%ƒau²¨fC#*Úžt.¯uV"¯ãJ«êò3±`qøý\ò÷<ˤ{äfTÕèŽ,øQpÖæóy™#*F£¸¢Â„°”Rm]tlö»h¦$Õ–²åXZL„7,æapXE#&åÂþ˜Ê?~âEÕxízŠQK•"˜ÐªpAÝ‚ÅA±V,fRýƒa\ðeíªR®j±6ر­^‚ÔbÌ´¶ìg6±ÎZþ^|“|ÏQUEQUPí‹b~=cH©æ×¾‰áš[XÁYæ–e%lï!vÛJ™lVJñ®sq0#…Ç™mÉf’¹”‚®#Ž1ýlØ™j*4([e#*Im¢•;g4Zl-–Ïìúf/›L„#•S“šlCxˆK`½FA×™ P¯7½vuÙxt’Ô`3A|È©ˆ›/Œ÷¯ˆ·Cn|øqéßZîg=Á"ì*>Ìïßj…Íaޝ°{î*mmî$=¦Ïa,rb({8&¡Æ”pœwyzgÚ%T 1ŒÃË—·#ØÒ½OP¦Õ:¸4ìÊBºïÂ'v3Œ|óÒ+tÄ„dˆøò8àõ,Åýÿ I;IËX*ÈÃøÿ^±‚aû>Iù£(¤aøýr`h™ Ø{!®ÚÁŸ;d‘E!Ê@é©…nñº“oºÝ‘3(ëÍzÕl#*"2,e$ m°ÊÔp¤•’èâ(±åÀLhÛ*‚*0*TPN" ‘ d˜2T™† "Æ@&ݵ´ËIìwÅ}åÔß×]nI˜û>›¹‰ÈH—w†ÃeÉ<û§ÇæÏª¦F·ÚÔù¤Sèߥ ŒzµÉWèËÒðûˬ͚-¿ráò•<`j¯ño&²x·ƒ5ÿ´0ßGsoi¾Yåm¼TV÷lÓFÑ2‚Ö«”eSM&š•×Eͺ˜’É6+¢ß¶¹±Q­|´¢ªô½/D-ˆ0bÿ7í«AXDŠÁB)Q¢ÑŠ9ìd8è œ j}¿hò‹8Ÿ¯zÉ=øÃšŒ†!&2T±#*2Dcìo:ßh‘þ(uØo’†gËHDŒ/‚ÞЬ èfÙ2!Cˆ˜°ZpZµ¬a"jWŽ~Ý#&8CIàÔ7,#ç¹%‚°wM#&λ×÷IEC¡þÐŽZ…¥(xC¢Ž©"˜• <#*Ÿ×j¨ÌUgãBÂåY#œÞ)—&,ŠFÄ›}(ÈvÇö×.›š¹´Ó]Ý£i‘Q¤É ˆJZU#&A·Ïl¯Xè`aÞ³ ›×nøÓ&Œüª|¯Ë„¿Šaw¨öDP"¡Ä‚dXA·¥¾ðäûƒs¸œ0ZÑA:„YãI@@„ #${[cqk=$ü÷qÎ&™ú±õÿ{#$]Ž‘5À¹舓—ŸÒkÆ2¢ ””MA¿ð°ä®Fþû>ûyKHh‡Bm’°j¤„$v‘ §0M#$û÷Ù0 Y¤ØblÓWù,ù1ùé_ûÜ—GÀ¹ ($i(–Ѽ3ÝvM¿„ô_QÝÖ3Àziã#*ðQV‡4ý~qÁ@TTð²Ïa81#*êç{œ ëÔBtiÅO+3ØÈ°ûyÀ» ÀÌ"Ä'(H÷CX9î…5E6 ÷úÀèuWïô}ûÏ '¬€"I 2"„IT¥D(Š+ ‹*©" ÑÜ‹0D/¨¬ˆH„ArZjÎm¹Ët3ws_òAèÕ¨&ÜpBe”œdšÜk2$(·²üÓ£0ŽGiÒ'ÎÇÍëS ûþÝ‹Çb± DŸPòH16'Šy¥üÐ^pV?œ܃Œ’@‘RA#&Á¢,ûŒd>ÃQýL˜©1%Eu«îR'/)Ü{°–µêIÂõo^ýÛb;  ½rË3Q@_{Äö‹´«º{õ‡´îÙÝò‰Þu–·U¥|eúh‘ë #&9eٖ㟪b}ÊDí" uäHBH‰Òfšv½šX#*øÒZ©l®2 ËgúYÆáf¤ë¡ÿ€ýÚ0å¤**…²#*sþ,ÇkJN0*pµYV#$“Ë!ªÀ1#&f!G÷ kGºÜLñÿ&/jÀ¨DBž2Ãßl‘E#&Òœô1Öå÷}m’qb—í¾wÇ!¨ ™IÐ*IºRT»`by·y¹˜Ù5 T#*ÈzdÖóCA8‹%+e»Š$32eër6œE‚ˆ›‰q„¬–²——Qf3Ó*–²Èt•€xa|X ,XœÈLO •Ý¡¨l`¾Pœód5t²AI*±aÆc"$ `²t‰R§Žcr½! ¢Ñ;f$é8¢N™¢jbbÁp«1Ê].Ü«®šPT³Ä5ÐÆbó órh_9Í£n 釮SY&*(±CÛeaR¹¶¥wEFØ®[—ÈÞ1&+×w¥Æ‡—]·ÊÙ0k’I›ã Úxœ½m'i¬Ì=Y55 ´…Hàž‹=¿îstâe-böÙ;g2Äaï´]}àÈ_jOh tÛM+R. ¡hPi˜Î¥ÄÄ=Ù‡0øé1;¤J‰ b]ÆW¾ÜµX?riØøˆhÂsHˆÈ(e÷;(}`|©m®O#*“4MIß½ÔÆïÌ;ûê–ŸŽ4Àˆ 2,Æ ”¦ IHÁ”Œ¤@Ù"ŸOßÙIL#2 Ñü¼ò¶ó‡7§éoVŽ€‰üC )ȰîPÔ€(±ˆ‹HÿÈüžþ…€Ôý°TˆyK8;¿uúwUhô‹69|ü ×$4<:#*– $”¥!+kX…F, ’–¡gê±p…ä C.kwtÅÈÊ{7n×òצ3ÓIðÃÙÔõú#*kå71?²9A¼oUQÁd“øÛÂÊ@ÖJ’§„ÀHˆH‰2Ê $@+#*„ÆCâÂF1[%«#&ÛcÔÖ,EІ°8]ÁDÚK€yfµãW›ºŠ,Ĥ’Ú‹j "s«#$:TŒÄ-²I4§*(/Rùô8«Bvi,˜If%À§:H˜Lö0žÙ%d*BÿlÚXäÆh/@01DÂKxÈÁ'ºL@ÏnÄXÖþ Êhj(ȳ5?cS ©¹ f²C÷ºÌ¦é—C/FÕ‡±IÂy¯ºH°3D¸õîÌóòÚb«Qzi—è>¼˜µ‚M²ª‘²Õøý>]†4„ƒÚýÄn' I!D"æë&³.Á.X3,²át§Õœ¨(ið Y@]ï‰óãs«™R¨U#&œsÚ‹¬ÄCÖ„=Ã`ë+¹‚Zs­Ûw‘¥-)MV6 ÚkÞWliªÍ5”õ+¨ÚÄÍÊH›nÊóñúßE÷¯µ{°ìáê°b+tj» vFöÛ£èÕ(P\xl ‚dð°ú¾ Ôû6\N»þxÐ~G¦ ]ÂtĹ¦Ë¿êÓãäÃHsß§T˜šL›h¦ATšqpÿ¥á¹yŒç–0ÌH;Rµ„$¥CÏž÷N_ŽCÎ_RDˆÁ/˜ÍhˆtÉ {g0…1!ÛG«vC}¥}†IL¨Q}µƒi0 Œ&Cä¼=è êOG»Æ#*°;‰$’D„š™D|… #ˆÞ¹[Œ !Óètñ@J…DÆ#&6ò\«ñX²ËÂ¥9Ddu…âZBJ”¢Z!…þÿ†áÝ©ºHé©ÀGêæx:Ž’®MœÓú¬p(¡©ÐÌ“þÒk<æ6–ÒJyça°åÃ…4 ¾{+n‡^X¡îuh"Rcü™dž#*xaá˜p‡ `1aD¥šqÑb¤#TÑ<Ç×5ß|ôâiQ;Ó´ëÝÇ@N #$n£l *ÔØÅ'Rû--í¶ßx†×ìËo­ý¦ÐŸc$þoz Q`tZ)Ö¤ÂÓº@€Xñh \÷Þ烂|2&bâ€Â};Ž6Gœ{K¶Š^$(Ó¤Aì#*}éü8¤8D (ŠT#$<àQ#&rsCh#$d}¡õ"˜€Q¶ hª%@¨Ùä/j[¤R q¥OrÌ/g;ÄX¶31à|xéž›û®lÁ`Å•BÕc|À©Îp‘éë¦ÐÕ iJi}E²ø„aÓXSx2î.e4½ff`ò7G/Í.*à3þ%,,#& ¨ZäB·ŸÛßäªo"vCHÏaá“$óN±¢p#&çKU­kHEÎ%ÍSàn0ñQ“ÛÞ¡›ÀCr 5›ƒwyc(;0Måá€RxùQLraÁÄ &f̹÷“GñU¿³1§,1„ƒÖ•f’ý®úŒ0k-EY¸±6*y<ŸÀèÙóˆQîCôþÂà¦iYü5ÈÆ£[FÏÇ?JkÝUé¢#*£Ÿ2b!‹¥nâqÆQ:ëÄ•ƒ#$V¹¶¸Æå¬‡ òLóúná ’A¶¿³=›q®ÖŸTò“m&ÙJŠx#$lsz6Zw’OJœëüÇ’ì üÏçùÐ*NÒFº\!€ˆ2²Ê”LAƒ"ɪ~ÏÝöoËÅî¿M|-çlW¢(Ë+Ê“ =’‹²\±=2Æ.Aü·XθrÀ°ªïÿU]dÎØñh=Ö6áL$"€‚F3î¥!Ý*\ËiXÄ29BÑ -V$_»ôþðiÓe(§9÷lÑ"/TR°xЇ„37!R¡[h[Tã©&¦!¬’X!¨gv£dà3«*"œÌ>ìÙÖú>·;¦œ¤èÛ¿9“‰ à‚;<%‰C(S"‘Q5À%O¹1þZ¤ åœI{æ'dh‘"b‡Ê*é‚”þ*¡«L`)ªît†9ïú©³œ¼j‡ 2àÆÓ©:<õ½kž#&ÍŒ•”aZ!SO&nÈÌÞL˜fêuN AX•%èT 76LÅ#&Ÿ”h¼¦³³r4A"Œ8”¦L~@ÂY$M)D°¡‚ …„)€ ÓÁ°YÁ@0NõÎÍ'‡u/‚jTt@gy˜!—WJž.ÎÎò'ªª‰#$“ ”È‘UÖxMk¶Or~ÄLjžÛ#*Q¡³v¡³ AhI Vš`°WjœÃä€x¨ ØíÝ5ÑP盩d¡;èS¼_1¸´KÂA#*†¤ÂEdÙ*ñ®ci•ÊX™Ü’Þxz®ûÙ$±JˆT j¦~ £$ÂìɲnGg½—ùãr•ÒmªL‰\îi–¥ÀwF8¬§gdpŒ¨5 0³ ;l?J#&1b5dF7ÂvÁ•† Ÿ&wf.™žKý;çÔ¬oŸO&2úæŽîs˜b^`Za¸Tš¤5¦Ìæ1#$à‡(,YFã2î+M•fš5Îåë\#}ÛôeŽÞž{.E¾k‡¡žJ”1*I“¤ØÚ#$ Š#*HÄ” Á,1“$@‡ÂY,Š"r1€l+p;»JþŠdó ˜¨ ·‡²w}æ#&Ûè}D]ÿèÔ½èÐ#*þªû5Æ©à~Á£É“ tñ5çõâ#$ÈÈB*†…"uoE!¤Fôæ‡È8$ðð( ´ÄS#$Í@°†‘Gð‰ß÷P;»½»×wÇLÉ®ð{Îðü³‰´¾O§±èÕ3É>…Nï*`xIÉ‚OW®«ÑL#&‘6Òc^+Ï:n¼óWxí®Ñ&Ùµ’ÛT›m4@‘R…ŠJ,Ä(µQ`ø|¹œÍ…Èþ]›kRAÝÖ€Ø`@¯€ÒÛP/öþ»sûþÙ°Å`faÚ\gH(k5€VrÑ#å.ý¦c÷z¦9×KÏWs†i˜niºÍa&z°*†DȘÀRY0ÆD¦…¤ZÌ@D׈#&ë g-ã ¾4Ýx&™ïiººKżó„ñ°šÄˆ)á×’—œÃf†<¡Y²10³Ô*¿:BàmÜ{¼~·|^P)<  yù ˆ†Q•vÆ·(»m)k*tÁ;¬dBùw†&‡SÃV}k²+ˆž8Îg™rÏV^pNžƒáü,~Ê$ó3šo·R^vB£áë¤rÈü¹Kìàòò¸ åx nv³O¸CŠÃ¨@á;<Ù%¸B„û"¥µÑ#&‡ü5i¨gdÌ/³ßÖèxøÏÎdžÃás4*"›t¤>ž¬¤nf(E€¡Kf¤•‚ˆÈg· '-&"¬,",™úK%<ʾÝo~ÂŒ[³~ª o ¨BÝM˘OhÈÿÑüŒü´ÆL´0D‹[è\Xe†Í…*ˆâ]·Ë †Y Z ¸Å…å–´–´Ð0(É!,TĈˆŸ#*ÂàሊÀ¤dâÑnÀÕÁ£ò—ð:Qî<4Iá~'§ÍàôhsU—çÿUÂ󶩘µ‹[Z´Y7!ä‹çµ=àü6ëãÇ#&“káys‘ÛO¶~„ì”J™JæPšCÃس!#ï=ЩÆìàŸñpü4ëO—dæ¼(q‡‘À@¬iW6x¥¹“2Ã?@|Xxõ³ô0dˆ°ó×”W%êFþhu‰ðàtK¾Ï~À%ý,”4x Í#&½ À=³“@ÁBŠjÌ0„Vß!Œ …[ R°¶V»uš¦›iM­;v«©6&µ³Mjº­ÌËÅËË»JÞm[æË(ØR¥VËUX²‡M@œª¦®e t=Ý~©%Jõ Hoa̯,ç/#*ÃR oóذ*Oùç¡Äž×;?sS÷3ÌAÅï÷fÓ+¡Üx†ó–DGåG ÜL"w8˳k•H ÚÅ“÷ OG“s×ûrÇÚ‡Á1¬rÈpNäe6‡”+eè>~ÜÜ*bYˆ!܇‚L²Ž(<ÿ£ú†:ÁÎ#*eµãÎÜ »2ŒÚ •üPÞ7ÎÎ/‘½\™Yuô÷ƒdgºrû«©ÃpúÄ )= œûðà…Œ›±âsû6¼öJøöz\Á@4î07Xùz-Qzº7]_íýœGü7’ÒHCf¶ï,óâžYˆ_^;“Ç#&†ú¸½ÄüÓk ;ÖîæMç>“mœC9#$ôf½gúOìþßîýíû¬Ñüˆ)bsª5Áͨ‡6 ¤d踸$\²ˆxÁ>>4–©#*rŽ™kWVíhXm Šb¤C;ÚL)ý7š{S&¥MÝ9Ïãœr™¥3mUÛÀ蔣8¬k|-C´¸r .°)`Š÷ÉÐâN´ÃÈäcøO‰³‚ÄsGÍ¡ícŽÃ¿Åc%ç‚,–‡A54ŒºXHÝë•ñv†ge¸§»i[Xy•E4Uþ_N7ßè´’KÉ›AlË|HlÌ·ãjÐâòß”@ç‚Âìî!ѵ:ÉF2v>»ÃBÂ~'€A)l€ƒ&bÍÁ;@ƒ§ªÞ‰7Ë4-}V|£Œ"v‡jG¤œ8ZZ[— ázÀáwj0Ïñðq€ç¨pÝîª+Pùe,vf¹×ùCêÜòÒ/$ ¬ˆ-ÖÐÂÊ’Ô<=±®’áÐÁà ÆRYn¶’¿ß€fä¼#(„pÓøVÍZ5™’!aO”é[ðÒÙç•Æˆ\Ò9•³x&óÁ@±…þÝvè|dz¯æ²R{¤/ oضէê“ô#*ÏWZ½y³ÜÌñdLà–5 ¨:EÄ„WÔœd1¯p@ô™&ëOÛ#=h$°€ŒWW2FÀ‘#$ýJ‚¥m=È3OåãÔçí»½œ„ZŽJ#A*wN´Ù#&´‚›“#*F3%2ß´>}²Ýõ^»úì?…ª±¼¹tØ¥©ÖÒÙ¤ð#* ÅˆÈNÒB~„üŸáS$q1N™Õ~ÓÛ‚»*eùŇ'äkj—«r¢&ËBC!S*— NQ³“<–#&špKLððÚÎ>QÁÓfE‹@ Åõ?KnÍt@€TàîçfMÙ¹v1.'Ký¯'ôØõ}=àý>!{í¨j^«¤ÝÇ¡Š¢ì=ét6ùk.§/©Ò²=™˜øÕIní…âÞ¨’HIû§†ŽïÒB²%´X°d?¡ðЬðáù@è§-ìG’¯V”´teˆ ã2@r–de˜JRF’Œm®ë´¤mLf±PÑhÕ)AI¬À¹ü£ uÀ›„Þ´ÐÕm($Þ DF2 êSCƒIÔl”­‚$µh›™ƒ]ÂQÁœ7"I‚… VÛ,Fq!rÒ”d#*'µ•g^çTô‡3çúîe!Hfx ,¶"¢$I ƒ®•s|fæE›&Ú”«&Fyºë&T…(´®øùäó†K⛈(„bÃbK*áJ ÊC<ŽuÕ†0ŒAFs.$ —]åä¯Myã]i³S24 Ô­”ÔÐÌÆÆ-I!¥6›Y¶f&F¶SRõlÂÀ¨Á¶¥d¨Š¤e)RJÉÛ·¼¹¯7äM*Z†cTžÛxñŒŠÄ`Ƹj˜^¬êg dQB$d¸.·à:Äï#&ŠÈŒÄ/g3' ‡ (Àråv…0Òd°"€*ˆV½mt¢ŠF¾ŤÚù¨ð0u¯0äÂb¬\s8áp•¢1*/ A@ã1ó!Â%ÙÆC¦C€%Bƒ<0 ÔbŠÃ£¸[’bn;2Øb,‰v­@ÌÃ*["j™…¸˜Â#*C7RÆõÒˆŠHŽ[`‰ºædD7¯®æ÷Ü+*ºl)¸Uʦi»´c/t;Þƒ¬èq®áŒ¹˜a›Ñ»š.ýsYËM¶~–WîÊKz #*0òéôl•ë ±rôò]§À(_&g:Èš_Zzµw=A>6ÔÍ)¥N¯(i~°ö³ØFÇÄÙ6×Neêõ:†i™}aY;@9•Œ†iÐ7Y:M^žd¢I‘†Û­Z’áV å©{Œi–Ì­b‡ ~gy¥Ò¼ÍLä¾™Y½¨/Q]„6šûgª*‡ôñ5r-·¬„Ý[Úáµ›«¨·„4ªîzpøýºáŽ\éÐ5”uWÅð†8m1ò“-5è)—†³ð,£¬62˜É2,Äë»Éày‡}fÒì—«×6Ó„ˆzß–pèêiÉÌÒÌ•¹–aŸ ËF,ÐÈÁ‹bðÃÜG¬ùû<ŸI­±Œ3Šrl þý²±°“Ú“ˆzq"ƒË#rLÍ2¦nL\Ê‹ò"Þ¨~ߎö‡!/!Á†È "0Ì0…#*RQ„:L€²#*“ 0i#$B`iTpG#*a „IF2HaAž!°¢+„,çûˆŽ›Î}G±êóÕÄ#&½yß—DÒéD¹ºUOÍH‹ýJÒâêZ¨#*©ÙØ^o¨(üÜhÆVéúÿtëzNý/ÙåbÉ¿ÎÕKÍ™¿[ÁÓœ6ZïÕbºríˆ5ãÌärÎÚü›ŽÓoc÷zæó.çÍò–\°Ó1 #*^ (9ÓðHIÂHZ‚–¹!¤Ú¢°•!¢Úóoño«çõû—øÿ í™qB˜"9yt°aûø(aiØ#&Ài"ÁB -efmñ/3 o-ò£“Ú#$ d|â›ZJehß7Çzêh¶ÇÙ5¾µkpµdýn® ´i^³¨‹iÝÙ×mwh¥­¹Š¶ÑImÍr泫´ÝµijÊõÛ³Y¨£Wm6í×ìy¿1|šÚmÊÎDbL³ùwÉ'‚R¨a2 n!”ºA$€ #& !:®ì#*6’H‚(CB2-¨XCa×Ë[T¡LPn+²b(î`@L˧\g©¼d5-ˆM'ÐùHú]©ïO‡¾‰íù0Q?O =/Þr?Êþ± š/I0„ˆù⥀às!ÒX;*™øã«:¾R^¦ºš·øeÊÕÝ·Œ¥ÙP%ÙlÊ'Rñpi#ݵÞÜÛÓ¥æ)àQ#*1"N<§ô\Þ<¼;^“^žÊh‡C&+'MŸ•oé/cŒSºÂ,ykd9'ØWIv6_{Æñ£ÂhSK͆\Â/"|HìÙß%µ(x=èxˆL µøo8tÏ_3 q§Ñé1#Ë8Ã5>ÃM=iÄÂkQ33Ãö>“¨°UÿŠO¥ŒFEëÑ r\áÀ¿€¬'¤Ð–®»!Â!4§Hm±hþWÐ?guøÕûnR›£$Œ–àá±S8V•RF@îCs¡;(£Á˜…×Úh›àŸÂ+#$²H€–ß#*}µªé©1I¯ö2¿Ù›{úìv@W8(ÜšDFÄ&%ŠÂ#*Œ‚„ŠÁE¶úíŒ+geêåi÷+À7ãg*.3åH#$†"¯ÆÍþ’¢n!üSs÷;²ˆ I$RÁåQ‘‘Œ¢(Z-ˆ »æ Ø!Á(ÂQ'7¬ŠN -³¡Ãö½ÿ#$`.E̸ ìŠ#*ÆŒŠÈŒ„añȈZPx—,EA+ ƒJý‰®¥Á†F2€“ГœÞ³LGx£˜¢©Sr@˜*31œÌ…ÀFÈbBÁæS'4¦HMXdC$…’d°”ÈJbL©4>d< ‰°r¼ 7>Ê’õ„Q Rà° h]Ùá<ƒélîåÀ*##À Dò£ƒ|/åñõæ5êÎÄc )H^Vzº!ÏÀüdŸcÖÐa`ªsèpÄ%X¬gÑ™3nš#&Çœ/@i„5¦L™V‡8ã?oLs–ÄõñÍU¶¾ÜÚª‹Z¾ýá0 ¸‡`E(DoߨwéŸÔ}$ tƒ¿ÐoÚæ!Ë?†ÕýïDY(bV~¤a~–OÜÄÖiDôýê¡! 6_·$2袪Bª'©Þˆ;ý/sï—¥9>Ù#ÉûøìµtúÏ“³~…3g%âvû¶2tÆÊ#&ò¦ˆbCDÐÌÊ*,õç×­>/⓱ԙ>ž¹¾²“ã—hOÝeâSªK®¡ LCŒQ?¼:é‘ByƒŒ=öFÚÞgd¹ï &ØT ĉs “­62J,ÞÌ%¢y†07-±’D¤ ®²0Í(,G$Érl_ÜŠ‹ö„#$5Aؼ±¦³»Wf ‚·dT+9ˆ–‘Òð¯zĈÅ4£éž¥S§52œSÔ‹ùŸÊ |ó†f`ôñWÕ”ë›jéDj®í²ÝULŒC’PÀ¬…Ÿé¸vÒP…E¨ ‘ؘχè³Ýq܈k—”Ð@T”¢ÉÀe0˜Éí 6Ü}ú×Ù²oC™nZ(±*@ªÁ®ïØ”£/7›,‰¤‘°›*ZnÓlš,QLØÁãq4K›r«%dYjÚeŒÂÊ´¥‘­*ÙDP©‰1Á#$ZÁ"Lº¹w5™cC·yª–ÔdyÛmsr×M«&Û)b·X¦Ì²lž+ѺsWvgMrE-Ϋ¶\ë5È JEjˆÑhþlª»[€¥”@òOdÃpޏ:Ñ i½ž;„ rí³èŠX‡®€Ôhz¡x °b5@@JP±ˆ–ÄTG‡Ö¡Þ¨¦ä皇ù&ù梃‚6Ĥ Ð$R xfû#Ûß æ_,òȦ'–±£_¨RŠ…#*¿Ój~^äØuä,ª»UIŒd$cƱ½Azà‰Q‘Dª ?í)þˆH±Š†sŸ»…DÄA‘MX­Z´2ÖøSW¼×é_¦Ûè ¡ûd*X,XŒ©,­8뻩fÑc#&ݵêZñ^I>ì˜(Æ@`ŸpS™Õ¥Êwl˜ÀŸOæ,Ö©(ƒ”“±Š"ƒ­2LÐlEîµkçíºåkßÕVõ]mÑí»Ò$RkÐÔ @¥ dL˜Á)@´ƒL`BA¨.³ú÷®bd§Á+:ð…ú÷žÐÊü,R ˜ÃH$ Â1ŠÅ(†ý_Æj{«¨Ä;Rgr#$#$uÄE‚P ň„H EH$7#*ìÇÈÌSC¨€#*™ˆè«#$!+  F H"²E@ÏYæÆ18!ª¨Gã~_§ßbŒš’Ó12¶¿­Fˆ’EŒEb¹pÆÕ@f«S;)᪗X²*HÓœÕb…4÷§Àõ’M@˜QÏÙ“¦#$¢f3æÙÛ[&ž÷ AàDô¡,Þ„ ÂOÈKÞü½+òßUâƒÐZ´€Àܧo9ìPUˆ‚RÈ Õº9‰ßt8—÷æÊöåÁ…¶Z;»à—dç8Ñœ+д6Oó8@G‹0xí>Öuï÷#$bK'Û¦`©£_µ ÄhI#&HÀm“\Ks ]”,šKp¡B[RC«@PÀ±‹J¢B/hj`B##þ§Œ…`Q tã$Rs« ‚˜œ´¹#*\p#$ÆhÍMfgIF#*›®L˜†Ä’.?FŒSY*$©Ñ! I 0a’Éÿ+áÁU_Y†stÜEaAUíq'·Úð¨weUƒ€þ@¶´#I$;I+#$*Lž‡Lë`XñܶãÏð“0f“N/*² –E‚8Å#*Âà ´ÎÍ1LK,%TVµ½[k—K…¯—|*òäšD#&M¹&ytUØRoß¼6… hû¨GðM½%D!ªÁí…!àUüÎ#&ïâÆbY¦BÉp°01 ë‹™&#&´HÒÕ tn–±§«Ò¬>HAÿL4} ð$c lX‚qTêÛŠŠÊZ#$ ±TbA¢T*€ä"§mköUµî¶#Jû±LÒ»z´¥ˆ5lÁ²5ªI§L³©‰ŠÐÂйÌwÏl#&¡šx'KŒ(F)ûŠÿÛDñ›øC÷ÿjç•„åRL#»²8US›3H—ºy~*à”#ö¾øv·¯ß$Å1õ²Í«n¶„-î9Èö4=KüÓGówgLiθ0g‘‹ФC05#*ÇÎÓ--nZ)ÇQ-¡òt€‚4^SgW©ÂÎŽsa«'áo# h)·>O—i|À⇟£Çyz·ô±Í;£w¼8Hè„,I8£h«Óë-35¢”\ijËNÖt§š^¿½ëO6Í€[É~ó©\HïêAº7ö`ú;S÷²på–«nügׂœ hÞ™®›Æþ=eu yà[xVa7iyQ~ߟMc)rB““Ù4=I9zÙÌϳi¿D‡•JqLB ô}"M˜ÎÙéPŸ¼Ä<·‡î¢ Kׯ/С™Ïµø|™…õÈÏxdkM±@!߈h!”;z ëY‘#$â]B†ç¿ª~ô#ÑãÀp.÷‰k ){R2'Yv «Ä„!¥VÝþÍ/¬6Š©iž˜ªÃ\ XyÃù)ð¦Ý4·tϰ?Ÿ)FÊîn@ësáÌfÞpîÉœê˜gôÇ€©$’¦mÖ!’ð&LIv[#Ÿni;a¢©BÓSÈ„X¨&_€Ôú²Â§#*I&i²Ý÷ºˆ¾ú %2ùÁ@6e†¡RŠMî#&(Bf Ý—8ï¾€jîÁ€Ð3ˆtýý¾Ù#$¯5©ó”7÷ä`Ä‚H¿" ^#$ˆ‡¨ä‡Å ¿Ó’§ìƒw¼Š ’ H "D C·izºµOvî8*G¾ºŸ•{bd33}Цîó‹ôˆí!×™è’âñfz-Ã0)”D™KA;Õ_,ÿ‘|}0ˆÛÊÜA)ˆÓŒ‰"¶gŒ-M-R‘È}6kï …#$iªDtb eàéMç¥ ‚áÎ {†LwþñÉ[Å@BÝ8„y$¾øöÁELÏîrnáÆò~|¿€í:©ÁùYIÿ¥"NÜ™ež´ÃñÒãȉ ÎÊ5BCáoô}[+bžˆÛ·@«Ã!Èô‰ûsᅦ™z*Š „)úé³ **šU/틲ë›~Þ¾£œ^jdÈÖˆM>?Hw>öœà˜Øû÷Y™§â¢ýN_m&²ÁUTKV„K[é2ŸÎO6¤|¥Œ_¸#&Û`¢²„ 3¡¡YuÀz¸Q’¤t©!g¬É™lO:^BCQOëÜáml¬åÌ­Vë/±#*rY1+Þxå›÷Ö:Û\ÓÉÌr¹WtýÕ]¶ÄV-¶k-²Í²Ò•ªj6Ù§~ý¶@Þó­RH#$Æô:.k.¶tøþ=är”Š•ùÙq$PŠôO¼ÝGþ÷áðó hÌ£*HŠ3fŠ M¢bj"““%ˆÓJ‘’‰¡I“fɰ¡#0“è»¶Ï5#*„*¤*2#&ŵYm˜üyp£€±hQ'ù)Tt¨¶¨[ä잃ìDµE¶Å SfÓ6Y2hv+U,¢¨{À¥?¤p¼=ž‰¿tÐÔW¡)ØŽºïü7ߘPDŠ‘`zg#$ò ¸#$z×–-õÞª(úb”ÇEÙE=RÛÙ¤žäýJ„@#*F#(Ð@v¤P¹mZ^ÃÎH>ê”äv×£ÈwÌòøXÚ…åID’\ŠXE“ú ÿBÈøqø*Œ%šžé8Éî•ˉhÓþMÛTŠ[dQEP¡$ä0¢#*Ûf öKL$ÉE#&ÃVc(#w Õ«T3ªhÒ2Fç>ÒBÅ7ÀC3Éß–tól(xJ3yä–¤ûúå86:/Ûòk…†Ày“6Scìï%p—…ܾÛ5×¼5¾àÌCèäXŒîÕRß Ä= jÈÜbÐO@èª/ëƒw{›Y/z‘ݦ/¤b8 ÷¶èr}ºùh•íJÜ>Õ_Ÿðˆ©#;·2w?žÓ'#å®Q8H Þ0»“¡%gÄêƒÁuLFóîÜþ> ~yù˜m¤`éM.äÛ%ëwãeë8ÔøõjëCü§Ä¦7@ÏWUßÊÌêË´P7î£ôü_kµ‚—†’I×g“¶óºÀßGX¢Í[æãXŸî"<+wm1µ€ÒÛH8Û)xø½Ê>óÔ@Œ!Ù|üÌt’#&AP¨@Bºü“¼ œô©°:È]ÝŠ†2‰Æ'r[PX,é;lTÙàPö@B ‰½ƒ¾µØl¬·XâR:óüKã‰TìÎÛ$b„Áh°8aÈÍ…†”š HÈ$FIH£dÃí‡#*ª$ wU8Óß 8…Ÿ¡¾Ž¸Ñ€l‰äC¯æÙ5œHvòÚ…à#&­)‘Ñ+njpa¸â„»‹:$„RE$q!Ò¿è´5¶¡ïHXˆ•’Q¶J¤ŸÙˆ)#&BD[E{-nÄ·.î­¿}-pÚñ¶+9¹[¦­ºm¤5¹i-®VMrå¶¹³»Ý®E²mAŠ5ErñgucUûø¶—‰”#&Çd,ZÖ#&£}Ô‹œWâÄ#$àQ‘§8ß ÌJÌ=‰¯Ú@^Êí"ØÎº’!6Å Þ#*´Þ”=¯õar"K°wøïáUÏóÍó5 ä𪀿8¼Áp£ñAsû‘ó@âšj_|ý•è!ÈO#$éðAH é˜>ߕΫ#*Œ„±‚BÕ’RÚ¶FA¥´¥Â¾T#*Z\# y Ù €¡†©" ¤"‹H²DRˆE’A 'N±o"]ºÅB“!.Þï¢í‰Eå0$HDa @U„dCúÏQòP°h":„ÿ}T=ª¡¯ëˆzû(;mУ¨V èKÅ2Pñ¢‰•åJŸEíiAÏr(’—x  fLËî;þŒA‰S·ÄÜ•“t:0WΤ”=v¡›#$öÅ#$éA2‰O­P¡Cõ„Å’IJ1idŒ´i66hmIE¨ÙJL’’m­´Eª6Ö6µZ–•¦U-™Shµ¥$Ya# Ÿ¬ßÑ‹‹m j+ŒÝ°ÂÀ¨-uTÆÙ û3M7*¡]ɸQ­kkEÔ Ú2L®Xq5•Ü„S#%¶ÉXí#&G#*[ S!‰#*TD¤ÊÄKs¶J(8!8t0µ¢•€Fˆ£(iˆ@E@ ‹]F…È$K1gÏüyØ.ª˜í¥-´D¢’Œ[FØÅ•ZAýˆXjJld†0#* ’$‘‚$˜râ[C¬#&÷#&%ÕU¹Â$gÂÁöŽ’Dì©ãÍ·ÙÛUeg¯) #*¢¤œ#$ó ²`÷„goïäÊ´YðNëì\(#*aM¶~¦ë gXhh eÅ¢0%Æ @[¸ÈBA¹£ÕÂM‚`:U~ì˜BZ¨0~}8©‹„J± z&bA²‡#*S¨À5ªGþÜ4{ÓŠx!ÕS¡IAʉ*·‚˜D €ƒ¡~UÞ«W-[mª;¨*H\[¿ c¤?‹:ÝT~é"fí¨¿WÙÄ€¨VSüÛ׋ÎA|:‡Y|¦úCÚ „¶ßQöYGåŠÊ%N]›*Q£›Ý–}Ѳ״ðh9ì Ô—Ö­üïÏT'ÜB+´’¡ÖÏå.›²ë˜ß4gNý±Èí"ß¿3[ÚC»,àsg¥âˆhÈ÷Ö]o=¯býVÆqÊŽø%aúPík„éBv4~vGÏÆâè%š&)Ó]:ØžÙ—îÊÆù¿xo ?@Y@°àÐ&¸—ÃÇE*žxª'ËÕÚ4› nŠŠŽënÌöŠ©x¤BÕzÁáÅ4¢À ³Ÿ=ûØÍ†˜¦ãÙj̪–mê_GWÛp '#&åÜíª5×Xô̪Ԯ8•.¸]03?w‚pÁÆ&Òe¥”ÌI#‰Ã—mOm=²ëmŸ˜öû#&溻xNíš[¢pñÍ Íž%PDËP„j­¯¤GhNjg¯£Üµ®†¡'.x¹¬$Ý Z¶h dsÍ^¸;:½Zï"óù¢æBy$øQü»«„ôÅ0S#*EO QR¡ÔÁ #¤Y.k¥6A\—•Óþ#&¢ÐE¥,z>#*oU'aE-²¢“öiYKP>×´µŽåBÏöu#&ZË[‡_d§õN.W»Þ®Ý’àtáÑp{êSk±ÖbQ8Z¸——ÊA†1qÓu>yc·:Ò¾ÞÛÃs.a8ã¨CˆÏAî%·•D%›êw’¥ám86‘ïÑÞïÌ“C¹Ð¬^• ½ÍËì˜Óg¬T#ÀrýÝ:ôéœné!šŠv#&´ÅFõÎô[è{q¾N@ß@w¡Á¼ÌŒ †b´¶ïÆxÛJto®¯*¨ºÞžöÆz!òüq³Ñ¹¹,3\^ïÁÍL#½Ë¾¿­˜…ææ‚L½˜vcÖ³µ}îãmmŒ°HÚdh@²ÔæŽM#&Zã¯P˜Ë+›ÐM”al3Øß‹ß}\ŽT)D¥ÊŠã*eNâTfɱëvoànúêóÇAx`¡µ^³‘´ò#¨æÉ°Ý oQa7:yS³ß’9ð$Äfðaç48Ç‚óÀ:¯®M%F)< ñO^±ÈZ¼”ÉOCÁ\vE0²v1ÞXïLì¢#$·/ ÑŒf9`3i…(ßÏÏP `A÷#*ØC'f ‡¹ÓÁÒ_ª¤òü§X Âñ^pé¹sxRðKc^üoÒc}¾ýaðcÕi=Õ#*i¤¬ùÓÁ°M‚Æâ‚€êÌ,h)„D#„Tµ#—Åo¢‘ÀW-õ׳¯,·ib‘ÝЈë,Ý 4+’9ð1‘»Œ©Ù¢Wq±M˜°Í`(@ˆ<åîg®8oÑ2²Œ­.‹s3#*­âm¹‡7”6NP£óªµôTÙ¤P¨ed#*#´=þBæï'–Œ¬* i·j°Œi¨cA< ú /tð?JLœ"}æ9ò¥L᳟np]¡[eÒv5Λ\ê6—KʼgÒx·“;G3ÕÕ?<ÌtT´¤ö<¸!÷ëÔPŠ*¦Òˆªššr7ãAEŠŸ¶ÜâiíˆÛ>ÓÚ=˲8‡µ®½+j 7†æN1ê6›öwàT )Î:>´—{iè†v›•}:m!*k ™‰ha"ŒKû#&œˆQ­£{ä ç¸YÉ|ÍToMZµÎ%7[û¨}t<9)­h´í£#I¢9ñæRØŧñ}¥V-_U=ŠžwâX” Ó`Ö•fµLŽo‡Þ\Ź'LÑFØaÃîü¯fÀd¾, õYª™™25†T ñ‚¨†–ñÌY°¼Ñ†uòSUÀè²G·”áP;ƒç#*…ˆ°¹" 1PКÁ]°/3rÌ†Š‹wG›Ñ“Á»ð4=tn>,Մ邸59ò À°ë˜…«£³ü"k#&AO-Gj&´8æì^Ò:o.XC;–mrçS‘w¸(aK›2âáðvÙ˜5Ò…Ípün#$¿zX,Y„c²tŠ6d¾ãظZ¹aa§~”}Ìkóö9Ô><‹?UU©ãÉ÷…ûÀl¦¤ SrdÖâƒS°áÚLèRIšǨ„oawS91!$Èá$Ùw°9&«±`†Ø$ð#&U’&pöü…÷ªë®úNý¡‡K*;”1¶m#&Ÿ_¦=#ïûˆ[ïjÖ±ó³Rñþšà‹ºÚ1ÓÝÙ1î;l›ãø)G %½†>÷ VÇ"u à€“Éø/œÛëð†hâ¶þòc°Æ1fý®ý¤Ô†"^(wy‡šöÁÅ ùÎÄ,@RªI*«ôfâ9èWNlS"#$Q…ý#&ýiÅ艸7š‡os^™ZSH²@~öªdY÷(”$%`[ü×O³:Þu ¥ƒÁ^r6`*Jœ¸)™d ð5˜´T#*Âí ‘Ýa5„  m#*’#*bR‘³AUHX¼KA#*Š9ˆ¢¬hÕbµ½+F«rµ¹k+¥NiOXnm†¡Í&¬fƒ,Ù‚`Ld³aÈ`˜d”*Œ1ãÍæzÎ/Þgˆ[[Ìä#$dÀ~íûˆâEɘÑ2|d‚¢,gÂwTükj†·¼mÈäØÖ›jµøšÑ­i@ VI"$šÊ€S‰˜þKjmýÍ8oûðÔÕZ>·&¾‘R¥:#&êt«œ|p)´e:,Ã!ƒ=:"ެ2s¢äñp¬,¨i¸3zÊ™Òç!¯Éïy’TDãÑÊN7þ£àt¾½%îgŸ#$ûùÄ0,:Œ+‘™–Ö0ÆÚí¹ÜÁ{¸ç— /&ñE<Ý;Ms ~>æõÀ¨¯ÑóãhÌ`•XÖÛ[žR˜%(ccˆã!®0ÔNœ/8ž#&—çÂîøÌÉaw«º8{ÐܳéÜ<ûìÁwG‡[È8bzÎ*«‰#*7…hˆþþð]ѶLëP¦ei Z RJõl᥮.\4–x÷¡Æ¼Ë¨ÍÁ”ÖîdàÞ…%íâ»4Ì»†÷Lè´ÎeM÷ŠÉÃ4Hc3DËJŽV'ÁÍ-JxúɽG¬9Lr%ˆÐéÖNÒlPÞ킼œXæoIÎtã8BÃIæL#&†‰Î^ù)ÎôÄ@é*rD#*r‡BVVZ/®nÁ›—ÀgA<öiÞø˜ÞrÆ-†jíóU òª©³!Þx¹z}³)TÑ“@ÈL$9Ü‚!ØÉ$¶øï[¢ùà2ú4þb}Ð#Â+¢x¨|F T“šRÒT#*‘ˆ{WO‘²<ÊÕ'äb Ø…<äéV#¡,ÑŠÆW0*EUDž´Ñø•Wô°¯Ä뺟9m$^ŪbøOÂî $÷½aX÷ŠsŸ$Q#&/ß'd$wV/KP$6>î¾ð~z¸ŽýÉFùܘd+”ÂrœM†_߹ØŒ)ù¶yu”óÁ¢\IymnaelPAC2¼@¼§„ˆq’0 tìvÒM`bM‘€g>ä˜Å£:L-”TFưæPÈÈÆS]›{Jò“x¹¢Vej’¹úhxfò’´3†hé¨P1lösºnP†0”Á¤#*D©fY#*Â}'AÕF¼²ÉæxÐfJ‡”¼ÙddI-Nä ĉd ñÙ ‚l*² '¦ÅAÅRDgád;9ª?åÐðAÕz!¿ÎZ’ä¶85³O©N¬â'„„&!èHCsuT!èåµgL2ÑEII“ (NBdBrt‡A:3õEX*«8¡É€ˆynå¶LÅT4Ñ0)Jß›tKO¡a"+^=”ñ~ÜlÇ«#°…`>l‘ïëÌ{ gü¢zJ3¶ô=¤HFBÿ§úà˜t…„*Ù>~h%¡Ø> éB„¶“Ǿ˜Fx<0d?2©%Z™é[Z™§xgŸ÷€LÝ»YZX»A¶”Z?š¤aÙó¶«à½cÙϪø’#*téà©íhgÖÍ/¸óM„JS»‡ªÍ0?LO#*óƦxNÊš Žñ”!@D#$£È*Cmá»b#&% (_òdÄ&‰I?ïØ†j2ìÈ/&YÛr-Þ¨½@X#*dl2³ˆF¥"DÙ#&¤‘²Ö²ñl&4‡$²Î9Ž`#wa¨Fl¼Í ™˜2™eé#$à.q#$Ë;Qž7ÔŒÍK7 Ú”5‹™j‘JªW*…a,Rì῱Àìã@Ýcb A•i>:Aá i¢€F{(BÌRB@ÒšXD#&E‡iö˜n(+"'-ègŸY¢Ú˜…0:Ö8¶³6',3@ÌØ—-4¤ÒÒ£Ë;Y¨ŒT¼R$4€…u^›Ë³!0„!“4FÙÞûÖø1ë$®q’#*73ÂSÍ›¼åH†!!ƒQ³3ê F÷^3ë¾³Ç p~wÖ ‘|dg雇i0`)½Á„S!2,3“x ©Ô 3ÒEDÊä˜x]–vÅþù„åm·-TÌ®˜Ü™LÎ.À&XpIykn“,“çÜ7ä–BmÁv®‰PnÍlJVT ``5Ûk.Û’‚9eDJ%)PH£#$>^Ç.(`$XL…†ÒzŒ‚"$;óBN$ê|{':s†àœŽæ©&ž*R—QQÔh¹•usGnñ1ìân»«®Ž_9á{näìŒc©J-¦XÌ逩7h±ÆˆÃƒ„8þªU «xÙðÃÄ’"÷‚m”je3šåÍ `䶜Ʉí#áBÁHwÒ$¶!šÙHC8–{¤Ž*0ìj3I˜„¤ÀnÛm´°S#Ô‘BU΢ŒFe¥%oÚYTh̹c¬ùmÃdɽ>ñ¡9²ä˜ŽY˜pâS^ÒM)ï— €­WMˆHÙé;5”í¶Ç®¹6ÓNù)ØlæÏNðÞôè±–mëj÷«.¶|BÛi‚ÓËYs(tR0å\ºoîjH¶BM}Ýñbg…nÁ ÍÇ…\v‰ë¢¶·7ÄÓCîñ‘,ôD2FÊ0ëC¸¨ÄÎM ö6åîé¾.eᡦoÆ_š>oŸHF­zD©›ÞZ…Šæ]8š\îÄá®›º}qʼ\ɸšDô;µ+\tH`Za#&YxB˕ʬÕmv[€ìˆq›YÅ;ƒ»“/ Ð'ip4äE{óUä[ò-my‚³cD¾)ånÎÄÀWGìî\{×\劺šé4,V"…Ù‰d̓â²46æyàtÖ¯.˜T¸¹là㘠u‡s"¾´[Ô7N7<@¡CÛÍ=Œ9ï˜l-*ŸÁí25[®fà´ŽèjL#Þ“Z‘´2ʃ<1C®ûñ#$9âx`+ID,`ÉÚöμ o:È&eiø$Øb²h®’ 8ñ¾u5f³½lð4ÜrZÑ\0JdÓš ½îåÙÁã߿޸ã eÍ?5Ç[¢ºÙÞPìÓRÃCL?j…¢é’bíq-°B\^˜(‡KFÔpÖçAu3i"OÇöý°Œ½¶\˃¦È¥Èk^Xši?<œt­¼²a$Á ncŸU¹Qªs S>s:ŽºyScPC¹Yöj鵦%=áÛ(™vΞM—_O†Ò³•ñbýâví»{3ÓÛå‡#+g ÛFóe‰‘ђ΢¡1AïÓoamo·”šÂqÙ#$èV6…Î*=ú¸“; gHPVO4˜å8aØœ%.ƒ…ù<ù÷'MñdòHÛ m–Ï~39!š-îe»;uV­˜HœPˆK¥(f…¤cn8Ó,àœÍLä¡â `ÂQ„¨k~¾Ývc°š-9‚‡hn9X˜›jšh,±ËzbÛáS UÌîu[#8§b5*"až*wdØ”O>LÈéÉËWDW/HXvåÀ}C8L™ûGn¬ Æó-©gaÄ×;„Ì¡œÊ—NÏtå¼bûíÊkaßÀÞ8Õ§(;µLˆnòÛ.Õ˜/–uÀäD@–¾b=¡´S¢wd>3IñÁž(¤ƒ 9£g"éZÀ,™PjcUK°a…ZÓ‰m”VYK!÷¡»kD_T)­H"»j‡iÜý@ý^\9ì÷”+õgIá0ÌVpÀæ˜(±ãˆC·WcvÀtDFC£ODÐôY0`J"A'DÌ‹â6´Nþ§! ‡RPg#$b¨É܇SÑÐ{ò•^ıž5Ši—6ˆvNæ:Y,=!Û98ÅTJiè« \žƒB H ¤,¡T²DûS"dÍp‚{BOj& – Ÿv 7€Ð,ˆ±°:A‘¡ƒGFìÈÅè°0ˆy#&’{{ÌJ–•™LÂÌžO)àžÁA!i=‘‡G£÷”µ#*Àõ<Êìða`1«„‹eu¤¦ ©|ìHºÐ/C·Aƒ9ÆÜ'N°HÈ Ž€s6ÁìÄ¥„ò{…‹ÚrX``„ÔLd,Í×$$F!Ðzû7>Eûå¤f¶ƒ)K7ñéõð„<ÃßÁúþÕýÃ`Ì1éÄŠ¡°èDXD„€š€T**!ЂËÃ÷ž›m…Pó;ŽŸebÞº+˜wÂÞL«à²ý:µW¸ÖòZ«DgâLǹ-!$îe—°Þ[[<#&¤Å´s%TÜc`S”u`ô‘#*æFf‚mų¥†I qÛU¹rHä2#gÉ,×ãáØŠ»3ÙW%U>V¢/áÛ åÜ¢…yÒ+ ¾MÒC5{k x((¤f’„ßpp¤ÛÇK@Ÿ-;!–#&„Ù(’ôS¤¦8&­šbz÷ÈAg°œŠ­³ùa„oÙ|ø:áá3ÙÝ“!åøÉ¼Z±˜jLtÒèþÆõ~DCЈŸÃC–ÙÆPœ2Dv¨hMpÌFDî’}±%§£â®ß¬>ñy¾Š,‹Ìγ„sësˆ}Ð ?pºÿ¹žÀHªyÑ–ýïÒ|”é8åºDʳ9_£\{PôŠ©`gø¢Ë!·'.GÓ¦KÄ"‰è €xEžZ6Ù"ýS)#emÀ"~ÓÜžîÎYs»ÖhÉq†‰#*²i #&1jZ ˆ¬DD˜Œå¢L–ÁYBák"4¢ªR—m¸Q+šhHL€1€T‰³HF ´8iYF‡ÝIš¬âÅ{Y&Ù%”C®–æ*¢k¸ÉÂ*£ ƒ¢—\#&Ffhê«3D‚#&'§w|ð~ß0‡Æò:ë³x´‡ÉAì‚'4è c[3ñ ZŠ…Fö¶„rq#…eè£ ]stæîØSxL`ÃLè¶$á¦:}á͈̈́‡rŽKEtïƒi@xò ©N'#&Ì‚›XëGƒ-:¾Þ@é<¬oä™J8Éeªœi½Š®¼)o„¸}…Ì)”õr+Aï P½ÅžÆ&>©ì¾òv{o]‘ôùäÈæ>YJeV¨¥˜…Bóîq-Ÿ(Ó…Óc¢b'@s ¦!®•#$!éä4ô€¦:uìÁÞ»B@^¼+šÎº8i¬{î6ô`Ág(™çéÇ’fôZG<澟Ž#&$Œ‹#AõBq›²ñ߀ϕ1•oAëã#*aOtdˆžù­ºµQk6Õ¥!+S}=Il›k%¨Û[Úmd¶õ•£¤¤!D˜Ô!¸s$’ ,Én0‰`„MB/L#$uª„óËèÊŠB•!tËûð µPM#&¾YVïV4¹¼Û‘H‘aE‘D„Ö÷õy|_0ì}ÊjªÅ¿8Íô##*¬€Œˆ©  jìøå·o¿LTïáFþ]ôŽ5ëé#&‚èÑxÍú€<‡rí:’·³.Ÿ¥ÌQ‹áÃRŠ(×#$@ÁsFJ‹à—,‘CæäÆI `æBØ•[‘´I+[1ªSn`IPMŒP#RÀŠdd¨,³H#*È äiÃ[¨e™d¥Gf2S1z–„6ÜȳÌÒÖŸ‚vp7‘ãÁéK IбçÏò{©™†A™Œ@[í‘Ù„BzQ—²„?Û:PÅo¸ÌMÑÓÔ=y¶MñØ@笢®H(.éÃ]‡""ð1Èç䜃æ’mY>ôÁ¸œ‡$"ȣ݅BPH£2BŒëÚÌ`?zR=†ìUZÓaˆàÅ¡*r!E$ù>-4|É1ãkh.ÉOƒ1nW«âC ¢Zñ•o4UÓ;B)$  P-#*ƶ*5R¼EuÞˆJL°Xéü7\BÂ]æä…c[F^9ÙÄuˆu†™Pv?h?d$Aaú ¢s,0€€’Ë(’UwðDõèwhš.üeŽ~ÄùŸAد»j¶#$OPrî¹y$H‹[E3i5$©e¦1´JPUµ‹Y†Ð›(©lŶ¤ÛõÚ+WÍt4#)CµÜnˆléãXªã‘˜»u#aà¢N½“‡œÕÍ4@sÔ‰$œ‰Ž±…5°|NÜq³!´€Ø:Ò#*M ) ƒRÛ9F!¯)è•ÀˆA  Cë§Í#!¾°†L[üG¬‰x¸(Îh.yÌd$‹Üx¢xf8󢫾ß}ï`±Jˆ2éYüÃýÙDî9“ðI¢o™ ž40HÈ0"Éù@à›zl÷\*^mcÚ©©BåÈ‘0Št§¹Z€¡ ¼„ZPS5Õ°TD%SD‚ÛRiF©¦Ê÷ݼ¦-l`նͤÂ"þHj¡X¥Âò¥=JU‘aîµ™eðÔË·g?u¾>«æ1Rצ}ÙNŸØiÍ77“Bõb;' ´?Í™}'.`â*È7ø`^àxŸÐŒ Ô˜q³Ìù¶¬ '#$?ÁG±\·°9æ^»FÆ?$q͈ù’`ËìcA!Ü #&÷°|¿À¯õ*…z½ ;êF˜@YHÊ“dTRØÅF#&DmRm°¥£}·é5x–+H¬"F’,„ ÙÐßוŽMŒ”e˜(X‹ý’ °â{¡#…HÒ(ÄÉ…”SDÐÛ¸£`YT)#&Ú<^ùøãžåÙ®ô;nô*ES÷$PkʱmF5 µ(U¬Ùb-cmQšZÊߥn¾»æ¿¼M}Ñé­¥I$l5?œÏÎÿ¦?E#$Ñük,GÛbƒ‚˜j­•Ë0p¦¡¹ çôRÌrúÏlÆ2\Ìÿ§ÂM®/I»`²$Ó).DˆžüÌ`±ÓþžEB%‚… Q˜ÒyIHÁM©oY°®Úwe®îÊñ¬˜ 2#*Da5¢“%¤c»3*ºæí¨«¨$¶E›W—v4Ñ6»º»ºÚM•% EdXL´&A†P³)l“Ì2ii£C #*+*Lf ÅEzo^\EªSKe5&YW¤B„Ê̤‚ c$ŠZó»–íwn‹,elºk¤Š²¼0òœÌ@6Ó+#$óÝ Œ!Ô4'†Oí÷[yEèZ,Y<×ä¼ùºö¯ ‚—µ³=PZ¦+÷p°ß§:œLiÊUŸÔ“Œb#*¡#&¢I¼cÃ6P;1¿G„Ðá“s¦-.`¹=ˆºÿ[—ñÜP¸yBK°‡"=žÎ„òÂvÆZ‰#$­ºz^äƒÐ½¦™ºâŠT¦Ø!ÕÆÇ‹yn%Ä…»µãžOæDèh-Œ“ žsá|§«:ÑNç]u¡ÄX–{Œ7œp4"|tï`Ed$%ës ³ØÍ]<÷ò&ù‹Ëþkàa Ùñƒ \€š’hãß™ªÈ‹»ñ,?½”’þ¢:3CÞ+õ‘­_C§ÎJ˺éRzk{üQlIÀ$§#$?#*(k#*¶!ÞÙûÿ8wwéo3³ãç„óŠ¤Ñ´R£¾lªÏ‘¨g#* ;ô9”šß°Ùñ%pÁ'L}Ó/޲E~4># äey¼<Ö B‘=ÑúOšQ8³yÁMÊ#&r5 µi:’[E!cqC1§&á©3<Ó¬M`£ÐÂ!¯f¦`¢ùÃ#ŠØÇo‚Úšös«T,„$H@[A¨"ÐD¢4P´Z°¬Ò¸ÿË_`côcþv×gÑÑÈ#±:»ºu`—w8ÚÿwÅÌ6°Çl3¦KD”l53èhè@åÕÁæt›­ÿM (Vƒ#$7¯Í±Ý™ËÌ7¾v“"P<µpþÖL˜¢"³ŸK|çÍ_¾âãðqAȬ£'ú¨³\¥žàÉ3’¶|J­\'På2y÷ïf~œ €ÔZ3¡½ÀìbÄ«èôoî3ÝÃÌï°ó#*VÖãPÅŽÑ nËC!½,ŠJeˆ®G!!!Òw¥N|¯˜†e¨<4°è#$†ÇÜd玬ѣø1|J¯k ´BMì4Oò9œ™È"˜=!g3´õúhd@Üæe­À­ÇÛ#&ÈÏë~ÇÇôûí§˜i³dO­ÀÈDIõÄBЂƒùÄÏãU#&2 ·¶•AÊ6i#&è„zf|Ê„(­¶óñ†#*#$J´«ž'Œ<ñ—‚¨}¹e×a™²úó5rb¡4ý¶F(‡åößÿý=Ÿîÿ‡Ïû?îÿ¹úÿßÿ—û›ýÿíð—þþ¿Ëÿ/ùÇöø5}^ߣýš>ý“Ýý}Ÿï¿ÿ“ýþ/üqåÿúóòÿÃÿ·ý_ðÿ—ÇÿòÿÓþ;°Oüåÿ/7Ÿåܽ_OÙõÃêT}jŸÛ±Ö.gãÿqüâ™"å;Âp ƒ2®ÓýR"›ˆT¤Eâ\õ?Ð)ˆÑ‘p¿•g‹Þ·úÍWàX±¯{Ô[¼ýÅþö¹ÝÔI I™Ÿo…²­öÐ @îá ÷m-Ò}iIìÅ…9ÞóB‹0¦³q¶X?¢Îí÷°8VXT:Çý{·lL‡M…ΈP¶éxÛpAêþ#$äú¢ÿ´I”r#¯a\Õ#¸$«mµ‹Æûf`]€è¡G#*•qÿ:‚Jå6tçCý/ñàÓþþœ^º»g0¥âЩaœóÃÏ#&ÎÑ'MX/y$5#$û0 2ξ2ÿïÕËrž#&‡x˜ß/ýÕºÊ0,LBˆÝpÅÄ©îb›¿ëï:“@ë©r2ÛýiYöõ‰ÄQeàZÖ ¦aAÀ÷øI†ˆ†ðèÃ…,»“|ÁyÓ¯_.Mýó;ìq²Ôöf=zèë4â˜Ìc2Ò`ø¦kMŽ[²å†ÊÅLÅ„ƒ¼I"ÉŒZáç¶/!–f`Ë#$häƒïÇB#&ù™Ÿv˜xe:ùÕ½kÄ ß]ÛÅuiÒ64k…³)Ü#*fá†;9i‚¯¦S…ßüÕˆžç@Ðg\^$jäûÏo/âÃÇż¶mú¤É¤ø¢‚‘öGHX‡Ð7ïÒçTÂÓ²m4˜Üuñeúý«#&‹¨A<Äfa’Q\nSãÓ· ´ɨ™¿‡aƒ>s–¨*ùmP©¿³ã¢™]Û ÀÀfP‘M›òáµÌM"­ AîA¡Þu‰EùÙ°¡‰ #$H‘‚„HeÚ2àî(wf¹QV€Â S{D?ªbX™œ1ý¥ºådÀO”,¦\lí× ,F#$"aCŽª6±MKuì­‚v0j5 BB2jÄÔJƒlU¦“6¨¶5“hÌÒDÅ…!# #$‰°¥#*@<èDå#$C¿Gàíÿ<@ÿ{$ߺ×)qøÁ-qÞЗjRV#$‡§?‚¨nðäžþ$H„On-ÞÎ= 4.0 are ' + 'required.\nTry --no-lsb option if not interested in ' + 'building LSB binary.') + + # lsbcc as CC compiler + ctx.env.append_value('CFLAGS', '--lsb-cc=%s' % ctx.env.CC[0]) + ctx.env.append_value('LINKFLAGS', '--lsb-cc=%s' % ctx.env.CC[0]) + ctx.env.CC = ctx.env.LSBCC + ctx.env.LINK_CC = ctx.env.LSBCC + ## check LSBCC flags + # --lsb-besteffort - binary will work on platforms without LSB stuff + # --lsb-besteffort - available in LSB build tools >= 4.0 + ctx.check_cc(cflags='--lsb-besteffort', + msg='Checking for LSB build tools >= 4.0', + errmsg='LSB >= 4.0 is required', mandatory=True) + ctx.env.append_value('CFLAGS', '--lsb-besteffort') + ctx.env.append_value('LINKFLAGS', '--lsb-besteffort') + # binary compatibility with a specific LSB version + # LSB 4.0 can generate binaries compatible with 3.0, 3.1, 3.2, 4.0 + # however because of using function 'mkdtemp', loader requires + # using target version 4.0 + lsb_target_flag = '--lsb-target-version=%s' % ctx.options.lsb_version + ctx.env.append_value('CFLAGS', lsb_target_flag) + ctx.env.append_value('LINKFLAGS', lsb_target_flag) + + +def check_sizeof_pointer(ctx): + + def check(type, expected): + # test code taken from autoconf resp. Scons: this is a pretty clever + # hack to find that a type is of a given size using only compilation. + # This speeds things up quite a bit compared to straightforward code + # actually running the code. + # Plus: This works cross :-) + fragment = ''' + int main() { + static int test_array[1 - 2 * !(sizeof(%s) == %d)]; + test_array[0] = 0; + return 0; + }''' % (type, expected) + return ctx.check_cc(fragment=fragment, execute=False, mandatory=False) + + ctx.start_msg("Checking size of pointer") + for size in (4, 8): + if check("void *", size): + break + else: + ctx.end_msg(False) + ctx.fatal("Couldn't determine pointer size, only 32 or 64 bit are supported. Please use `--target-arch' to set the pointer size.") + ctx.end_msg(size) + return size + + +@conf +def detect_arch(ctx): + """ + Handle options --target-arch or use the same + architecture as the compiler. + """ + try: + system = DESTOS_TO_SYSTEM[ctx.env.DEST_OS] + except KeyError: + ctx.fatal('Unrecognized target system: %s' % ctx.env.DEST_OS) + + # Get arch values either from CLI or detect it. + if ctx.options.target_arch: + arch = ctx.options.target_arch + ctx.msg('Platform', '%s-%s manually chosen' % (system, arch)) + ctx.env.ARCH_FLAGS_REQUIRED = True + else: + # PyInstaller uses the result of platform.architecture() to determine + # the bits and this is testing the pointer size (via module struct). + # We do the same here. + arch = "%sbit" % (8 * check_sizeof_pointer(ctx)) + ctx.msg('Platform', '%s-%s detected based on compiler' % (system, arch)) + ctx.env.ARCH_FLAGS_REQUIRED = False + if not arch in ('32bit','64bit'): + ctx.fatal('Unrecognized target architecture: %s' % arch) + + # Pass return values as environment variables. + ctx.env.PYI_ARCH = arch # '32bit' or '64bit' + ctx.env.PYI_SYSTEM = system + + +@conf +def set_arch_flags(ctx): + """ + Set properly architecture flag (32 or 64 bit) cflags for compiler + and CPU target for compiler. + """ + def check_arch_cflag(cflag32, cflag64): + cflag = cflag32 if ctx.env.PYI_ARCH == '32bit' else cflag64 + if ctx.check_cc(cflags=cflag, + features='c', # only compile, don't link + mandatory=ctx.env.ARCH_FLAGS_REQUIRED): + ctx.env.append_value('CFLAGS', cflag) + if ctx.check_cc(linkflags=cflag, + mandatory=ctx.env.ARCH_FLAGS_REQUIRED): + ctx.env.append_value('LINKFLAGS', cflag) + + if ctx.env.DEST_OS == 'win32' and ctx.env.CC_NAME == 'msvc': + # Set msvc linkflags based on architecture. + if ctx.env.PYI_ARCH == '32bit': + ctx.env['MSVC_TARGETS'] = ['x86'] + ctx.env.append_value('LINKFLAGS', '/MACHINE:X86') + # Set LARGE_ADDRESS_AWARE_FLAG to True. + # On Windows this allows 32bit apps to use 4GB of memory and + ctx.env.append_value('LINKFLAGS', '/LARGEADDRESSAWARE') + elif ctx.env.PYI_ARCH == '64bit': + ctx.env['MSVC_TARGETS'] = ['x64'] + ctx.env.append_value('LINKFLAGS', '/MACHINE:X64') + + # Enable 64bit porting warnings and other warnings too. + ctx.env.append_value('CFLAGS', '/W3') + # Disable warnings about deprecated POSIX function names + ctx.env.append_value('CFLAGS', '/D_CRT_NONSTDC_NO_WARNINGS') + # Disable warnings about unsafe CRT functions + ctx.env.append_value('CFLAGS', '/D_CRT_SECURE_NO_WARNINGS') + # We use SEH exceptions in winmain.c; make sure they are activated. + ctx.env.append_value('CFLAGS', '/EHa') + # Set the PE checksum on resulting binary + ctx.env.append_value('LINKFLAGS', '/RELEASE') + + # Ensure proper architecture flags on Mac OS X. + elif ctx.env.DEST_OS == 'darwin': + # Default compiler on Mac OS X is Clang. + # Clang does not have flags '-m32' and '-m64'. + if ctx.env.PYI_ARCH == '32bit': + mac_arch = ['-arch', 'i386'] + else: + mac_arch = ['-arch', 'x86_64'] + ctx.env.append_value('CFLAGS', mac_arch) + ctx.env.append_value('CXXFLAGS', mac_arch) + ctx.env.append_value('LINKFLAGS', mac_arch) + + # AIX specific flags + elif ctx.env.DEST_OS == 'aix': + if ctx.env.CC_NAME == 'gcc': + check_arch_cflag('-maix32', '-maix64') + else: + # We are using AIX/xlc compiler + check_arch_cflag('-q32', '-q64') + + elif ctx.env.DEST_OS == 'sunos': + if ctx.env.CC_NAME == 'gcc': + check_arch_cflag('-m32', '-m64') + else: + # We use SUNWpro C compiler + check_arch_cflag('-xarch=generic', '-xarch=v9') + + elif ctx.env.DEST_OS == 'hpux': + if ctx.env.CC_NAME == 'gcc': + check_arch_cflag('-milp32', '-mlp64') + else: + # We use xlc compiler + pass + + # Other compiler - not msvc. + else: + if machine() == 'sw_64': + # The gcc has no '-m64' option under sw64 machine, but the + # __x86_64__ macro needs to be defined + ctx.env.append_value('CCDEFINES', '__x86_64__') + # This ensures proper compilation with 64bit gcc and 32bit Python + # or vice versa or with manually choosen --target-arch. + # Option -m32/-m64 has to be passed to cflags and linkflages. + else: + check_arch_cflag('-m32', '-m64') + if ctx.env.PYI_ARCH == '32bit' and ctx.env.DEST_OS == 'win32': + # Set LARGE_ADDRESS_AWARE_FLAG to True. + # On Windows this allows 32bit apps to use 4GB of memory and + # not only 2GB. + # TODO verify if this option being as default might cause any side + # effects. + ctx.env.append_value('LINKFLAGS', '-Wl,--large-address-aware') + + # We need to pass architecture switch to the 'windres' tool. + if ctx.env.DEST_OS == 'win32' and ctx.env.CC_NAME != 'msvc': + if ctx.env.PYI_ARCH == '32bit': + ctx.env.WINRCFLAGS = ['--target=pe-i386'] + else: + ctx.env.WINRCFLAGS = ['--target=pe-x86-64'] + # Since WINRC config changed above, must set other options as well + ctx.env.WINRC_TGT_F = '-o' + ctx.env.WINRC_SRC_F = '-i' + + +def configure(ctx): + ctx.msg('Python Version', sys.version.replace(os.linesep, '')) + # For MSVC the target arch must already been set when the compiler is + # searched. + if ctx.options.target_arch == '32bit': + ctx.env['MSVC_TARGETS'] = ['x86'] + elif ctx.options.target_arch == '64bit': + ctx.env['MSVC_TARGETS'] = ['x64'] + + ### C compiler + + # Allow to use Clang if preferred. + if ctx.options.clang: + ctx.load('clang') + # Allow to use gcc if preferred. + elif ctx.options.gcc: + ctx.load('gcc') + else: + ctx.load('compiler_c') # Any available C compiler. + + # LSB compatible bootloader only for Linux and without cli option --no-lsb. + if ctx.env.DEST_OS == 'linux' and not ctx.options.nolsb: + ctx.set_lsb_compiler() + + global is_cross + is_cross = (BUILD_OS != ctx.env.DEST_OS) + + if is_cross: + ctx.msg('System', 'Assuming cross-compilation for %s' % + DESTOS_TO_SYSTEM[ctx.env.DEST_OS]) + + if ctx.env.DEST_OS in ('hpux', 'sunos'): + # For SunOS and HP-UX we determine some settings from + # Python's sysconfig. For cross-compiling somebody needs to + # implement options to overwrite these values as they may be + # wrong. + # For SunOS/Solaris mappgin DEST_OS to system is not yet known. + ctx.fatal('Cross-compiling for target %s is not yet supported. ' + 'If you want this feature, please help implementing. ' + 'See the wscript file for details.' % ctx.env.DEST_OS) + + # Detect architecture after completing compiler search + ctx.detect_arch() + + # Set proper architecture and CPU for C compiler + ctx.set_arch_flags() + + ### Other Tools + + if ctx.env.DEST_OS == 'win32': + # Do not embed manifest file when using MSVC (Visual Studio). + # Manifest file will be added in the phase of packaging python + # application by PyInstaller. + ctx.env.MSVC_MANIFEST = False + + if ctx.env.CC_NAME != 'msvc': + # Load tool to process *.rc* files for C/C++ like icon for exe + # files. For msvc waf loads this tool automatically + ctx.find_program([assoc_programm(ctx, 'windres')], var='WINRC') + ctx.load('winres') + + ### C Compiler optimizations. + # TODO Set proper optimization flags for MSVC (Visual Studio). + + if ctx.options.debug: + if ctx.env.DEST_OS == 'win32' and ctx.env.CC_NAME == 'msvc': + # Include information for debugging in MSVC/msdebug + ctx.env.append_value('CFLAGS', '/Z7') + ctx.env.append_value('CFLAGS', '/Od') + ctx.env.append_value('LINKFLAGS', '/DEBUG') + else: + # Include gcc debugging information for debugging in GDB. + ctx.env.append_value('CFLAGS', '-g') + else: + if ctx.env.DEST_OS != 'sunos': + ctx.env.append_value('CFLAGS', '-O2') + else: + # Solaris SUN CC doesn't support '-O2' flag + ctx.env.append_value('CFLAGS', '-O') + + if ctx.env.CC_NAME == 'gcc': + # !! These flags are gcc specific + # Turn on all warnings to improve code quality and avoid + # errors. Unused variables and unused functions are still + # accepted to avoid even more conditional code. + # If you are ever tempted to change this, review the commit + # history of this place first. + ctx.env.append_value('CFLAGS', ['-Wall', + '-Werror', + '-Wno-error=unused-variable', + '-Wno-error=unused-function']) + if not ctx.options.show_warnings: + ctx.env.append_value('CFLAGS', ['-Wno-unused-variable', + '-Wno-unused-function']) + + ### Defines, Includes + + if not ctx.env.DEST_OS == 'win32': + # Defines common for Unix and Unix-like platforms. + # For details see: + # http://man.he.net/man7/feature_test_macros + ctx.env.append_value('DEFINES', '_REENTRANT') + + # mkdtemp() is available only if _BSD_SOURCE is defined. + ctx.env.append_value('DEFINES', '_BSD_SOURCE') + + if ctx.env.DEST_OS == 'linux': + # Recent GCC 5.x complains about _BSD_SOURCE under Linux: + # _BSD_SOURCE and _SVID_SOURCE are deprecated, use _DEFAULT_SOURCE + ctx.env.append_value('DEFINES', '_DEFAULT_SOURCE') + + # TODO What other platforms support _FORTIFY_SOURCE macro? OS X? + # TODO OS X's CLang appears to support this macro as well. See: + # https://marc.info/?l=cfe-dev&m=122032133830183 + + # For security, enable the _FORTIFY_SOURCE macro detecting buffer + # overflows in various string and memory manipulation functions. + if ctx.options.debug: + ctx.env.append_value('CFLAGS', '-U_FORTIFY_SOURCE') + elif ctx.env.CC_NAME == 'gcc': + # Undefine this macro if already defined by default to avoid + # "macro redefinition" errors. + ctx.env.append_value('CFLAGS', '-U_FORTIFY_SOURCE') + + # Define this macro. + ctx.env.append_value('DEFINES', '_FORTIFY_SOURCE=2') + # On Mac OS X, mkdtemp() is available only with _DARWIN_C_SOURCE. + elif ctx.env.DEST_OS == 'darwin': + ctx.env.append_value('DEFINES', '_DARWIN_C_SOURCE') + + if ctx.env.DEST_OS == 'win32': + ctx.env.append_value('DEFINES', 'WIN32') + ctx.env.append_value('CPPPATH', '../zlib') + + elif ctx.env.DEST_OS == 'sunos': + ctx.env.append_value('DEFINES', 'SUNOS') + if ctx.env.CC_NAME == 'gcc': + # On Solaris using gcc the linker options for shared and static + # libraries are slightly different from other platforms. + ctx.env['SHLIB_MARKER'] = '-Wl,-Bdynamic' + ctx.env['STLIB_MARKER'] = '-Wl,-Bstatic' + # On Solaris using gcc, the compiler needs to be gnu99 + ctx.env.append_value('CFLAGS', '-std=gnu99') + + elif ctx.env.DEST_OS == 'aix': + ctx.env.append_value('DEFINES', 'AIX') + # On AIX some APIs are restricted if _ALL_SOURCE is not defined. + # In the case of PyInstaller, we need the AIX specific flag RTLD_MEMBER + # for dlopen() which is used to load a shared object from a library + # archive. We need to load the Python library like this: + # dlopen("libpython2.7.a(libpython2.7.so)", RTLD_MEMBER) + ctx.env.append_value('DEFINES', '_ALL_SOURCE') + + # On AIX using gcc the linker options for shared and static + # libraries are slightly different from other platforms. + ctx.env['SHLIB_MARKER'] = '-Wl,-bdynamic' + ctx.env['STLIB_MARKER'] = '-Wl,-bstatic' + + elif ctx.env.DEST_OS == 'hpux': + ctx.env.append_value('DEFINES', 'HPUX') + if ctx.env.CC_NAME == 'gcc': + if ctx.env.PYI_ARCH == '32bit': + ctx.env.append_value('LIBPATH', '/usr/local/lib/hpux32') + ctx.env.append_value('STATICLIBPATH', '/usr/local/lib/hpux32') + else: + ctx.env.append_value('LIBPATH', '/usr/local/lib/hpux64') + ctx.env.append_value('STATICLIBPATH', '/usr/local/lib/hpux64') + + + elif ctx.env.DEST_OS == 'darwin': + # macOS 10.13 is the oldest version supported by Apple. + # According to OS X doc this variable is equivalent to gcc option: + # -mmacosx-version-min=10.13 + if not os.environ.get('MACOSX_DEPLOYMENT_TARGET'): + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.13' + + ### Libraries + + if ctx.env.DEST_OS == 'win32': + if ctx.env.CC_NAME == 'msvc': + ctx.check_libs_msvc('user32 comctl32 kernel32 advapi32', + mandatory=True) + else: + ctx.check_cc(lib='user32', mandatory=True) + ctx.check_cc(lib='comctl32', mandatory=True) + ctx.check_cc(lib='kernel32', mandatory=True) + ctx.check_cc(lib='advapi32', mandatory=True) + else: + # Mac OS X and FreeBSD do not need libdl. + # https://stackoverflow.com/questions/20169660/where-is-libdl-so-on-mac-os-x + if ctx.env.DEST_OS not in ('darwin', 'freebsd', 'openbsd'): + ctx.check_cc(lib='dl', mandatory=True) + # libdl to be thread-safe requires libpthread! + ctx.check_cc(lib='pthread', mandatory=True) + if ctx.env.DEST_OS == 'freebsd' and sysconfig.get_config_var('HAVE_PTHREAD_H'): + # On FreeBSD if python has threads: libthr needs to be loaded in + # the main process, so the bootloader needs to be link to thr. + ctx.check_cc(lib='thr', mandatory=True) + elif ctx.env.DEST_OS == 'hpux' and sysconfig.get_config_var('HAVE_PTHREAD_H'): + ctx.check_cc(lib='pthread', mandatory=True) + ctx.check_cc(lib='m', mandatory=True) + ctx.check_cc(lib='z', mandatory=True, uselib_store='Z') + # This uses Boehm GC to manage memory - it replaces malloc() / free() + # functions. Some messages are printed if memory is not deallocated. + if ctx.options.boehmgc: + ctx.check_cc(lib='gc', mandatory=True) + ctx.env.append_value('DEFINES', 'PYI_LEAK_DETECTOR') + ctx.env.append_value('DEFINES', 'GC_FIND_LEAK') + ctx.env.append_value('DEFINES', 'GC_DEBUG') + ctx.env.append_value('DEFINES', 'SAVE_CALL_CHAIN') + + ctx.recurse("tests") + + ### Functions + + # The old ``function_name`` parameter to ``check_cc`` is no longer + # supported. This code is based on old waf source at + # https://gitlab.com/ita1024/waf/commit/62fe305d04ed37b1be1a3327a74b2fee6c458634#255b2344e5268e6a34bedd2f8c4680798344fec7. + SNIP_FUNCTION = ''' + #include <%s> + + int main(int argc, char **argv) { + void (*p)(); + + (void)argc; (void)argv; + p=(void(*)())(%s); + return !p; + } +''' + # OS support for these functions varies. + for header, function_name in (('stdlib.h', 'unsetenv'), + ('stdlib.h', 'mkdtemp'), + ('libgen.h', 'dirname'), + ('libgen.h', 'basename'), + ('string.h', 'strndup'), + ('string.h', 'strnlen')): + ctx.check( + fragment=SNIP_FUNCTION % (header, function_name), + mandatory=False, + define_name=ctx.have_define(function_name), + msg='Checking for function %s' % function_name) + + ### CFLAGS + + if ctx.env.DEST_OS == 'win32': + if ctx.env.CC_NAME == 'msvc': + # Use Unicode entry point wmain/wWinMain and wchar_t WinAPI + ctx.env.append_value('CFLAGS', '-DUNICODE') + ctx.env.append_value('CFLAGS', '-D_UNICODE') + # set XP target as minimal target OS ver. when using Windows w/MSVC + # https://blogs.msdn.microsoft.com/vcblog/2012/10/08/windows-xp-targeting-with-c-in-visual-studio-2012/ + ctx.env.append_value('LINKFLAGS', '/SUBSYSTEM:CONSOLE,%s' % ( + '5.01' if ctx.env.PYI_ARCH == '32bit' else '5.02')) + else: + # Use Visual C++ compatible alignment + ctx.env.append_value('CFLAGS', '-mms-bitfields') + + # Define UNICODE and _UNICODE for wchar_t WinAPI + ctx.env.append_value('CFLAGS', '-municode') + + # Use Unicode entry point wmain/wWinMain + ctx.env.append_value('LINKFLAGS', '-municode') + + if ctx.env.DEST_OS == 'darwin': + if not any(x for x in ctx.env.CPPFLAGS + ctx.env.CFLAGS + if x.startswith('-mmacosx-version-min=')): + ctx.env.append_value('CFLAGS', '-mmacosx-version-min=10.7') + if not any(x for x in ctx.env.LDFLAGS + ctx.env.LINKFLAGS + if x.startswith('-mmacosx-version-min=')): + ctx.env.append_value('LINKFLAGS', '-mmacosx-version-min=10.7') + + # On linux link only with needed libraries. + # -Wl,--as-needed is on some platforms detected during configure but + # fails during build. (Mac OS X, Solaris, AIX) + if ctx.env.DEST_OS == 'linux' and ctx.check_cc(cflags='-Wl,--as-needed'): + ctx.env.append_value('LINKFLAGS', '-Wl,--as-needed') + + if ctx.env.CC_NAME != 'msvc': + # This tool allows reducing the size of executables. + ctx.find_program([assoc_programm(ctx, 'strip')], var='STRIP') + ctx.load('strip', tooldir='tools') + + def windowed(name, baseenv): + """Setup windowed environment based on `baseenv`.""" + ctx.setenv(name, baseenv) # Inherit from `baseenv`. + ctx.env.append_value('DEFINES', 'WINDOWED') + + if ctx.env.DEST_OS == 'win32': + if ctx.env.CC_NAME != 'msvc': + # For MinGW disable console window on Windows - MinGW option + # TODO Is it necessary to have -mwindows for C and LINK flags? + ctx.env.append_value('LINKFLAGS', '-mwindows') + ctx.env.append_value('CFLAGS', '-mwindows') + else: + _link_flags = ctx.env._get_list_value_for_modification('LINKFLAGS') + _subsystem = [x for x in _link_flags if x.startswith('/SUBSYSTEM:')] + for parameter in _subsystem: + _link_flags.remove(parameter) + ctx.env.append_value('LINKFLAGS', '/SUBSYSTEM:WINDOWS,%s' % ( + '5.01' if ctx.env.PYI_ARCH == '32bit' else '5.02')) + elif ctx.env.DEST_OS == 'darwin': + # To support catching AppleEvents and running as ordinary OSX GUI + # app, we have to link against the Carbon framework. + # This linkage only needs to be there for the windowed bootloaders. + ctx.env.append_value('LINKFLAGS', '-framework') + ctx.env.append_value('LINKFLAGS', 'Carbon') + # TODO Do we need to link with this framework? + # conf.env.append_value('LINKFLAGS', '-framework') + # conf.env.append_value('LINKFLAGS', 'ApplicationServices') + + ### DEBUG and RELEASE environments + basic_env = ctx.env + + ## setup DEBUG environment + ctx.setenv('debug', basic_env) # Ensure env contains shared values. + debug_env = ctx.env + # This defines enable verbose console output of the bootloader. + ctx.env.append_value('DEFINES', ['LAUNCH_DEBUG']) + ctx.env.append_value('DEFINES', 'NDEBUG') + + ## setup windowed DEBUG environment + windowed('debugw', debug_env) + + ## setup RELEASE environment + ctx.setenv('release', basic_env) # Ensure env contains shared values. + release_env = ctx.env + ctx.env.append_value('DEFINES', 'NDEBUG') + + ## setup windowed RELEASE environment + windowed('releasew', release_env) + +# TODO Use 'strip' command to decrease the size of compiled bootloaders. +def build(ctx): + if not ctx.variant: + ctx.fatal('Call "python waf all" to compile all bootloaders.') + + exe_name = variants[ctx.variant] + + install_path = os.path.join(os.getcwd(), '../PyInstaller/bootloader', + ctx.env.PYI_SYSTEM + "-" + ctx.env.PYI_ARCH) + install_path = os.path.normpath(install_path) + + if machine(): + install_path += '-' + machine() + + if not ctx.env.LIB_Z: + # If the operating system does not provide zlib, build our own. The + # configure phase defines whether or not zlib is mandatory for a + # platform. + ctx.stlib( + source=ctx.path.ant_glob('zlib/*.c'), + target='static_zlib', + name='Z', + includes='zlib') + + # By default strip final executables to make them smaller. + features = 'strip' + if ctx.env.CC_NAME == 'msvc': + # Do not strip bootloaders when using MSVC. + features = '' + + ctx.objects(source=ctx.path.ant_glob('src/*.c', excl="src/main.c"), + includes='src windows zlib', + target="OBJECTS") + + ctx.env.link_with_dynlibs = [] + ctx.env.link_with_staticlibs = [] + if ctx.env.DEST_OS == 'win32': + # Use different RC file (icon) for console/windowed mode - remove '_d' + icon_rc = 'windows/' + exe_name.replace('_d', '') + '.rc' + # On Windows we need to link library zlib statically. + ctx.program( + source=['src/main.c', icon_rc], + target=exe_name, + install_path=install_path, + use='OBJECTS USER32 COMCTL32 KERNEL32 ADVAPI32 Z', + includes='src windows zlib', + features=features, + ) + else: + # Linux, Darwin (MacOSX), ... + # Only the libs found will actually be used, so it's safe to list all + # here. The decision if a lib is required for a specific platform is + # made in the configure phase. + libs = ['DL', 'M', 'Z', # 'z' - zlib, 'm' - math, + 'PTHREAD', # important! needs for libdl to be thread-safe + 'THR'] # may be used on FreBSD + staticlibs = [] + if ctx.env.DEST_OS == 'aix': + # link statically with zlib, case sensitive + libs.remove('Z') + staticlibs.append('z') + + if ctx.options.boehmgc: + libs.append('GC') + + ctx.env.link_with_dynlibs = libs + ctx.env.link_with_staticlibs = staticlibs + + ctx.program( + source='src/main.c', + target=exe_name, + includes='src', + use=libs + ["OBJECTS"], + stlib=staticlibs, + install_path=install_path, + features=features) + + ctx.recurse("tests") + + +class make_all(BuildContext): + """ + Do build and install in one step. + """ + cmd = 'make_all' + + def execute_build(ctx): + Options.commands = ['build_debug', 'build_release'] + # On Windows and Mac OS X we also need console/windowed bootloaders. + # On other platforms they make no sense. + if ctx.env.DEST_OS in ('win32', 'darwin'): + Options.commands += ['build_debugw', 'build_releasew'] + # Install bootloaders. + Options.commands += ['install_debug', 'install_release'] + if ctx.env.DEST_OS in ('win32', 'darwin'): + Options.commands += ['install_debugw', 'install_releasew'] + + +def all(ctx): + """ + Do configure, build and install in one step. + """ + # `all` is run prior to `configure`, thus it does not get a build context. + # Thus another command `make_all` is required which gets the build + # context and can make decisions based on the outcome of `configure`. + Options.commands = ['distclean', 'configure', 'make_all'] + + +# Set up building several variants of bootloader. +for x in variants: + class BootloaderContext(BuildContext): + cmd = 'build' + '_' + x + variant = x + + class BootloaderInstallContext(InstallContext): + cmd = 'install' + '_' + x + variant = x diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/README b/3rdparty/pyinstaller-4.3/bootloader/zlib/README new file mode 100644 index 0000000000000000000000000000000000000000..18871b24f47c09049616a5be4803c560dff7020e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/README @@ -0,0 +1,3 @@ +This directory contains an extract of zlib 1.2.11. It is the minimum set of +files necessary to decompress data from a zip file with the inflate compression +scheme. diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/adler32.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/adler32.c new file mode 100644 index 0000000000000000000000000000000000000000..73b36cad74c7e98ba5c73607e62687b4b027608b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/adler32.c @@ -0,0 +1,186 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2011, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); + +#define BASE 65521U /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware -- + try it both ways to see which is faster */ +#ifdef NO_DIVIDE +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 + (thank you to John Reiser for pointing this out) */ +# define CHOP(a) \ + do { \ + unsigned long tmp = a >> 16; \ + a &= 0xffffUL; \ + a += (tmp << 4) - tmp; \ + } while (0) +# define MOD28(a) \ + do { \ + CHOP(a); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD(a) \ + do { \ + CHOP(a); \ + MOD28(a); \ + } while (0) +# define MOD63(a) \ + do { /* this assumes a is not negative */ \ + z_off64_t tmp = a >> 32; \ + a &= 0xffffffffL; \ + a += (tmp << 8) - (tmp << 5) + tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD28(a) a %= BASE +# define MOD63(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32_z(adler, buf, len) + uLong adler; + const Bytef *buf; + z_size_t len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD28(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + return adler32_z(adler, buf, len); +} + +/* ========================================================================= */ +local uLong adler32_combine_(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* for negative len, return invalid adler32 as a clue for debugging */ + if (len2 < 0) + return 0xffffffffUL; + + /* the derivation of this formula is left as an exercise for the reader */ + MOD63(len2); /* assumes len2 >= 0 */ + rem = (unsigned)len2; + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= ((unsigned long)BASE << 1)) sum2 -= ((unsigned long)BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} + +uLong ZEXPORT adler32_combine64(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off64_t len2; +{ + return adler32_combine_(adler1, adler2, len2); +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/crc32.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/crc32.c new file mode 100644 index 0000000000000000000000000000000000000000..3d523cb183e972091f472e9c4a54a5a0602820a5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/crc32.c @@ -0,0 +1,442 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2006, 2010, 2011, 2012, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +/* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif +#ifdef BYFOUR + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, z_size_t)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, z_size_t)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); + + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local z_crc_t FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const z_crc_t FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + z_crc_t c; + int n, k; + z_crc_t poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (z_crc_t)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = ZSWAP32(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = ZSWAP32(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const z_crc_t FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const z_crc_t FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const z_crc_t FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const z_crc_t FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32_z(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + z_size_t len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + z_crc_t endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + uInt len; +{ + return crc32_z(crc, buf, len); +} + +#ifdef BYFOUR + +/* + This BYFOUR code accesses the passed unsigned char * buffer with a 32-bit + integer pointer type. This violates the strict aliasing rule, where a + compiler can assume, for optimization purposes, that two pointers to + fundamentally different types won't ever point to the same memory. This can + manifest as a problem only if one of the pointers is written to. This code + only reads from those pointers. So long as this code remains isolated in + this compilation unit, there won't be a problem. For this reason, this code + should not be copied and pasted into a compilation unit in which other code + writes to the buffer that is passed to these routines. + */ + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + z_size_t len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = (z_crc_t)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *buf4++; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + z_size_t len; +{ + register z_crc_t c; + register const z_crc_t FAR *buf4; + + c = ZSWAP32((z_crc_t)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(ZSWAP32(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +local uLong crc32_combine_(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case (also disallow negative lengths) */ + if (len2 <= 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320UL; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} + +uLong ZEXPORT crc32_combine64(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off64_t len2; +{ + return crc32_combine_(crc1, crc2, len2); +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/crc32.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/crc32.h new file mode 100644 index 0000000000000000000000000000000000000000..b7e25cf928c37992fa2710de7330efb622351367 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const z_crc_t FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/crypt.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/crypt.h new file mode 100644 index 0000000000000000000000000000000000000000..f14a628b4c7cbdb5bf8e9795405719f8f767cfc5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/crypt.h @@ -0,0 +1,132 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting) + const char *passwd; /* password string */ + unsigned char *buf; /* where to write header */ + int bufSize; + unsigned long* pkeys; + const unsigned long* pcrc_32_tab; + unsigned long crcForCrypting; +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/gzguts.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/gzguts.h new file mode 100644 index 0000000000000000000000000000000000000000..581c6279156c1686bb5a9bffb9aff418a6430617 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/gzguts.h @@ -0,0 +1,218 @@ +/* gzguts.h -- zlib internal header definitions for gz* operations + * Copyright (C) 2004, 2005, 2010, 2011, 2012, 2013, 2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#ifdef _LARGEFILE64_SOURCE +# ifndef _LARGEFILE_SOURCE +# define _LARGEFILE_SOURCE 1 +# endif +# ifdef _FILE_OFFSET_BITS +# undef _FILE_OFFSET_BITS +# endif +#endif + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include +#include "zlib.h" +#ifdef STDC +# include +# include +# include +#endif + +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#include + +#ifdef _WIN32 +# include +#endif + +#if defined(__TURBOC__) || defined(_MSC_VER) || defined(_WIN32) +# include +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) +# define WIDECHAR +#endif + +#ifdef WINAPI_FAMILY +# define open _open +# define read _read +# define write _write +# define close _close +#endif + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#if defined(MSDOS) && defined(__BORLANDC__) && (BORLANDC > 0x410) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif + +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS +/* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 +/* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) +# define vsnprintf _vsnprintf +# endif +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +# ifdef VMS +# define NO_vsnprintf +# endif +# ifdef __OS400__ +# define NO_vsnprintf +# endif +# ifdef __MVS__ +# define NO_vsnprintf +# endif +#endif + +/* unlike snprintf (which is required in C99), _snprintf does not guarantee + null termination of the result -- however this is only used in gzlib.c where + the result is assured to fit in the space provided */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf _snprintf +#endif + +#ifndef local +# define local static +#endif +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +/* gz* functions always use library allocation functions */ +#ifndef STDC + extern voidp malloc OF((uInt size)); + extern void free OF((voidpf ptr)); +#endif + +/* get errno and strerror definition */ +#if defined UNDER_CE +# include +# define zstrerror() gz_strwinerror((DWORD)GetLastError()) +#else +# ifndef NO_STRERROR +# include +# define zstrerror() strerror(errno) +# else +# define zstrerror() "stdio error (consult errno)" +# endif +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); +#endif + +/* default memLevel */ +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif + +/* default i/o buffer size -- double this for output when reading (this and + twice this must be able to fit in an unsigned type) */ +#define GZBUFSIZE 8192 + +/* gzip modes, also provide a little integrity check on the passed structure */ +#define GZ_NONE 0 +#define GZ_READ 7247 +#define GZ_WRITE 31153 +#define GZ_APPEND 1 /* mode set to GZ_WRITE after the file is opened */ + +/* values for gz_state how */ +#define LOOK 0 /* look for a gzip header */ +#define COPY 1 /* copy input directly */ +#define GZIP 2 /* decompress a gzip stream */ + +/* internal gzip file state data structure */ +typedef struct { + /* exposed contents for gzgetc() macro */ + struct gzFile_s x; /* "x" for exposed */ + /* x.have: number of bytes available at x.next */ + /* x.next: next output data to deliver or write */ + /* x.pos: current position in uncompressed data */ + /* used for both reading and writing */ + int mode; /* see gzip modes above */ + int fd; /* file descriptor */ + char *path; /* path or fd for error messages */ + unsigned size; /* buffer size, zero if not allocated yet */ + unsigned want; /* requested buffer size, default is GZBUFSIZE */ + unsigned char *in; /* input buffer (double-sized when writing) */ + unsigned char *out; /* output buffer (double-sized when reading) */ + int direct; /* 0 if processing gzip, 1 if transparent */ + /* just for reading */ + int how; /* 0: get header, 1: copy, 2: decompress */ + z_off64_t start; /* where the gzip data started, for rewinding */ + int eof; /* true if end of input file reached */ + int past; /* true if read requested past end */ + /* just for writing */ + int level; /* compression level */ + int strategy; /* compression strategy */ + /* seek request */ + z_off64_t skip; /* amount to skip (already rewound if backwards) */ + int seek; /* true if seek request pending */ + /* error information */ + int err; /* error code */ + char *msg; /* error message */ + /* zlib inflate or deflate stream */ + z_stream strm; /* stream structure in-place (not a pointer) */ +} gz_state; +typedef gz_state FAR *gz_statep; + +/* shared functions */ +void ZLIB_INTERNAL gz_error OF((gz_statep, int, const char *)); +#if defined UNDER_CE +char ZLIB_INTERNAL *gz_strwinerror OF((DWORD error)); +#endif + +/* GT_OFF(x), where x is an unsigned value, is true if x > maximum z_off64_t + value -- needed when comparing unsigned to z_off64_t, which is signed + (possible z_off64_t types off_t, off64_t, and long are all signed) */ +#ifdef INT_MAX +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > INT_MAX) +#else +unsigned ZLIB_INTERNAL gz_intmax OF((void)); +# define GT_OFF(x) (sizeof(int) == sizeof(z_off64_t) && (x) > gz_intmax()) +#endif diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/inffast.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/inffast.c new file mode 100644 index 0000000000000000000000000000000000000000..f14174cd663893ad8d277d7c220c287e66e43129 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/inffast.c @@ -0,0 +1,323 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef ASMINF +# pragma message("Assembler code may have bugs -- use at your own risk") +#else + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void ZLIB_INTERNAL inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code here; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in; + last = in + (strm->avail_in - 5); + out = strm->next_out; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + wnext = state->wnext; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + here = lcode[hold & lmask]; + dolen: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op == 0) { /* literal */ + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + *out++ = (unsigned char)(here.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + here = dcode[hold & dmask]; + dodist: + op = (unsigned)(here.bits); + hold >>= op; + bits -= op; + op = (unsigned)(here.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(here.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(*in++) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state->sane) { + strm->msg = + (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + if (len <= op - whave) { + do { + *out++ = 0; + } while (--len); + continue; + } + len -= op - whave; + do { + *out++ = 0; + } while (--op > whave); + if (op == 0) { + from = out - dist; + do { + *out++ = *from++; + } while (--len); + continue; + } +#endif + } + from = window; + if (wnext == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + *out++ = *from++; + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (wnext < op) { /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { /* some from end of window */ + len -= op; + do { + *out++ = *from++; + } while (--op); + from = window; + if (wnext < len) { /* some from start of window */ + op = wnext; + len -= op; + do { + *out++ = *from++; + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += wnext - op; + if (op < len) { /* some from window */ + len -= op; + do { + *out++ = *from++; + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; + len -= 3; + } + if (len) { + *out++ = *from++; + if (len > 1) + *out++ = *from++; + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + *out++ = *from++; + *out++ = *from++; + *out++ = *from++; + len -= 3; + } while (len > 2); + if (len) { + *out++ = *from++; + if (len > 1) + *out++ = *from++; + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + here = dcode[here.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + here = lcode[here.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in; + strm->next_out = out; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and wnext == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/inffast.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/inffast.h new file mode 100644 index 0000000000000000000000000000000000000000..e1e6db4ac5d76e694dfe9d1fc0467e547b07a249 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/inffixed.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/inffixed.h new file mode 100644 index 0000000000000000000000000000000000000000..0b29a5ad6e1f252a9e170084d76860f814953a13 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/inflate.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/inflate.c new file mode 100644 index 0000000000000000000000000000000000000000..5c29e06e0a220cedd1d0a5dfd025f80820e41287 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/inflate.c @@ -0,0 +1,1561 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common wnext == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local int inflateStateCheck OF((z_streamp strm)); +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, + unsigned len)); + +local int inflateStateCheck(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) + return 1; + state = (struct inflate_state FAR *)strm->state; + if (state == Z_NULL || state->strm != strm || + state->mode < HEAD || state->mode > SYNC) + return 1; + return 0; +} + +int ZEXPORT inflateResetKeep(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + state->sane = 1; + state->back = -1; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + +int ZEXPORT inflateReset2(strm, windowBits) +z_streamp strm; +int windowBits; +{ + int wrap; + struct inflate_state FAR *state; + + /* get the state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } + else { + wrap = (windowBits >> 4) + 5; +#ifdef GUNZIP + if (windowBits < 48) + windowBits &= 15; +#endif + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) + return Z_STREAM_ERROR; + if (state->window != Z_NULL && state->wbits != (unsigned)windowBits) { + ZFREE(strm, state->window); + state->window = Z_NULL; + } + + /* update state and reset the rest of it */ + state->wrap = wrap; + state->wbits = (unsigned)windowBits; + return inflateReset(strm); +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + int ret; + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->strm = strm; + state->window = Z_NULL; + state->mode = HEAD; /* to pass state test in inflateReset2() */ + ret = inflateReset2(strm, windowBits); + if (ret != Z_OK) { + ZFREE(strm, state); + strm->state = Z_NULL; + } + return ret; +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits < 0) { + state->hold = 0; + state->bits = 0; + return Z_OK; + } + if (bits > 16 || state->bits + (uInt)bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += (unsigned)value << state->bits; + state->bits += (uInt)bits; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, end, copy) +z_streamp strm; +const Bytef *end; +unsigned copy; +{ + struct inflate_state FAR *state; + unsigned dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->wnext = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state->wsize) { + zmemcpy(state->window, end - state->wsize, state->wsize); + state->wnext = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->wnext; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->wnext, end - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, end - copy, copy); + state->wnext = copy; + state->whave = state->wsize; + } + else { + state->wnext += dist; + if (state->wnext == state->wsize) state->wnext = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (inflateStateCheck(strm) || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + if (state->wbits == 0) + state->wbits = 15; + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (state->wbits == 0) + state->wbits = len; + if (len > 15 || len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if ((state->flags & 0x0200) && (state->wrap & 4)) + CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if ((state->flags & 0x0200) && (state->wrap & 4)) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = (Bytef)len; + } while (len && copy < have); + if ((state->flags & 0x0200) && (state->wrap & 4)) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = (Bytef)len; + } while (len && copy < have); + if ((state->flags & 0x0200) && (state->wrap & 4)) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if ((state->wrap & 4) && hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = ZSWAP32(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK || flush == Z_TREES) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN_; /* decode codes */ + if (flush == Z_TREES) { + DROPBITS(2); + goto inf_leave; + } + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY_; + if (flush == Z_TREES) goto inf_leave; + case COPY_: + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (const code FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (const code FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN_; + if (flush == Z_TREES) goto inf_leave; + case LEN_: + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + if (state->mode == TYPE) + state->back = -1; + break; + } + state->back = 0; + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + state->length = (unsigned)here.val; + if ((int)(here.op) == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + state->mode = LIT; + break; + } + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->back = -1; + state->mode = TYPE; + break; + } + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(here.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->was = state->length; + state->mode = DIST; + case DIST: + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + state->back += last.bits; + } + DROPBITS(here.bits); + state->back += here.bits; + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + state->extra = (unsigned)(here.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + state->back += state->extra; + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->whave) { + if (state->sane) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + Trace((stderr, "inflate.c too far\n")); + copy -= state->whave; + if (copy > state->length) copy = state->length; + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = 0; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; +#endif + } + if (copy > state->wnext) { + copy -= state->wnext; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->wnext - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if ((state->wrap & 4) && out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if ((state->wrap & 4) && ( +#ifdef GUNZIP + state->flags ? hold : +#endif + ZSWAP32(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if ((state->wrap & 4) && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = (int)state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0) + + (state->mode == LEN_ || state->mode == COPY_ ? 256 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (inflateStateCheck(strm)) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) +z_streamp strm; +Bytef *dictionary; +uInt *dictLength; +{ + struct inflate_state FAR *state; + + /* check state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long dictid; + int ret; + + /* check state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary identifier */ + if (state->mode == DICT) { + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { + state->mode = MEM; + return Z_MEM_ERROR; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +const unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (inflateStateCheck(source) || dest == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); + copy->strm = dest; + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} + +int ZEXPORT inflateUndermine(strm, subvert) +z_streamp strm; +int subvert; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; +#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + state->sane = !subvert; + return Z_OK; +#else + (void)subvert; + state->sane = 1; + return Z_DATA_ERROR; +#endif +} + +int ZEXPORT inflateValidate(strm, check) +z_streamp strm; +int check; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (check) + state->wrap |= 4; + else + state->wrap &= ~4; + return Z_OK; +} + +long ZEXPORT inflateMark(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (inflateStateCheck(strm)) + return -(1L << 16); + state = (struct inflate_state FAR *)strm->state; + return (long)(((unsigned long)((long)state->back)) << 16) + + (state->mode == COPY ? state->length : + (state->mode == MATCH ? state->was - state->length : 0)); +} + +unsigned long ZEXPORT inflateCodesUsed(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (inflateStateCheck(strm)) return (unsigned long)-1; + state = (struct inflate_state FAR *)strm->state; + return (unsigned long)(state->next - state->codes); +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/inflate.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/inflate.h new file mode 100644 index 0000000000000000000000000000000000000000..947db39aaa045a2d675c547468b402b78ab6e992 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/inflate.h @@ -0,0 +1,125 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2016 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD = 16180, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY_, /* i/o: same as COPY below, but only first time in */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN_, /* i: same as LEN below, but only first time in */ + LEN, /* i: waiting for length/lit/eob code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to BAD or MEM on error -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) or (raw) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT -> + HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + (raw) -> TYPEDO + Read deflate blocks: + TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK + STORED -> COPY_ -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN_ + LEN_ -> LEN + Read deflate codes in fixed or dynamic block: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* State maintained between inflate() calls -- approximately 7K bytes, not + including the allocated sliding window, which is up to 32K bytes. */ +struct inflate_state { + z_streamp strm; /* pointer back to this zlib stream */ + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip, + bit 2 true to validate check value */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned wnext; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ + int sane; /* if false, allow invalid distance too far */ + int back; /* bits back of last unprocessed length/lit */ + unsigned was; /* initial length of match */ +}; diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/inftrees.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/inftrees.c new file mode 100644 index 0000000000000000000000000000000000000000..a49e29cba9573ed0a50dc2e5a8e6e19a8cb12179 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/inftrees.c @@ -0,0 +1,304 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2017 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.11 Copyright 1995-2017 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int ZLIB_INTERNAL inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code here; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + unsigned match; /* use base and extra for symbol >= match */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 77, 202}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)1; + here.val = (unsigned short)0; + *(*table)++ = here; /* make a table to force an error */ + *(*table)++ = here; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + match = 20; + break; + case LENS: + base = lbase; + extra = lext; + match = 257; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + match = 0; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here.bits = (unsigned char)(len - drop); + if (work[sym] + 1U < match) { + here.op = (unsigned char)0; + here.val = work[sym]; + } + else if (work[sym] >= match) { + here.op = (unsigned char)(extra[work[sym] - match]); + here.val = base[work[sym] - match]; + } + else { + here.op = (unsigned char)(32 + 64); /* end of block */ + here.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = here; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/inftrees.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/inftrees.h new file mode 100644 index 0000000000000000000000000000000000000000..a685d8c6ea0dc7d684706c84e6ab126e802ee176 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/inftrees.h @@ -0,0 +1,62 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005, 2010 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of the dynamic table. The maximum number of code structures is + 1444, which is the sum of 852 for literal/length codes and 592 for distance + codes. These values were found by exhaustive searches using the program + examples/enough.c found in the zlib distribtution. The arguments to that + program are the number of symbols, the initial root table size, and the + maximum bit length of a code. "enough 286 9 15" for literal/length codes + returns returns 852, and "enough 30 6 15" for distance codes returns 592. + The initial root table size (9 or 6) is found in the fifth argument of the + inflate_table() calls in inflate.c and infback.c. If the root table size is + changed, then these maximum sizes would be need to be recalculated and + updated. */ +#define ENOUGH_LENS 852 +#define ENOUGH_DISTS 592 +#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS) + +/* Type of code to build for inflate_table() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/ioapi.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/ioapi.c new file mode 100644 index 0000000000000000000000000000000000000000..7f20c182f98f794eb8170d78e4fac5949371ac16 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/ioapi.c @@ -0,0 +1,177 @@ +/* ioapi.c -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#include +#include +#include + +#include "zlib.h" +#include "ioapi.h" + + + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +voidpf ZCALLBACK fopen_file_func OF(( + voidpf opaque, + const char* filename, + int mode)); + +uLong ZCALLBACK fread_file_func OF(( + voidpf opaque, + voidpf stream, + void* buf, + uLong size)); + +uLong ZCALLBACK fwrite_file_func OF(( + voidpf opaque, + voidpf stream, + const void* buf, + uLong size)); + +long ZCALLBACK ftell_file_func OF(( + voidpf opaque, + voidpf stream)); + +long ZCALLBACK fseek_file_func OF(( + voidpf opaque, + voidpf stream, + uLong offset, + int origin)); + +int ZCALLBACK fclose_file_func OF(( + voidpf opaque, + voidpf stream)); + +int ZCALLBACK ferror_file_func OF(( + voidpf opaque, + voidpf stream)); + + +voidpf ZCALLBACK fopen_file_func (opaque, filename, mode) + voidpf opaque; + const char* filename; + int mode; +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + + +uLong ZCALLBACK fread_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + + +uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size) + voidpf opaque; + voidpf stream; + const void* buf; + uLong size; +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +long ZCALLBACK ftell_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + +long ZCALLBACK fseek_file_func (opaque, stream, offset, origin) + voidpf opaque; + voidpf stream; + uLong offset; + int origin; +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + fseek((FILE *)stream, offset, fseek_origin); + return ret; +} + +int ZCALLBACK fclose_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +int ZCALLBACK ferror_file_func (opaque, stream) + voidpf opaque; + voidpf stream; +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/ioapi.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/ioapi.h new file mode 100644 index 0000000000000000000000000000000000000000..e73a3b2bd882185a8d29c3c53289d76b90282134 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/ioapi.h @@ -0,0 +1,75 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + files using zlib + zip or unzip API + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant +*/ + +#ifndef _ZLIBIOAPI_H +#define _ZLIBIOAPI_H + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + +#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) +#define ZCALLBACK CALLBACK +#else +#define ZCALLBACK +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + + + +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size)) +#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size)) +#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream)) +#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream)) +#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream)) + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/unzip.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/unzip.c new file mode 100644 index 0000000000000000000000000000000000000000..ddf37b39e969ea4a95e9adec6a88d99be8515bf3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/unzip.c @@ -0,0 +1,1602 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + Read unzip.h for more info +*/ + +/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of +compatibility with older software. The following is from the original crypt.c. Code +woven in by Terry Thorsen 1/2003. +*/ +/* + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html +*/ +/* + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + */ + +/* + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + */ + + +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info_internal_s +{ + uLong offset_curfile;/* relative offset of local header 4 bytes */ +} unz_file_info_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + + uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + uLong offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + uLong pos_local_extrafield; /* position in the local extra field in read*/ + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + uLong rest_read_compressed; /* number of byte to be decompressed */ + uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip_read_info_s; + + +/* unz_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info gi; /* public global information */ + uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + uLong num_file; /* number of the current file in the zipfile*/ + uLong pos_in_central_dir; /* pos of the current file in the central dir*/ + uLong current_file_ok; /* flag about the usability of the current file*/ + uLong central_pos; /* position of the beginning of the central dir*/ + + uLong size_central_dir; /* size of the central directory */ + uLong offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info cur_file_info; /* public info about the current file in zip*/ + unz_file_info_internal cur_file_info_internal; /* private info about it*/ + file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unzlocal_getByte OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + int *pi; +{ + unsigned char c; + int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unzlocal_getShort OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unzlocal_getLong OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; + uLong *pX; +{ + uLong x ; + int i; + int err; + + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (fileName1,fileName2) + const char* fileName1; + const char* fileName2; +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity) + const char* fileName1; + const char* fileName2; + int iCaseSensitivity; +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local uLong unzlocal_SearchCentralDir OF(( + const zlib_filefunc_def* pzlib_filefunc_def, + voidpf filestream)); + +local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream) + const zlib_filefunc_def* pzlib_filefunc_def; + voidpf filestream; +{ + unsigned char* buf; + uLong uSizeFile; + uLong uBackRead; + uLong uMaxBack=0xffff; /* maximum size of global comment */ + uLong uPosFound=0; + + if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uSizeFile-uReadPos); + if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def) + const char *path; + zlib_filefunc_def* pzlib_filefunc_def; +{ + unz_s us; + unz_s *s; + uLong central_pos,uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + uLong number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + if (pzlib_filefunc_def==NULL) + fill_fopen_filefunc(&us.z_filefunc); + else + us.z_filefunc = *pzlib_filefunc_def; + + us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + if (ZSEEK(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* zipfile comment length */ + if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info) + unzFile file; + unz_global_info *pglobal_info; +{ + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + + +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unzlocal_DosDateToTmuDate (ulDosDate, ptm) + uLong ulDosDate; + tm_unz* ptm; +{ + uLong uDate; + uDate = (uLong)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info *pfile_info, + unz_file_info_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unzlocal_GetCurrentFileInfoInternal (file, + pfile_info, + pfile_info_internal, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + unz_file_info_internal *pfile_info_internal; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + unz_s* s; + unz_file_info file_info; + unz_file_info_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (ZSEEK(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + + if ((err==UNZ_OK) && (extraField!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek += file_info.size_file_extra - uSizeRead; + } + else + lSeek+=file_info.size_file_extra; + + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo (file, + pfile_info, + szFileName, fileNameBufferSize, + extraField, extraFieldBufferSize, + szComment, commentBufferSize) + unzFile file; + unz_file_info *pfile_info; + char *szFileName; + uLong fileNameBufferSize; + void *extraField; + uLong extraFieldBufferSize; + char *szComment; + uLong commentBufferSize; +{ + return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (file) + unzFile file; +{ + int err=UNZ_OK; + unz_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (file) + unzFile file; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity) + unzFile file; + const char *szFileName; + int iCaseSensitivity; +{ + unz_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info cur_file_infoSaved; + unz_file_info_internal cur_file_info_internalSaved; + uLong num_fileSaved; + uLong pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; // offset in file + uLong num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGoToFilePos(file, file_pos) + unzFile file; + unz_file_pos* file_pos; +{ + unz_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar, + poffset_local_extrafield, + psize_local_extrafield) + unz_s* s; + uInt* piSizeVar; + uLong *poffset_local_extrafield; + uInt *psize_local_extrafield; +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) { + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && + ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password) + unzFile file; + int* method; + int* level; + int raw; + const char* password; +{ + int err=UNZ_OK; + uInt iSizeVar; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uLong offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar, + &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip_read_info_s*) + ALLOC(sizeof(file_in_zip_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->compression_method = + s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_DEFLATED) && + (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=1; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = (unsigned long*) get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (file) + unzFile file; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (file, password) + unzFile file; + const char* password; +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw) + unzFile file; + int* method; + int* level; + int raw; +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (file, buf, len) + unzFile file; + voidp buf; + unsigned len; +{ + int err=UNZ_OK; + uInt iRead = 0; + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->read_buffer == NULL)) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else + { + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (file) + unzFile file; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (file,buf,len) + unzFile file; + voidp buf; + unsigned len; +{ + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + uInt read_now; + uLong size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (file) + unzFile file; +{ + int err=UNZ_OK; + + unz_s* s; + file_in_zip_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised) + inflateEnd(&pfile_in_zip_read_info->stream); + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf) + unzFile file; + char *szComment; + uLong uSizeBuf; +{ + int err=UNZ_OK; + unz_s* s; + uLong uReadThis ; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern uLong ZEXPORT unzGetOffset (file) + unzFile file; +{ + unz_s* s; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern int ZEXPORT unzSetOffset (file, pos) + unzFile file; + uLong pos; +{ + unz_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/unzip.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/unzip.h new file mode 100644 index 0000000000000000000000000000000000000000..c3206a05899d3229a9024a1431d45416edd8c5e3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/unzip.h @@ -0,0 +1,354 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g + WinZip, InfoZip tools and compatible. + + Multi volume ZipFile (span) are not supported. + Encryption compatible with pkzip 2.04g only supported + Old compressions used by old PKZip 1.x are not supported + + + I WAIT FEEDBACK at mail info@winimage.com + Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution + + Condition of use and distribution are the same than zlib : + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + +*/ + +/* for more info about .ZIP format, see + http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip + http://www.info-zip.org/pub/infozip/doc/ + PkWare has also a specification at : + ftp://ftp.pkware.com/probdesc.zip +*/ + +#ifndef _unz_H +#define _unz_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/zconf.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/zconf.h new file mode 100644 index 0000000000000000000000000000000000000000..44d5bef9880375faec16a85b58cfd0fcb5ecabb0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/zconf.h @@ -0,0 +1,534 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + * Even better than compiling with -DZ_PREFIX would be to use configure to set + * this permanently in zconf.h using "./configure --zprefix". + */ +#ifdef Z_PREFIX /* may be set to #if 1 by ./configure */ +# define Z_PREFIX_SET + +/* all linked symbols and init macros */ +# define _dist_code z__dist_code +# define _length_code z__length_code +# define _tr_align z__tr_align +# define _tr_flush_bits z__tr_flush_bits +# define _tr_flush_block z__tr_flush_block +# define _tr_init z__tr_init +# define _tr_stored_block z__tr_stored_block +# define _tr_tally z__tr_tally +# define adler32 z_adler32 +# define adler32_combine z_adler32_combine +# define adler32_combine64 z_adler32_combine64 +# define adler32_z z_adler32_z +# ifndef Z_SOLO +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# endif +# define crc32 z_crc32 +# define crc32_combine z_crc32_combine +# define crc32_combine64 z_crc32_combine64 +# define crc32_z z_crc32_z +# define deflate z_deflate +# define deflateBound z_deflateBound +# define deflateCopy z_deflateCopy +# define deflateEnd z_deflateEnd +# define deflateGetDictionary z_deflateGetDictionary +# define deflateInit z_deflateInit +# define deflateInit2 z_deflateInit2 +# define deflateInit2_ z_deflateInit2_ +# define deflateInit_ z_deflateInit_ +# define deflateParams z_deflateParams +# define deflatePending z_deflatePending +# define deflatePrime z_deflatePrime +# define deflateReset z_deflateReset +# define deflateResetKeep z_deflateResetKeep +# define deflateSetDictionary z_deflateSetDictionary +# define deflateSetHeader z_deflateSetHeader +# define deflateTune z_deflateTune +# define deflate_copyright z_deflate_copyright +# define get_crc_table z_get_crc_table +# ifndef Z_SOLO +# define gz_error z_gz_error +# define gz_intmax z_gz_intmax +# define gz_strwinerror z_gz_strwinerror +# define gzbuffer z_gzbuffer +# define gzclearerr z_gzclearerr +# define gzclose z_gzclose +# define gzclose_r z_gzclose_r +# define gzclose_w z_gzclose_w +# define gzdirect z_gzdirect +# define gzdopen z_gzdopen +# define gzeof z_gzeof +# define gzerror z_gzerror +# define gzflush z_gzflush +# define gzfread z_gzfread +# define gzfwrite z_gzfwrite +# define gzgetc z_gzgetc +# define gzgetc_ z_gzgetc_ +# define gzgets z_gzgets +# define gzoffset z_gzoffset +# define gzoffset64 z_gzoffset64 +# define gzopen z_gzopen +# define gzopen64 z_gzopen64 +# ifdef _WIN32 +# define gzopen_w z_gzopen_w +# endif +# define gzprintf z_gzprintf +# define gzputc z_gzputc +# define gzputs z_gzputs +# define gzread z_gzread +# define gzrewind z_gzrewind +# define gzseek z_gzseek +# define gzseek64 z_gzseek64 +# define gzsetparams z_gzsetparams +# define gztell z_gztell +# define gztell64 z_gztell64 +# define gzungetc z_gzungetc +# define gzvprintf z_gzvprintf +# define gzwrite z_gzwrite +# endif +# define inflate z_inflate +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define inflateBackInit z_inflateBackInit +# define inflateBackInit_ z_inflateBackInit_ +# define inflateCodesUsed z_inflateCodesUsed +# define inflateCopy z_inflateCopy +# define inflateEnd z_inflateEnd +# define inflateGetDictionary z_inflateGetDictionary +# define inflateGetHeader z_inflateGetHeader +# define inflateInit z_inflateInit +# define inflateInit2 z_inflateInit2 +# define inflateInit2_ z_inflateInit2_ +# define inflateInit_ z_inflateInit_ +# define inflateMark z_inflateMark +# define inflatePrime z_inflatePrime +# define inflateReset z_inflateReset +# define inflateReset2 z_inflateReset2 +# define inflateResetKeep z_inflateResetKeep +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateUndermine z_inflateUndermine +# define inflateValidate z_inflateValidate +# define inflate_copyright z_inflate_copyright +# define inflate_fast z_inflate_fast +# define inflate_table z_inflate_table +# ifndef Z_SOLO +# define uncompress z_uncompress +# define uncompress2 z_uncompress2 +# endif +# define zError z_zError +# ifndef Z_SOLO +# define zcalloc z_zcalloc +# define zcfree z_zcfree +# endif +# define zlibCompileFlags z_zlibCompileFlags +# define zlibVersion z_zlibVersion + +/* all zlib typedefs in zlib.h and zconf.h */ +# define Byte z_Byte +# define Bytef z_Bytef +# define alloc_func z_alloc_func +# define charf z_charf +# define free_func z_free_func +# ifndef Z_SOLO +# define gzFile z_gzFile +# endif +# define gz_header z_gz_header +# define gz_headerp z_gz_headerp +# define in_func z_in_func +# define intf z_intf +# define out_func z_out_func +# define uInt z_uInt +# define uIntf z_uIntf +# define uLong z_uLong +# define uLongf z_uLongf +# define voidp z_voidp +# define voidpc z_voidpc +# define voidpf z_voidpf + +/* all zlib structs in zlib.h and zconf.h */ +# define gz_header_s z_gz_header_s +# define internal_state z_internal_state + +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +#if defined(ZLIB_CONST) && !defined(z_const) +# define z_const const +#else +# define z_const +#endif + +#ifdef Z_SOLO + typedef unsigned long z_size_t; +#else +# define z_longlong long long +# if defined(NO_SIZE_T) + typedef unsigned NO_SIZE_T z_size_t; +# elif defined(STDC) +# include + typedef size_t z_size_t; +# else + typedef unsigned long z_size_t; +# endif +# undef z_longlong +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus about 7 kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +#ifndef Z_ARG /* function prototypes for stdarg */ +# if defined(STDC) || defined(Z_HAVE_STDARG_H) +# define Z_ARG(args) args +# else +# define Z_ARG(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC) +# include +# if (UINT_MAX == 0xffffffffUL) +# define Z_U4 unsigned +# elif (ULONG_MAX == 0xffffffffUL) +# define Z_U4 unsigned long +# elif (USHRT_MAX == 0xffffffffUL) +# define Z_U4 unsigned short +# endif +#endif + +#ifdef Z_U4 + typedef Z_U4 z_crc_t; +#else + typedef unsigned long z_crc_t; +#endif + +#ifdef HAVE_UNISTD_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_UNISTD_H +#endif + +#ifdef HAVE_STDARG_H /* may be set to #if 1 by ./configure */ +# define Z_HAVE_STDARG_H +#endif + +#ifdef STDC +# ifndef Z_SOLO +# include /* for off_t */ +# endif +#endif + +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +# include /* for va_list */ +# endif +#endif + +#ifdef _WIN32 +# ifndef Z_SOLO +# include /* for wchar_t */ +# endif +#endif + +/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and + * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even + * though the former does not conform to the LFS document), but considering + * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as + * equivalently requesting no 64-bit operations + */ +#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1 +# undef _LARGEFILE64_SOURCE +#endif + +#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H) +# define Z_HAVE_UNISTD_H +#endif +#ifndef Z_SOLO +# if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE) +# include /* for SEEK_*, off_t, and _LFS64_LARGEFILE */ +# ifdef VMS +# include /* for off_t */ +# endif +# ifndef z_off_t +# define z_off_t off_t +# endif +# endif +#endif + +#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0 +# define Z_LFS64 +#endif + +#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64) +# define Z_LARGE64 +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64) +# define Z_WANT64 +#endif + +#if !defined(SEEK_SET) && !defined(Z_SOLO) +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif + +#ifndef z_off_t +# define z_off_t long +#endif + +#if !defined(_WIN32) && defined(Z_LARGE64) +# define z_off64_t off64_t +#else +# if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO) +# define z_off64_t __int64 +# else +# define z_off64_t z_off_t +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) + #pragma map(deflateInit_,"DEIN") + #pragma map(deflateInit2_,"DEIN2") + #pragma map(deflateEnd,"DEEND") + #pragma map(deflateBound,"DEBND") + #pragma map(inflateInit_,"ININ") + #pragma map(inflateInit2_,"ININ2") + #pragma map(inflateEnd,"INEND") + #pragma map(inflateSync,"INSY") + #pragma map(inflateSetDictionary,"INSEDI") + #pragma map(compressBound,"CMBND") + #pragma map(inflate_table,"INTABL") + #pragma map(inflate_fast,"INFA") + #pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/zlib.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/zlib.h new file mode 100644 index 0000000000000000000000000000000000000000..5fa98b5dc407d8e429d3b3e9911e86e77732f2e1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/zlib.h @@ -0,0 +1,1912 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.11" +#define ZLIB_VERNUM 0x12b0 +#define ZLIB_VER_MAJOR 1 +#define ZLIB_VER_MINOR 2 +#define ZLIB_VER_REVISION 11 +#define ZLIB_VER_SUBREVISION 0 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed data. + This version of the library supports only one compression method (deflation) + but other algorithms will be added later and will have the same stream + interface. + + Compression can be done in a single step if the buffers are large enough, + or can be done by repeated calls of the compression function. In the latter + case, the application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip and raw deflate streams in + memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never crash + even in the case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + z_const Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total number of input bytes read so far */ + + Bytef *next_out; /* next output byte will go here */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total number of bytes output so far */ + + z_const char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text + for deflate, or the decoding state for inflate */ + uLong adler; /* Adler-32 or CRC-32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has dropped + to zero. It must update next_out and avail_out when avail_out has dropped + to zero. The application must initialize zalloc, zfree and opaque before + calling the init function. All other fields are set by the compression + library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. In that case, zlib is thread-safe. When zalloc and zfree are + Z_NULL on entry to the initialization function, they are set to internal + routines that use the standard library functions malloc() and free(). + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this if + the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, pointers + returned by zalloc for objects of exactly 65536 bytes *must* have their + offset normalized to zero. The default allocation function provided by this + library ensures this (see zutil.c). To reduce memory requirements and avoid + any allocation of 64K objects, at the expense of compression ratio, compile + the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or progress + reports. After compression, total_in holds the total size of the + uncompressed data and may be saved for use by the decompressor (particularly + if the decompressor wants to decompress everything in a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field for deflate() */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is not + compatible with the zlib.h header file used by the application. This check + is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. If + zalloc and zfree are set to Z_NULL, deflateInit updates them to use default + allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at all + (the input data is simply copied a block at a time). Z_DEFAULT_COMPRESSION + requests a default compromise between speed and compression (currently + equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if level is not a valid compression level, or + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). msg is set to null + if there is no error message. deflateInit does not perform any compression: + this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary. Some output may be provided even if + flush is zero. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating avail_in or avail_out accordingly; avail_out should + never be zero before the call. The application can consume the compressed + output when it wants, for example when the output buffer is full (avail_out + == 0), or after each call of deflate(). If deflate returns Z_OK and with + zero avail_out, it must be called again after making room in the output + buffer because there might be more output pending. See deflatePending(), + which can be used if desired to determine whether or not there is more ouput + in that case. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumulate before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In + particular avail_in is zero after the call if enough output space has been + provided before the call.) Flushing may degrade compression for some + compression algorithms and so it should be used only when necessary. This + completes the current deflate block and follows it with an empty stored block + that is three bits plus filler bits to the next byte, followed by four bytes + (00 00 ff ff). + + If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the + output buffer, but the output is not aligned to a byte boundary. All of the + input data so far will be available to the decompressor, as for Z_SYNC_FLUSH. + This completes the current deflate block and follows it with an empty fixed + codes block that is 10 bits long. This assures that enough bytes are output + in order for the decompressor to finish the block before the empty fixed + codes block. + + If flush is set to Z_BLOCK, a deflate block is completed and emitted, as + for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to + seven bits of the current block are held to be written as the next byte after + the next deflate block is completed. In this case, the decompressor may not + be provided enough bits at this point in order to complete decompression of + the data provided so far to the compressor. It may need to wait for the next + block to be emitted. This is for advanced applications that need to control + the emission of deflate blocks. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there was + enough output space. If deflate returns with Z_OK or Z_BUF_ERROR, this + function must be called again with Z_FINISH and more output space (updated + avail_out) but no more input data, until it returns with Z_STREAM_END or an + error. After deflate has returned Z_STREAM_END, the only possible operations + on the stream are deflateReset or deflateEnd. + + Z_FINISH can be used in the first deflate call after deflateInit if all the + compression is to be done in a single step. In order to complete in one + call, avail_out must be at least the value returned by deflateBound (see + below). Then deflate is guaranteed to return Z_STREAM_END. If not enough + output space is provided, deflate will not return Z_STREAM_END, and it must + be called again as described above. + + deflate() sets strm->adler to the Adler-32 checksum of all input read + so far (that is, total_in bytes). If a gzip stream is being generated, then + strm->adler will be the CRC-32 checksum of the input read so far. (See + deflateInit2 below.) + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). If in doubt, the data is + considered binary. This field is only for information purposes and does not + affect the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was Z_NULL or the state was inadvertently written over + by the application), or Z_BUF_ERROR if no progress is possible (for example + avail_in or avail_out was zero). Note that Z_BUF_ERROR is not fatal, and + deflate() can be called again with more input and more output space to + continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, msg + may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. In the current version of inflate, the provided input is not + read or consumed. The allocation of a sliding window will be deferred to + the first call of inflate (if the decompression does not complete on the + first call). If zalloc and zfree are set to Z_NULL, inflateInit updates + them to use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit does not perform any decompression. + Actual decompression will be done by inflate(). So next_in, and avail_in, + next_out, and avail_out are unused and unchanged. The current + implementation of inflateInit() does not process any header information -- + that is deferred until inflate() is called. +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), then next_in and avail_in are updated + accordingly, and processing will resume at this point for the next call of + inflate(). + + - Generate more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there is + no more input data or no more space in the output buffer (see below about + the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming more + output, and updating the next_* and avail_* values accordingly. If the + caller of inflate() does not provide both available input and available + output space, it is possible that there will be no progress made. The + application can consume the uncompressed output when it wants, for example + when the output buffer is full (avail_out == 0), or after each call of + inflate(). If inflate returns Z_OK and with zero avail_out, it must be + called again after making room in the output buffer because there might be + more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH, + Z_BLOCK, or Z_TREES. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() + stop if and when it gets to the next deflate block boundary. When decoding + the zlib or gzip format, this will cause inflate() to return immediately + after the header and before the first block. When doing a raw inflate, + inflate() will go ahead and process the first block, and will return when it + gets to the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + To assist in this, on return inflate() always sets strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 if + inflate() is currently decoding the last block in the deflate stream, plus + 128 if inflate() returned immediately after decoding an end-of-block code or + decoding the complete header up to just before the first byte of the deflate + stream. The end-of-block will not be indicated until all of the uncompressed + data from that block has been written to strm->next_out. The number of + unused bits may in general be greater than seven, except when bit 7 of + data_type is set, in which case the number of unused bits will be less than + eight. data_type is set as noted here every time inflate() returns for all + flush options, and so can be used to determine the amount of currently + consumed input in bits. + + The Z_TREES option behaves as Z_BLOCK does, but it also returns when the + end of each deflate block header is reached, before any actual data in that + block is decoded. This allows the caller to determine the length of the + deflate block header for later use in random access within a deflate block. + 256 is added to the value of strm->data_type when inflate() returns + immediately after reaching the end of the deflate block header. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step (a + single call of inflate), the parameter flush should be set to Z_FINISH. In + this case all pending input is processed and all pending output is flushed; + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the Adler-32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed Adler-32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() can decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically, if requested when + initializing with inflateInit2(). Any information contained in the gzip + header is not retained unless inflateGetHeader() is used. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + produced so far. The CRC-32 is checked against the gzip trailer, as is the + uncompressed length, modulo 2^32. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value, in which case strm->msg points to a string with a more specific + error), Z_STREAM_ERROR if the stream structure was inconsistent (for example + next_in or next_out was Z_NULL, or the state was inadvertently written over + by the application), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR + if no progress was possible or if there was not enough room in the output + buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may + then call inflateSync() to look for a good compression block if a partial + recovery of the data is to be attempted. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any pending + output. + + inflateEnd returns Z_OK if success, or Z_STREAM_ERROR if the stream state + was inconsistent. +*/ + + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by the + caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + For the current implementation of deflate(), a windowBits value of 8 (a + window size of 256 bytes) is not supported. As a result, a request for 8 + will result in 9 (a 512-byte window). In that case, providing 8 to + inflateInit2() will result in an error when the zlib header with 9 is + checked against the initialization of inflate(). The remedy is to not use 8 + with deflateInit2() with this initialization, or at least in that case use 9 + with inflateInit2(). + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute a check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), no + header crc, and the operating system will be set to the appropriate value, + if the operating system was determined at compile time. If a gzip stream is + being written, strm->adler is a CRC-32 instead of an Adler-32. + + For raw deflate or gzip encoding, a request for a 256-byte window is + rejected as invalid, since only the zlib header provides a means of + transmitting the window size to the decompressor. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but is + slow and reduces compression ratio; memLevel=9 uses maximum memory for + optimal speed. The default value is 8. See zconf.h for total memory usage + as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as + fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data. The + strategy parameter only affects the compression ratio but not the + correctness of the compressed output even if it is not set appropriately. + Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler + decoder for special applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid + method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is + incompatible with the version assumed by the caller (ZLIB_VERSION). msg is + set to null if there is no error message. deflateInit2 does not perform any + compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size + provided in deflateInit or deflateInit2. Thus the strings most likely to be + useful should be put at the end of the dictionary, not at the front. In + addition, the current implementation of deflate will use at most the window + size minus 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the Adler-32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The Adler-32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + Adler-32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by deflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If deflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + deflateGetDictionary() may return a length less than the window size, even + when more than the window size in input has been provided. It may return up + to 258 bytes less in that case, due to how zlib's implementation of deflate + manages the sliding window and lookahead for matches, where matches can be + up to 258 bytes long. If the application needs the last window-size bytes of + input, then that would need to be saved by the application outside of zlib. + + deflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and can + consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, but + does not free and reallocate the internal compression state. The stream + will leave the compression level and any other attributes that may have been + set unchanged. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2(). This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different strategy. + If the compression approach (which is a function of the level) or the + strategy is changed, and if any input has been consumed in a previous + deflate() call, then the input available so far is compressed with the old + level and strategy using deflate(strm, Z_BLOCK). There are three approaches + for the compression levels 0, 1..3, and 4..9 respectively. The new level + and strategy will take effect at the next call of deflate(). + + If a deflate(strm, Z_BLOCK) is performed by deflateParams(), and it does + not have enough output space to complete, then the parameter change will not + take effect. In this case, deflateParams() can be called again with the + same parameters and more output space to try again. + + In order to assure a change in the parameters on the first try, the + deflate stream should be flushed using deflate() with Z_BLOCK or other flush + request until strm.avail_out is not zero, before calling deflateParams(). + Then no more input data should be provided before the deflateParams() call. + If this is done, the old level and strategy will be applied to the data + compressed before deflateParams(), and the new level and strategy will be + applied to the the data compressed after deflateParams(). + + deflateParams returns Z_OK on success, Z_STREAM_ERROR if the source stream + state was inconsistent or if a parameter was invalid, or Z_BUF_ERROR if + there was not enough output space to complete the compression of the + available input data before a change in the strategy or approach. Note that + in the case of a Z_BUF_ERROR, the parameters are not changed. A return + value of Z_BUF_ERROR is not fatal, in which case deflateParams() can be + retried with more output space. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() or + deflateInit2(), and after deflateSetHeader(), if used. This would be used + to allocate an output buffer for deflation in a single pass, and so would be + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the bits + leftover from a previous deflate stream when appending to it. As such, this + function can only be used for raw deflate, and must be used before the first + deflate() call after a deflateInit2() or deflateReset(). bits must be less + than or equal to 16, and that many of the least significant bits of value + will be inserted in the output. + + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be zero to request that inflate use the window size in + the zlib header of the compressed stream. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an Adler-32 or a CRC-32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is a + CRC-32 instead of an Adler-32. Unlike the gunzip utility and gzread() (see + below), inflate() will not automatically decode concatenated gzip streams. + inflate() will return Z_STREAM_END at the end of the gzip stream. The state + would need to be reset to continue decoding a subsequent gzip stream. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller, or Z_STREAM_ERROR if the parameters are + invalid, such as a null pointer to the structure. msg is set to null if + there is no error message. inflateInit2 does not perform any decompression + apart from possibly reading the zlib header if present: actual decompression + will be done by inflate(). (So next_in and avail_in may be modified, but + next_out and avail_out are unused and unchanged.) The current implementation + of inflateInit2() does not process any header information -- that is + deferred until inflate() is called. +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the Adler-32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect Adler-32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being Z_NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate the internal decompression state. The + stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL). +*/ + +ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm, + int windowBits)); +/* + This function is the same as inflateReset, but it also permits changing + the wrap and window size requests. The windowBits parameter is interpreted + the same as it is for inflateInit2. If the window size is changed, then the + memory allocated for the window is freed, and the window will be reallocated + by inflate() if needed. + + inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being Z_NULL), or if + the windowBits parameter is invalid. +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + If bits is negative, then the input stream bit buffer is emptied. Then + inflatePrime() can be called again to put bits in the buffer. This is used + to clear out bits leftover after feeding inflate a block description prior + to feeding inflate codes. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm)); +/* + This function returns two values, one in the lower 16 bits of the return + value, and the other in the remaining upper bits, obtained by shifting the + return value down 16 bits. If the upper value is -1 and the lower value is + zero, then inflate() is currently decoding information outside of a block. + If the upper value is -1 and the lower value is non-zero, then inflate is in + the middle of a stored block, with the lower value equaling the number of + bytes from the input remaining to copy. If the upper value is not -1, then + it is the number of bits back from the current bit position in the input of + the code (literal or length/distance pair) currently being processed. In + that case the lower value is the number of bytes already emitted for that + code. + + A code is being processed if inflate is waiting for more input to complete + decoding of the code, or if it has completed decoding but is waiting for + more output space to write the literal or match data. + + inflateMark() is used to mark locations in the input data for random + access, which may be at bit positions, and to note those cases where the + output of a code may span boundaries of random access blocks. The current + location in the input stream can be determined from avail_in and data_type + as noted in the description for the Z_BLOCK flush parameter for inflate. + + inflateMark returns the value noted above, or -65536 if the provided + source stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK or Z_TREES can be + used to force inflate() to return immediately after header processing is + complete and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When any + of extra, name, or comment are not Z_NULL and the respective field is not + present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the parameters are invalid, Z_MEM_ERROR if the internal state could not be + allocated, or Z_VERSION_ERROR if the version of the library does not match + the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free the + allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects only + the raw deflate stream to decompress. This is different from the default + behavior of inflate(), which expects a zlib header and trailer around the + deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero -- buf is ignored in that + case -- and inflateBack() will return a buffer error. inflateBack() will + call out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. + out() should return zero on success, or non-zero on failure. If out() + returns non-zero, inflateBack() will return with an error. Neither in() nor + out() are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format error + in the deflate stream (in which case strm->msg is set to indicate the nature + of the error), or Z_STREAM_ERROR if the stream was not properly initialized. + In the case of Z_BUF_ERROR, an input or output error can be distinguished + using strm->next_in which will be Z_NULL only if in() returned an error. If + strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning + non-zero. (in() will always be called before out(), so strm->next_in is + assured to be defined if out() returns non-zero.) Note that inflateBack() + cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: ZLIB_DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + +#ifndef Z_SOLO + + /* utility functions */ + +/* + The following utility functions are implemented on top of the basic + stream-oriented functions. To simplify the interface, some default options + are assumed (compression level and memory usage, standard memory allocation + functions). The source code of these utility functions can be modified if + you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. compress() is equivalent to compress2() with a level + parameter of Z_DEFAULT_COMPRESSION. + + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed data. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before a + compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total size + of the destination buffer, which must be large enough to hold the entire + uncompressed data. (The size of the uncompressed data must have been saved + previously by the compressor and transmitted to the decompressor by some + mechanism outside the scope of this compression library.) Upon exit, destLen + is the actual size of the uncompressed data. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. +*/ + +ZEXTERN int ZEXPORT uncompress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong *sourceLen)); +/* + Same as uncompress, except that sourceLen is a pointer, where the + length of the source is *sourceLen. On return, *sourceLen is the number of + source bytes consumed. +*/ + + /* gzip file access functions */ + +/* + This library supports reading and writing files in gzip (.gz) format with + an interface similar to that of stdio, using the functions that start with + "gz". The gzip format is different from the zlib format. gzip is a gzip + wrapper, documented in RFC 1952, wrapped around a deflate stream. +*/ + +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ + +/* +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); + + Opens a gzip (.gz) file for reading or writing. The mode parameter is as + in fopen ("rb" or "wb") but can also include a compression level ("wb9") or + a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only + compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' + for fixed code compression as in "wb9F". (See the description of + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. + + gzopen returns NULL if the file could not be opened, if there was + insufficient memory to allocate the gzFile state, or if an invalid mode was + specified (an 'r', 'w', or 'a' was not provided, or '+' was provided). + errno can be checked to determine if the reason gzopen failed was that the + file could not be opened. +*/ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen associates a gzFile with the file descriptor fd. File descriptors + are obtained from calls like open, dup, creat, pipe or fileno (if the file + has been previously opened with fopen). The mode parameter is as in gzopen. + + The next call of gzclose on the returned gzFile will also close the file + descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor + fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, + mode);. The duplicated descriptor should be saved to avoid a leak, since + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. + + gzdopen returns NULL if there was insufficient memory to allocate the + gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not + provided, or '+' was provided), or if fd is -1. The file descriptor is not + used until the next gz* read, write, seek, or close operation, so gzdopen + will not detect if fd is invalid (unless fd is -1). +*/ + +ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size)); +/* + Set the internal buffer size used by this library's functions. The + default buffer size is 8192 bytes. This function must be called after + gzopen() or gzdopen(), and before any other calls that read or write the + file. The buffer memory allocation is always deferred to the first read or + write. Three times that size in buffer space is allocated. A larger buffer + size of, for example, 64K or 128K bytes will noticeably increase the speed + of decompression (reading). + + The new buffer size also affects the maximum length for gzprintf(). + + gzbuffer() returns 0 on success, or -1 on failure, such as being called + too late. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. Previously provided + data is flushed before the parameter change. + + gzsetparams returns Z_OK if success, Z_STREAM_ERROR if the file was not + opened for writing, Z_ERRNO if there is an error writing the flushed data, + or Z_MEM_ERROR if there is a memory allocation error. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. If + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. + + After reaching the end of a gzip stream in the input, gzread will continue + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. + + gzread returns the number of uncompressed bytes actually read, less than + len for end of file, or -1 for error. If len is too large to fit in an int, + then nothing is read, -1 is returned, and the error state is set to + Z_STREAM_ERROR. +*/ + +ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems, + gzFile file)); +/* + Read up to nitems items of size size from file to buf, otherwise operating + as gzread() does. This duplicates the interface of stdio's fread(), with + size_t request and return types. If the library defines size_t, then + z_size_t is identical to size_t. If not, then z_size_t is an unsigned + integer type that can contain a pointer. + + gzfread() returns the number of full items read of size size, or zero if + the end of the file was reached and a full item could not be read, or if + there was an error. gzerror() must be consulted if zero is returned in + order to determine if there was an error. If the multiplication of size and + nitems overflows, i.e. the product does not fit in a z_size_t, then nothing + is read, zero is returned, and the error state is set to Z_STREAM_ERROR. + + In the event that the end of file is reached and only a partial item is + available at the end, i.e. the remaining uncompressed data length is not a + multiple of size, then the final partial item is nevetheless read into buf + and the end-of-file flag is set. The length of the partial item read is not + provided, but could be inferred from the result of gztell(). This behavior + is the same as the behavior of fread() implementations in common libraries, + but it prevents the direct use of gzfread() to read a concurrently written + file, reseting and retrying on end-of-file, when size is not 1. +*/ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes written or 0 in case of + error. +*/ + +ZEXTERN z_size_t ZEXPORT gzfwrite OF((voidpc buf, z_size_t size, + z_size_t nitems, gzFile file)); +/* + gzfwrite() writes nitems items of size size from buf to file, duplicating + the interface of stdio's fwrite(), with size_t request and return types. If + the library defines size_t, then z_size_t is identical to size_t. If not, + then z_size_t is an unsigned integer type that can contain a pointer. + + gzfwrite() returns the number of full items written of size size, or zero + if there was an error. If the multiplication of size and nitems overflows, + i.e. the product does not fit in a z_size_t, then nothing is written, zero + is returned, and the error state is set to Z_STREAM_ERROR. +*/ + +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the arguments to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written, or a negative zlib error code in case + of error. The number of uncompressed bytes written is limited to 8191, or + one less than the buffer size given to gzbuffer(). The caller should assure + that this limit is not exceeded. If it is exceeded, then gzprintf() will + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. + This can be determined using zlibCompileFlags(). +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or a + newline character is read and transferred to buf, or an end-of-file + condition is encountered. If any characters are read or if len == 1, the + string is terminated with a null character. If no characters are read due + to an end-of-file or len < 1, then the buffer is left untouched. + + gzgets returns buf which is a null-terminated string, or it returns NULL + for end-of-file or in case of error. If there was an error, the contents at + buf are indeterminate. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. gzputc + returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte or -1 + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read as the first character + on the next read. At least one character of push-back is allowed. + gzungetc() returns the character pushed, or -1 on failure. gzungetc() will + fail if c is -1, and may fail if a character has been pushed but not read + yet. If gzungetc is used immediately after gzopen or gzdopen, at least the + output buffer size of pushed characters is allowed. (See gzbuffer above.) + The pushed character will be discarded if the stream is repositioned with + gzseek() or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter flush + is as in the deflate() function. The return value is the zlib error number + (see function gzerror below). gzflush is only permitted when writing. + + If the flush parameter is Z_FINISH, the remaining data is written and the + gzip stream is completed in the output. If gzwrite() is called again, a new + gzip stream will be started in the output. gzread() is able to read such + concatenated gzip streams. + + gzflush should be called only when strictly necessary because it will + degrade compression if called too often. +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); + + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); + + Returns the starting position for the next gzread or gzwrite on the given + compressed file. This position represents a number of bytes in the + uncompressed data stream, and is zero when starting, even if appending or + reading a gzip stream from the middle of a file using gzdopen(). + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +/* +ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file)); + + Returns the current offset in the file being read or written. This offset + includes the count of bytes that precede the gzip stream, for example when + appending or when using gzdopen() for reading. When reading, the offset + does not include as yet unused buffered input. This information can be used + for a progress indicator. On error, gzoffset() returns -1. +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns true (1) if the end-of-file indicator has been set while reading, + false (0) otherwise. Note that the end-of-file indicator is set only if the + read tried to go past the end of the input, but came up short. Therefore, + just like feof(), gzeof() may return false even if there is no more data to + read, in the event that the last read request was for the exact number of + bytes remaining in the input file. This will happen if the input file size + is an exact multiple of the buffer size. + + If gzeof() returns true, then the read functions will return no more data, + unless the end-of-file indicator is reset by gzclearerr() and the input file + has grown since the previous end of file was detected. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns true (1) if file is being copied directly while reading, or false + (0) if file is a gzip stream being decompressed. + + If the input file is empty, gzdirect() will return true, since the input + does not contain a gzip stream. + + If gzdirect() is used immediately after gzopen() or gzdopen() it will + cause buffers to be allocated to allow reading the file to determine if it + is a gzip file. Therefore if gzbuffer() is used, it should be called before + gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file and + deallocates the (de)compression state. Note that once file is closed, you + cannot call gzerror with file, since its structures have been deallocated. + gzclose must not be called more than once on the same file, just as free + must not be called more than once on the same allocation. + + gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. +*/ + +ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); +ZEXTERN int ZEXPORT gzclose_w OF((gzFile file)); +/* + Same as gzclose(), but gzclose_r() is only for use when reading, and + gzclose_w() is only for use when writing or appending. The advantage to + using these instead of gzclose() is that they avoid linking in zlib + compression or decompression code that is not used when only reading or only + writing respectively. If gzclose() is used, then both compression and + decompression code will be included the application when linking to a static + zlib library. +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the given + compressed file. errnum is set to zlib error number. If an error occurred + in the file system and not in the compression library, errnum is set to + Z_ERRNO and the application may consult errno to get the exact error code. + + The application must not modify the returned string. Future calls to + this function may invalidate the previously returned string. If file is + closed, then the string previously returned by gzerror will no longer be + available. + + gzerror() should be used to distinguish errors from end-of-file for those + functions above that do not distinguish those cases in their return values. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + +#endif /* !Z_SOLO */ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the compression + library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is Z_NULL, this function returns the + required initial value for the checksum. + + An Adler-32 checksum is almost as reliable as a CRC-32 but can be computed + much faster. + + Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as adler32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); + + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is Z_NULL, this function returns the required + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_z OF((uLong adler, const Bytef *buf, + z_size_t len)); +/* + Same as crc32(), but with a size_t length. +*/ + +/* +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#ifdef Z_PREFIX_SET +# define z_deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define z_inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define z_inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#else +# define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) +# define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) +# define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) +# define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, (int)sizeof(z_stream)) +#endif + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : (gzgetc)(g)) +#endif + +/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or + * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if + * both are true, the application gets the *64 functions, and the regular + * functions are changed to 64 bits) -- in case these are set on systems + * without large file support, _LFS64_LARGEFILE must also be true + */ +#ifdef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); + ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); +#endif + +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 + ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# endif +#else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); +#endif + +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + +/* undocumented functions */ +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); +ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateValidate OF((z_streamp, int)); +ZEXTERN unsigned long ZEXPORT inflateCodesUsed OF ((z_streamp)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/zutil.c b/3rdparty/pyinstaller-4.3/bootloader/zlib/zutil.c new file mode 100644 index 0000000000000000000000000000000000000000..85fea6d29849f71a569df8ad9384ea5e0ea983d8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/zutil.c @@ -0,0 +1,325 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2017 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" +#ifndef Z_SOLO +# include "gzguts.h" +#endif + +z_const char * const z_errmsg[10] = { + (z_const char *)"need dictionary", /* Z_NEED_DICT 2 */ + (z_const char *)"stream end", /* Z_STREAM_END 1 */ + (z_const char *)"", /* Z_OK 0 */ + (z_const char *)"file error", /* Z_ERRNO (-1) */ + (z_const char *)"stream error", /* Z_STREAM_ERROR (-2) */ + (z_const char *)"data error", /* Z_DATA_ERROR (-3) */ + (z_const char *)"insufficient memory", /* Z_MEM_ERROR (-4) */ + (z_const char *)"buffer error", /* Z_BUF_ERROR (-5) */ + (z_const char *)"incompatible version",/* Z_VERSION_ERROR (-6) */ + (z_const char *)"" +}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch ((int)(sizeof(uInt))) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch ((int)(sizeof(uLong))) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch ((int)(sizeof(voidpf))) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch ((int)(sizeof(z_off_t))) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef ZLIB_DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef ZLIB_DEBUG +#include +# ifndef verbose +# define verbose 0 +# endif +int ZLIB_INTERNAL z_verbose = verbose; + +void ZLIB_INTERNAL z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void ZLIB_INTERNAL zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int ZLIB_INTERNAL zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void ZLIB_INTERNAL zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + +#ifndef Z_SOLO + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf; + ulg bsize = (ulg)items*size; + + (void)opaque; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + int n; + + (void)opaque; + + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf ZLIB_INTERNAL zcalloc (voidpf opaque, uInt items, uInt size) +{ + (void)opaque; + return _halloc((long)items, size); +} + +void ZLIB_INTERNAL zcfree (voidpf opaque, voidpf ptr) +{ + (void)opaque; + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf ZLIB_INTERNAL zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + (void)opaque; + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void ZLIB_INTERNAL zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + (void)opaque; + free(ptr); +} + +#endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff --git a/3rdparty/pyinstaller-4.3/bootloader/zlib/zutil.h b/3rdparty/pyinstaller-4.3/bootloader/zlib/zutil.h new file mode 100644 index 0000000000000000000000000000000000000000..aecbe7433fe2a5dc82edc158c1fe9e570461988c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/bootloader/zlib/zutil.h @@ -0,0 +1,271 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2016 Jean-loup Gailly, Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#ifdef HAVE_HIDDEN +# define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) +#else +# define ZLIB_INTERNAL +#endif + +#include "zlib.h" + +#if defined(STDC) && !defined(Z_SOLO) +# if !(defined(_WIN32_WCE) && defined(_MSC_VER)) +# include +# endif +# include +# include +#endif + +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + +#ifndef local +# define local static +#endif +/* since "static" is used to mean two completely different things in C, we + define "local" for the non-static meaning of "static", for readability + (compile with -Dlocal if your debugger can't find static symbols) */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 1 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 2 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#ifdef __370__ +# if __TARGET_LIB__ < 0x20000000 +# define OS_CODE 4 +# elif __TARGET_LIB__ < 0x40000000 +# define OS_CODE 11 +# else +# define OS_CODE 8 +# endif +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 5 +#endif + +#ifdef OS2 +# define OS_CODE 6 +# if defined(M_I86) && !defined(Z_SOLO) +# include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 7 +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +# endif +#endif + +#ifdef __acorn +# define OS_CODE 13 +#endif + +#if defined(WIN32) && !defined(__CYGWIN__) +# define OS_CODE 10 +#endif + +#ifdef _BEOS_ +# define OS_CODE 16 +#endif + +#ifdef __TOS_OS400__ +# define OS_CODE 18 +#endif + +#ifdef __APPLE__ +# define OS_CODE 19 +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + +#if defined(__BORLANDC__) && !defined(MSDOS) + #pragma warn -8004 + #pragma warn -8008 + #pragma warn -8066 +#endif + +/* provide prototypes for these when building zlib without LFS */ +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 3 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(pyr) || defined(Z_SOLO) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef ZLIB_DEBUG +# include + extern int ZLIB_INTERNAL z_verbose; + extern void ZLIB_INTERNAL z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +#endif /* ZUTIL_H */ diff --git a/3rdparty/pyinstaller-4.3/doc/CHANGES-1.rst b/3rdparty/pyinstaller-4.3/doc/CHANGES-1.rst new file mode 100644 index 0000000000000000000000000000000000000000..16aa3809ab0c7b3ccb444329c1e70cbaa56944ce --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/CHANGES-1.rst @@ -0,0 +1,233 @@ +Changelog for PyInstaller 1.x +====================================================== + +1.5.1 (2011-08-01) +------------------ + +- New default PyInstaller icon for generated executables on Windows. +- Add support for Python built with --enable-shared on Mac OSX. +- Add requirements section to documentation. + + +- Documentation is now generated by rst2html and rst2pdf. +- Fix wrong path separators for bootloader-file on Windows +- Add workaround for incorrect platform.system() on some Python Windows + installation where this function returns 'Microsoft' instead 'Windows'. +- Fix --windowed option for Mac OSX where a console executable was + created every time even with this option. +- Mention dependency on otool, ldd and objdump in documentation. +- Fix typo preventing detection of DLL libraries loaded by ctypes module. + + +1.5 (2011-05-05) +---------------- + +- Full support for Python 2.7. +- Full support for Python 2.6 on Windows. No manual redistribution + of DLLs, CRT, manifest, etc. is required: PyInstaller is able to + bundle all required dependencies (thanks to Florian Hoech). +- Added support for Windows 64-bit (thanks to Martin Zibricky). +- Added binary bootloaders for Linux (32-bit and 64-bit, using LSB), + and Darwin (32-bit). This means that PyInstaller users on this + platform don't need to compile the bootloader themselves anymore + (thanks to Martin Zibricky and Lorenzo Mancini). + + +- Rewritten the build system for the bootloader using waf (thanks + to Martin Zibricky) +- Correctly detect Python unified binary under Mac OSX, and bail out + if the unsupported 64-bit version is used (thanks to Nathan Weston). +- Fix TkInter support under Mac OSX (thanks to Lorenzo Mancini). +- Improve bundle creation under Mac OSX and correctly support also + one-dir builds within bundles (thanks to Lorenzo Mancini). +- Fix spurious KeyError when using dbhash +- Fix import of nested packages made from Pyrex-generated files. +- PyInstaller is now able to follow dependencies of binary extensions + (.pyd/.so) compressed within .egg-files. +- Add import hook for PyTables. +- Add missing import hook for QtWebKit. +- Add import hook for pywinauto. +- Add import hook for reportlab (thanks Nevar). +- Improve matplotlib import hook (for Mac OSX). +- Improve Django import hooks. +- Improve compatibility across multiple Linux distributions by + being more careful on which libraries are included/excluded in + the package. +- Improve compatibility with older Python versions (Python 2.2+). +- Fix double-bouncing-icon bug on Mac OSX. Now windowed applications + correctly start on Mac OSX showing a single bouncing icon. +- Fix weird "missing symbol" errors under Mac OSX (thanks to Isaac + Wagner). + + +1.4 (2010-03-22) +---------------- + +- Fully support up to Python 2.6 on Linux/Mac and Python 2.5 + on Windows. +- Preliminar Mac OSX support: both one-file and one-dir is supported; + for non-console applications, a bundle can be created. Thanks + to many people that worked on this across several months (Daniele + Zannotti, Matteo Bertini, Lorenzo Mancini). +- Improved Linux support: generated executables are fatter but now + should now run on many different Linux distributions (thanks to David + Mugnai). +- Add support for specifying data files in import hooks. PyInstaller + can now automatically bundle all data files or plugins required + for a certain 3rd-party package. +- Add intelligent support for ctypes: PyInstaller is now able to + track all places in the source code where ctypes is used and + automatically bundle dynamic libraries accessed through ctypes. + (Thanks to Lorenzo Mancini for submitting this). This is very + useful when using ctypes with custom-made dynamic libraries. +- Executables built with PyInstaller under Windows can now be digitally + signed. +- Add support for absolute imports in Python 2.5+ (thanks to Arve + Knudsen). +- Add support for relative imports in Python 2.5+. +- Add support for cross-compilation: PyInstaller is now able to + build Windows executables when running under Linux. See documentation + for more details. +- Add support for .egg files: PyInstaller is now able to look for + dependencies within .egg files, bundle them and make them available + at runtime with all the standard features (entry-points, etc.). +- Add partial support for .egg directories: PyInstaller will treat them + as normal packages and thus it will not bundle metadata. +- Under Linux/Mac, it is now possible to build an executable even when + a system packages does not have .pyc or .pyo files available and the + system-directory can be written only by root. PyInstaller will in + fact generate the required .pyc/.pyo files on-the-fly within a + build-temporary directory. +- Add automatic import hooks for many third-party packages, including: + + - PyQt4 (thanks to Pascal Veret), with complete plugin support. + - pyodbc (thanks to Don Dwiggins) + - cElementTree (both native version and Python 2.5 version) + - lxml + - SQLAlchemy (thanks to Greg Copeland) + - email in Python 2.5 (though it does not support the old-style + Python 2.4 syntax with Python 2.5) + - gadfly + - PyQWt5 + - mako + - Improved PyGTK (thanks to Marco Bonifazi and foxx). + - paste (thanks to Jamie Kirkpatrick) + - matplotlib + +- Add fix for the very annoying "MSVCRT71 could not be extracted" bug, + which was caused by the DLL being packaged twice (thanks to Idris + Aykun). +- Removed C++-style comments from the bootloader for compatibility + with the AIX compiler. +- Fix support for .py files with DOS line endings under Linux (fixes + PyOpenGL). +- Fix support for PIL when imported without top-level package ("import + Image"). +- Fix PyXML import hook under NT (thanks to Lorenzo Mancini) +- Fixed problem with PyInstaller picking up the wrong copy of optparse. +- Improve correctness of the binary cache of UPX'd/strip'd files. This + fixes problems when switching between multiple versions of the + same third-party library (like e.g. wxPython allows to do). +- Fix a stupid bug with modules importing optparse (under Linux) (thanks + to Louai Al-Khanji). +- Under Python 2.4+, if an exception is raised while importing a module + inside a package, the module is now removed from the parent's + namespace (to match the behaviour of Python itself). +- Fix random race-condition at startup of one-file packages, that was + causing this exception to be generated: "PYZ entry 'encodings' (0j) + is not a valid code object". +- Fix problem when having unicode strings among path elements. +- Fix random exception ("bad file descriptor") with "prints" in non-console + mode (actually a pythonw "bug" that's fixed in Python 3.0). +- Sometimes the temporary directory did not get removed upon program + exit, when running on Linux. +- Fixed random segfaults at startup on 64-bit platforms (like x86-64). + + +1.3 (2006-12-20) +---------------- + +- Fix bug with user-provided icons disappearing from built executables + when these were compressed with UPX. +- Fix problems with packaging of applications using PIL (that was broken + because of a bug in Python's import machinery, in recent Python + versions). Also add a workaround including Tcl/Tk with PIL unless + ImageTk is imported. +- (Windows) When used under Windows XP, packaged programs now have + the correct look & feel and follow user's themes (thanks to the manifest + file being linked within the generated executable). This is especially + useful for applications using wxPython. +- Fix a buffer overrun in the bootloader (which could lead to a crash) + when the built executable is run from within a deep directory (more than + 70-80 characters in the pathname). +- Bootstrap modules are now compressed in the executable (so that they + are not visible in plaintext by just looking at it with a hex editor). +- Fixed a regression introduced in 1.1: under Linux, the bootloader does + not depend on libpythonX.X.so anymore. + + +1.2 (2006-06-29) +---------------- + +- Fix a crash when invoking UPX with certain kinds of builds. +- Fix icon support by re-adding a resource section in the bootloader + executable. + + +1.1 (2006-02-13) +---------------- + +- (Windows) Make single-file packages not depend on MSVCRT71.DLL anymore, + even under Python 2.4. You can eventually ship your programs really as + single-file executables, even when using the newest Python version! +- Fix problem with incorrect python path detection. Now using helpers from + distutils. +- Fix problem with rare encodings introduced in newer Python versions: now all + the encodings are automatically found and included, so this problem should + be gone forever. +- Fix building of COM servers (was broken in 1.0 because of the new build + system). +- Mimic Python 2.4 behaviour with broken imports: sys.modules is cleaned up + afterwise. This allows to package SQLObject applications under Windows + with Python 2.4 and above. +- Add import hook for the following packages: + + - GTK + - PyOpenGL (tested 2.0.1.09) + - dsnpython (tested 1.3.4) + - KInterasDB (courtesy of Eugene Prigorodov) + +- Fix packaging of code using "time.strptime" under Python 2.3+. +- (Linux) Ignore linux-gate.so while calculating dependencies (fix provided + by Vikram Aggarwal). +- (Windows) With Python 2.4, setup UPX properly so to be able to compress + binaries generated with Visual Studio .NET 2003 (such as most of the + extensions). UPX 1.92+ is needed for this. + + +1.0 (2005-09-19) with respect to McMillan's Python Installer 5b5 +---------------------------------------------------------------- + +- Add support for Python 2.3 (fix packaging of codecs). +- Add support for Python 2.4 (under Windows, needed to recompiled the + bootloader with a different compiler version). +- Fix support for Python 1.5.2, should be fully functional now (required + to rewrite some parts of the string module for the bootloader). +- Fix a rare bug in extracting the dependencies of a DLL (bug in PE header + parser). +- Fix packaging of PyQt programs (needed an import hook for a hidden import). +- Fix imports calculation for modules using the "from __init__ import" syntax. +- Fix a packaging bug when a module was being import both through binary + dependency and direct import. + + +- Restyle documentation (now using docutils and reStructuredText). +- New Windows build system for automatic compilations of bootloader in all + the required flavours (using Scons) + + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/CHANGES-2.rst b/3rdparty/pyinstaller-4.3/doc/CHANGES-2.rst new file mode 100644 index 0000000000000000000000000000000000000000..bad5c0ae7328ff6552dc406e9662858a7aa50c49 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/CHANGES-2.rst @@ -0,0 +1,127 @@ +Changelog for PyInstaller 2.x +====================================================== + +2.1 (2013-09-27) +---------------- + +- Rewritten manual explaining even very basic topics. +- PyInstaller integration with setuptools (direct installation with easy_install or pip + from PYPI - https://pypi.python.org/pypi). After installation there will be available + command 'pyinstaller' for PyInstaller usage. +- (Windows) Alter --version-file resource format to allow unicode support. +- (Windows) Fix running frozen app running from paths containing foreign characters. +- (Windows) Fix running PyInstaller from paths containing foreign characters. +- (OSX) Implement --icon option for the .app bundles. +- (OSX) Add argv emulation for OpenDocument AppleEvent (see manual for details). +- Rename --buildpath to --workpath. +- Created app is put to --distpath. +- All temporary work files are now put to --workpath. +- Add option --clean to remove PyInstaller cache and temporary files. +- Add experimental support for Linux arm. +- Minimum suported Python version is 2.4. +- Add import hooks for docutils, jinja2, sphinx, pytz, idlelib, sqlite3. +- Add import hooks for IPython, Scipy, pygst, Python for .NET. +- Add import hooks for PyQt5, Bacon, raven. +- Fix django import hook to work with Django 1.4. +- Add rthook for twisted, pygst. +- Add rthook for pkg_resource. It fixes the following functions for frozen app + pkg_resources.resource_stream(), pkg_resources.resource_string(). +- Better support for pkg_resources (.egg manipulation) in frozen executables. +- Add option --runtime-hook to allow running custom code from frozen app + before loading other Python from the frozen app. This is usefull for some + specialized preprocessing just for the frozen executable. E.g. this + option can be used to set SIP api v2 for PyQt4. + + +- Fix runtime option --Wignore. +- Rename utils to lowercase: archieve_viewer.py, bindepend.py, build.py, + grab_version.py, make_comserver.py, makespec.py, set_version.py. +- (OSX) Fix missing qt_menu.nib in dist directory when using PySide. +- (OSX) Fix bootloader compatibility with Mac OS X 10.5 +- (OSX) Search libpython in DYLD_LIBRARY_PATH if libpython cannot be found. +- (OSX) Fix Python library search in virtualenv. +- Environment variable PYTHONHOME is now unset and path to python home + is set in bootloader by function Py_SetPythonHome().This overrides + sys.prefix and sys.exec_prefix for frozen application. +- Python library filename (e.g. python27.dll, libpython2.7.so.1.0, etc) + is embedded to the created exe file. Bootloader is not trying several + filenames anymore. +- Frozen executables now use PEP-302 import hooks to import frozen modules + and C extensions. (sys.meta_path) +- Drop old import machinery from iu.py. +- Drop own code to import modules from zip archives (.egg files) in frozen + executales. Native Python implementation is kept unchanged. +- Drop old crypto code. This feature was never completed. +- Drop bootloader dependency on Python headers for compilation. +- (Windows) Recompile bootloaders with VS2008 to ensure win2k compatibility. +- (Windows) Use 8.3 filenames for homepath/temppath. +- Add prefix LOADER to the debug text from bootloader. +- Allow running PyInstaller programatically. +- Move/Rename some files, code refactoring. +- Add more tests. +- Tilde is in PyInstaller recognized as $HOME variable. + + +2.0 (2012-08-08) +---------------- + +- Minimum suported Python version is 2.3. +- (OSX) Add support for Mac OS X 64-bit +- (OSX) Add support Mac OS X 10.7 (Lion) and 10.8 (Mountain Lion). +- (OSX) With argument --windowed PyInstaller creates application bundle (.app) +- automatically. +- Add experimental support for AIX (thanks to Martin Gamwell Dawids). +- Add experimental support for Solaris (thanks to Hywel Richards). +- Add Multipackage function to create a collection of packages to avoid +- library duplication. See documentation for more details. +- New symplified command line interface. Configure.py/Makespec.py/Build.py +- replaced by pyinstaller.py. See documentation for more details. +- Removed cross-building/bundling feature which was never really finished. +- Added option --log-level to all scripts to adjust level of output + (thanks to Hartmut Goebel). +- rthooks.dat moved to support/rthooks.dat +- Packaged executable now returns the same return-code as the +- unpackaged script (thanks to Brandyn White). +- Add import hook for PyUSB (thanks to Chien-An "Zero" Cho). +- Add import hook for wx.lib.pubsub (thanks to Daniel Hyams). +- Add import hook for pyttsx. +- Improve import hook for Tkinter. +- Improve import hook for PyQt4. +- Improve import hook for win32com. +- Improve support for running PyInstaller in virtualenv. +- Add cli options --additional-hooks-dir and --hidden-import. +- Remove cli options -X, -K, -C, --upx, --tk, --configfile, --skip-configure. +- UPX is used by default if available in the PATH variable. + + +- Remove compatibility code for old platforms (dos, os2, MacOS 9). +- Use Python logging system for message output (thanks to Hartmut + Goebel). +- Environment variable MEIPASS2 is accessible as sys._MEIPASS. +- Bootloader now overrides PYTHONHOME and PYTHONPATH. + PYTHONHOME and PYTHONPATH is set to the value of MEIPASS2 variable. +- Bootloader uses absolute paths. +- (OSX) Drop dependency on otool from Xcode on Mac OSX. +- (OSX) Fix missing qt_menu.nib in dist directory when using PyQt4. +- (OSX) Bootloader does not use DYLD_LIBRARY_PATH on Mac OS X anymore. + @loader_path is used instead. +- (OSX) Add support to detect .dylib dependencies on Mac OS X containing + @executable_path, @loader_path and @rpath. +- (OSX) Use macholib to detect dependencies on dynamic libraries. +- Improve test suite. +- Improve source code structure. +- Replace os.system() calls by suprocess module. +- Bundle fake 'site' module with frozen applications to prevent loading + any user's Python modules from host OS. +- Include runtime hooks (rthooks) in code analysis. +- Source code hosting moved to github: + https://github.com/pyinstaller/pyinstaller +- Hosting for running tests daily: + https://jenkins.shiningpanda-ci.com/pyinstaller/ + + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/CHANGES-3.rst b/3rdparty/pyinstaller-4.3/doc/CHANGES-3.rst new file mode 100644 index 0000000000000000000000000000000000000000..68b0f802da4747903851eda09f1c8784560aa042 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/CHANGES-3.rst @@ -0,0 +1,1256 @@ +Changelog for PyInstaller 3.0 – 3.6 +====================================================== + + +3.6 (2020-01-09) +-------------------------- + +**Important:** This is the last release of PyInstaller supporting Python 2.7. +Python 2 is end-of-life, many packages are about to `drop support for Python +2.7 `_ - or already did it. + +Security +~~~~~~~~ + +* [SECURITY] (Win32) Fix CVE-2019-16784: Local Privilege Escalation caused by + insecure directory permissions of sys._MEIPATH. This security fix effects all + Windows software frozen by PyInstaller in "onefile" mode. + While PyInstaller itself was not vulnerable, all Windows software frozen + by PyInstaller in "onefile" mode is vulnerable. + + If you are using PyInstaller to freeze Windows software using "onefile" + mode, you should upgrade PyInstaller and rebuild your software. + + +Features +~~~~~~~~ + +* (Windows): Applications built in windowed mode have their debug messages + sent to any attached debugger or DebugView instead of message boxes. + (:issue:`#4288`) +* Better error message when file exists at path we want to be dir. + (:issue:`#4591`) + + +Bugfix +~~~~~~ + +* (Windows) Allow usage of `VSVersionInfo` as version argument to EXE again. + (:issue:`#4381`, :issue:`#4539`) +* (Windows) Fix MSYS2 dll's are not found by modulegraph. (:issue:`#4125`, + :issue:`#4417`) +* (Windows) The temporary copy of bootloader used add resources, icons, etc. + is not created in --workpath instead of in %TEMP%. This fixes issues on + systems where the anti-virus cleans %TEMP% immediately. (:issue:`#3869`) +* Do not fail the build when ``ldconfig`` is missing/inoperable. + (:issue:`#4261`) +* Fixed loading of IPython extensions. (:issue:`#4271`) +* Fixed pre-find-module-path hook for `distutils` to be compatible with + `virtualenv >= 16.3`. (:issue:`#4064`, :issue:`#4372`) +* Improve error reporting when the Python library can't be found. + (:issue:`#4162`) + + +Hooks +~~~~~ + +* Add hook for + avro (serialization and RPC framework) (:issue:`#4388`), + `django-babel `_ (:issue:`#4516`), + `enzyme `_ (:issue:`#4338`), + google.api (resp. google.api.core) (:issue:`#3251`), + google.cloud.bigquery (:issue:`#4083`, :issue:`#4084`), + google.cloud.pubsub (:issue:`#4446`), + google.cloud.speech (:issue:`#3888`), + nnpy (:issue:`#4483`), + passlib (:issue:`#4520`), + `pyarrow `_ (:issue:`#3720`, :issue:`#4517`), + pyexcel and its plugins io, ods, ods3, odsr, xls, xlsx, xlsxw (:issue:`#4305`), + pysnmp (:issue:`#4287`), + scrapy (:issue:`#4514`), + skimage.io (:issue:`#3934`), + sklearn.mixture (:issue:`#4612`), + sounddevice on macOS and Windows (:issue:`#4498`), + text-unidecode (:issue:`#4327`, :issue:`#4530`), + the google-cloud-kms client library (:issue:`#4408`), + ttkwidgets (:issue:`#4484`), and + webrtcvad (:issue:`#4490`). +* Correct the location of Qt translation files. (:issue:`#4429`) +* Exclude imports for pkg_resources to fix bundling issue. (:issue:`#4263`, + :issue:`#4360`) +* Fix hook for pywebview to collect all required libraries and data-files. + (:issue:`#4312`) +* Fix hook numpy and hook scipy to account for differences in location of extra + dlls on Windows. (:issue:`#4593`) +* Fix pysoundfile hook to bundle files correctly on both OSX and Windows. + (:issue:`#4325`) +* Fixed hook for `pint `_ + to also copy metadata as required to retrieve the version at runtime. + (:issue:`#4280`) +* Fixed PySide2.QtNetwork hook by mirroring PyQt5 approach. (:issue:`#4467`, + :issue:`#4468`) +* Hook for pywebview now collects data files and dynamic libraries only for the + correct OS (Windows). + Hook for pywebview now bundles only the required 'lib' subdirectory. + (:issue:`#4375`) +* Update hooks related to PySide2.QtWebEngineWidgets, ensure the relevant + supporting files required for a QtWebEngineView are copied into the + distribution. (:issue:`#4377`) +* Update PyQt5 loader to support PyQt >=5.12.3. (:issue:`#4293`, + :issue:`#4332`) +* Update PyQt5 to package 64-bit SSL support DLLs. (:issue:`#4321`) +* Update PyQt5 to place OpenGL DLLs correctly for PyQt >= 5.12.3. + (:issue:`#4322`) +* (GNU/Linux) Make hook for GdkPixbuf compatible with Ubuntu and Debian + (:issue:`#4486`). + + +Bootloader +~~~~~~~~~~ + +* (OSX): Added support for appending URL to program arguments when applications + is launched from custom protocol handler. (:issue:`#4397`, :issue:`#4399`) +* (POSIX) For one-file binaries, if the program is started via a symlink, the + second process now keeps the basename of the symlink. (:issue:`#3823`, + :issue:`#3829`) +* (Windows) If bundled with the application, proactivley load ``ucrtbase.dll`` + before loading the Python library. This works around unresolved symbol errors + when loading ``python35.dll`` (or later) on legacy Windows (7, 8, 8.1) + systems + with Universal CRT update is not installed. (:issue:`#1566`, :issue:`#2170`, + :issue:`#4230`) +* Add our own implementation for ``strndup`` and ``strnlen`` to be used on + platforms one of these is missing. + + +PyInstaller Core +~~~~~~~~~~~~~~~~ + +* Now uses hash based `.pyc` files as specified in :pep:`552` in + `base_library.zip` when using Python 3.7 (:issue:`#4096`) + + +Bootloader build +~~~~~~~~~~~~~~~~ + +* (MinGW-w64) Fix .rc.o file not found error. (:issue:`#4501`, :issue:`#4586`) +* Add a check whether ``strndup`` and ``strnlen`` are available. +* Added OpenBSD support. (:issue:`#4545`) +* Fix build on Solaris 10. +* Fix checking for compiler flags in `configure` phase. The check for compiler + flags actually did never work. (:issue:`#4278`) +* Update url for public key in update-waf script. (:issue:`#4584`) +* Update waf to version 2.0.19. + + +3.5 (2019-07-09) +---------------- + +Features +~~~~~~~~ + +* (Windows) Force ``--windowed`` option if first script is a ``.pyw`` file. + This might still be overwritten in the spec-file. (:issue:`#4001`) +* Add support for relative paths for icon-files, resource-files and + version-resource-files. (:issue:`#3333`, :issue:`#3444`) +* Add support for the RedHat Software Collections (SCL) Python 3.x. + (:issue:`#3536`, :issue:`#3881`) +* Install platform-specific dependencies only on that platform. + (:issue:`#4166`, :issue:`#4173`) +* New command-line option ``--upx-exclude``, which allows the user to prevent + binaries from being compressed with UPX. (:issue:`#3821`) + + +Bugfix +~~~~~~ + +* (conda) Fix detection of conda/anaconda platform. +* (GNU/Linux) Fix Anaconda Python library search. (:issue:`#3885`, + :issue:`#4015`) +* (Windows) Fix UAC in one-file mode by embedding the manifest. + (:issue:`#1729`, :issue:`#3746`) +* (Windows\\Py3.7) Now able to locate pylib when VERSION.dll is listed in + python.exe PE Header rather than pythonXY.dll (:issue:`#3942`, + :issue:`#3956`) +* Avoid errors if PyQt5 or PySide2 is referenced by the modulegraph but isn't + importable. (:issue:`#3997`) +* Correctly parse the ``--debug=import``, ``--debug=bootloader``, and + ``--debug=noarchive`` command-line options. (:issue:`#3808`) +* Don't treat PyQt5 and PySide2 files as resources in an OS X windowed build. + Doing so causes the resulting frozen app to fail under Qt 5.12. + (:issue:`#4237`) +* Explicitly specify an encoding of UTF-8 when opening *all* text files. + (:issue:`#3605`) +* Fix appending the content of ``datas`` in a `spec` files to ``binaries`` + instead of the internal ``datas``. (:issue:`#2326`, :issue:`#3694`) +* Fix crash when changing from ``--onefile`` to ``--onedir`` on consecutive + runs. (:issue:`#3662`) +* Fix discovery of Qt paths on Anaconda. (:issue:`#3740`) +* Fix encoding error raised when reading a XML manifest file which includes + non-ASCII characters. This error inhibited building an executable which + has non-ASCII characters in the filename. (:issue:`#3478`) +* Fix inputs to ``QCoreApplication`` constructor in ``Qt5LibraryInfo``. Now the + core application's initialization and finalization in addition to system-wide + and application-wide settings is safer. (:issue:`#4121`) +* Fix installation with pip 19.0. (:issue:`#4003`) +* Fixes PE-file corruption during version update. (:issue:`#3142`, + :issue:`#3572`) +* In the fake ´site` module set `USER_BASE` to empty string instead of None + as Jupyter Notebook requires it to be a 'str'. (:issue:`#3945`) +* Query PyQt5 to determine if SSL is supported, only adding SSL DLLs if so. In + addition, search the path for SSL DLLs, instead of looking in Qt's + ``BinariesPath``. (:issue:`#4048`) +* Require ``pywin32-ctypes`` version 0.2.0, the minimum version which supports + Python 3.7. (:issue:`#3763`) +* Use pkgutil instead of filesystem operations for interacting with the + modules. (:issue:`#4181`) + + +Incompatible Changes +~~~~~~~~~~~~~~~~~~~~ + +* PyInstaller is no longer tested against Python 3.4, which is end-of-live. +* Functions ``compat.architecture()``, ``compat.system()`` and + ``compat.machine()`` have been replace by variables of the same name. This + avoids evaluating the save several times. +* Require an option for the ``--debug`` argument, rather than assuming a + default of ``all``. (:issue:`#3737`) + + +Hooks +~~~~~ + +* Added hooks for + `aliyunsdkcore `_ (:issue:`#4228`), + astropy (:issue:`#4274`), + `BTrees `_ (:issue:`#4239`), + dateparser.utils.strptime (:issue:`#3790`), + `faker `_ (:issue:`#3989`, :issue:`#4133`), + gooey (:issue:`#3773`), + GtkSourceView (:issue:`#3893`), + imageio_ffmpeg (:issue:`#4051`), + importlib_metadata and importlib_resources (:issue:`#4095`), + jsonpath_rw_ext (:issue:`#3841`), + jupyterlab (:issue:`#3951`), + lz4 (:issue:`#3710`), + `magic `_ (:issue:`#4267`), + nanite (:issue:`#3860`), + nbconvert (:issue:`#3947`), + nbdime (:issue:`#3949`), + nbformat (:issue:`#3946`), + notebook (:issue:`#3950`), + pendulum (:issue:`#3906`), + pysoundfile (:issue:`#3844`), + python-docx (:issue:`#2574`, :issue:`#3848`), + python-wavefile (:issue:`#3785`), + pytzdata (:issue:`#3906`), + `PyWavelets pywt `_ (:issue:`#4120`), + pywebview (:issue:`#3771`), + radicale (:issue:`#4109`), + rdflib (:issue:`#3708`), + resampy (:issue:`#3702`), + `sqlalchemy-migrate `_ (:issue:`#4250`), + `textdistance `_ (:issue:`#4239`), + tcod (:issue:`#3622`), + ttkthemes (:issue:`#4105`), and + `umap-learn `_ (:issue:`#4165`). + +* Add runtime hook for certifi. (:issue:`#3952`) +* Updated hook for 'notebook' to look in all Jupyter paths reported by + jupyter_core. (:issue:`#4270`) +* Fixed hook for 'notebook' to only include directories that actually exist. + (:issue:`#4270`) + +* Fixed pre-safe-import-module hook for `setuptools.extern.six`. (:issue:`#3806`) +* Fixed QtWebEngine hook on OS X. (:issue:`#3661`) +* Fixed the QtWebEngine hook on distributions which don't have a NSS subdir + (such as Archlinux) (:issue:`#3758`) +* Include dynamically-imported backends in the ``eth_hash`` package. + (:issue:`#3681`) +* Install platform-specific dependencies only on that platform. + (:issue:`#4168`) +* Skip packaging PyQt5 QML files if the QML directory doesn't exist. + (:issue:`#3864`) +* Support ECC in PyCryptodome. (:issue:`#4212`, :issue:`#4229`) +* Updated PySide2 hooks to follow PyQt5 approach. (:issue:`#3655`, + :issue:`#3689`, :issue:`#3724`, :issue:`#4040`, :issue:`#4103`, + :issue:`#4136`, :issue:`#4175`, :issue:`#4177`, :issue:`#4198`, + :issue:`#4206`) +* Updated the jsonschema hook for v3.0+. (:issue:`#4100`) +* Updated the Sphinx hook to correctly package Sphinx 1.8. + + +Bootloader +~~~~~~~~~~ + +* Update bundled zlib library to 1.2.11 address vulnerabilities. + (:issue:`#3742`) + + +Documentation +~~~~~~~~~~~~~ + +* Update the text produced by ``--help`` to state that the ``--debug`` argument + requires an option. Correctly format this argument in the Sphinx build + process. (:issue:`#3737`) + + +Project & Process +~~~~~~~~~~~~~~~~~ + +* Remove the PEP-518 "build-system" table from ``pyproject.toml`` to fix + installation with pip 19.0. + + +PyInstaller Core +~~~~~~~~~~~~~~~~ + +* Add support for folders in `COLLECT` and `BUNDLE`. (:issue:`#3653`) +* Completely remove `pywin32` dependency, which has erratic releases and + the version on pypi may no longer have future releases. + Require `pywin32-ctypes` instead which is pure python. (:issue:`#3728`, + :issue:`#3729`) +* modulegraph: Align with upstream version 0.17. +* Now prints a more descriptive error when running a tool fails (instead of + dumping a trace-back). (:issue:`#3772`) +* Suppress warnings about missing UCRT dependencies on Win 10. (:issue:`#1566`, + :issue:`#3736`) + + +Test-suite and Continuous Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Fix Appveyor failures of ``test_stderr_encoding()`` and + ``test_stdout_encoding()`` on Windows Python 3.7 x64. (:issue:`#4144`) +* November update of packages used in testing. Prevent pyup from touching + ``test/requirements-tools.txt``. (:issue:`#3845`) +* Rewrite code to avoid a ``RemovedInPytest4Warning: Applying marks directly to + parameters is deprecated, please use pytest.param(..., marks=...) instead.`` +* Run Travis tests under Xenial; remove the deprecated ``sudo: false`` tag. + (:issue:`#4140`) +* Update the Markdown test to comply with `Markdown 3.0 changes + `_ + by using correct syntax for `extensions + `_. + + +3.4 (2018-09-09) +---------------- + +Features +~~~~~~~~ + +* Add support for Python 3.7 (:issue:`#2760`, :issue:`#3007`, :issue:`#3076`, + :issue:`#3399`, :issue:`#3656`), implemented by Hartmut Goebel. +* Improved support for Qt5-based applications (:issue:`#3439`). + By emulating much of the Qt deployment tools' behavior + most PyQt5 variants are supported. + However, Anaconda's PyQt5 packages are not supported + because its ``QlibraryInfo`` implementation reports incorrect values. + CI tests currently run on PyQt5 5.11.2. Many thanks to Bryan A. Jones for + taking this struggle. +* ``--debug`` now allows more debugging to be activated more easily. This + includes bootloader messages, Python's "verbose imports" and store collected + Python files in the output directory instead of freezing. See ``pyinstaller + –-help`` for details. (:issue:`#3546`, :issue:`#3585`, :issue:`#3587`) +* Hint users to install development package for missing `pyconfig.h`. + (:issue:`#3348`) +* In ``setup.py`` specify Python versions this distribution is compatible with. +* Make ``base_library.zip`` reproducible: Set time-stamp of files. (:issue:`#2952`, + :issue:`#2990`) +* New command-line option ``--bootloader-ignore-signals`` to make the + bootloader forward all signals to the bundle application. (:issue:`#208`, + :issue:`#3515`) +* (OS X) Python standard library module ``plistlib`` is now used for generating + the ``Info.plist`` file. This allows passing complex and nested data in + ``info_plist``. (:issue:`#3532`, :issue:`#3541`) + + +Bugfix +~~~~~~ + +* Add missing ``warnings`` module to ``base_library.zip``. (:issue:`#3397`, + :issue:`#3400`) +* Fix and simplify search for libpython on Windows, msys2, cygwin. + (:issue:`#3167`, :issue:`#3168`) +* Fix incompatibility with `pycryptodome` (a replacement for the apparently + abandoned `pycrypto` library) when using encrypted PYZ-archives. + (:issue:`#3537`) +* Fix race condition caused by the bootloader parent process terminating before + the child is finished. This might happen e.g. when the child process itself + plays with ``switch_root``. (:issue:`#2966`) +* Fix wrong security alert if a filename contains ``..``. (:issue:`#2641`, + :issue:`#3491`) +* Only update resources of cached files when necessary to keep signature valid. + (:issue:`#2526`) +* (OS X) Fix: App icon appears in the dock, even if ``LSUIElement=True``. + (:issue:`#1917`, :issue:`#2075`, :issue:`#3566`) +* (Windows) Fix crash when trying to add resources to Windows executable using + the ``--resource`` option. (:issue:`#2675`, :issue:`#3423`) +* (Windows) Only update resources when necessary to keep signature valid + (:issue:`#3323`) +* (Windows) Use UTF-8 when reading XML manifest file. (:issue:`#3476`) +* (Windows) utils/win32: trap invalid ``--icon`` arguments and terminate with a + message. (:issue:`#3126`) + + +Incompatible Changes +~~~~~~~~~~~~~~~~~~~~ + +* Drop support for Python 3.3 (:issue:`#3288`), Thanks to Hugo and xoviat. +* ``--debug`` now expects an (optional) argument. Thus using ``… --debug + script.py`` will break. Use ``… script.py --debug`` or ``… --debug=all + script.py`` instead. Also ``--debug=all`` (which is the default if no + argument is given) includes ``noarchive``, which will store all collected + Python files in the output directory instead of freezing them. Use + ``--debug=bootloader`` to get the former behavior. (:issue:`#3546`, + :issue:`#3585`, :issue:`#3587`) +* (minor) Change naming of intermediate build files and the `warn` file. This + only effects 3rd-party tools (if any exists) relying on the names of these + files. +* (minor) The destination path for ``--add-data`` and ``--add-binary`` must no + longer be empty, use ``.`` instead. (:issue:`#3066`) +* (minor) Use standard path, not dotted path, for C extensions (Python 3 only). + + +Hooks +~~~~~ + +* New hooks for bokeh visualization library (:issue:`#3607`), + Champlain, Clutter (:issue:`#3443`) dynaconf (:issue:`#3641`), flex + (:issue:`#3401`), FMPy (:issue:`#3589`), gi.repository.xlib + (:issue:`#2634`, :issue:`#3396`) google-cloud-translate, + google-api-core (:issue:`#3658`), jedi (:issue:`#3535`, + :issue:`#3612`), nltk (:issue:`#3705`), pandas (:issue:`#2978`, + :issue:`#2998`, :issue:`#2999`, :issue:`#3015`, :issue:`#3063`, + :issue:`#3079`), phonenumbers (:issue:`#3381`, :issue:`#3558`), + pinyin (:issue:`#2822`), PySide.phonon, PySide.QtSql + (:issue:`#2859`), pytorch (:issue:`#3657`), scipy (:issue:`#2987`, + :issue:`#3048`), uvloop (:issue:`#2898`), web3, eth_account, + eth_keyfile (:issue:`#3365`, :issue:`#3373`). +* Updated hooks for Cryptodome 3.4.8, Django 2.1, gevent 1.3. + Crypto (support for PyCryptodome) (:issue:`#3424`), + Gst and GdkPixbuf (to work on msys2, :issue:`#3257`, :issue:`#3387`), + sphinx 1.7.1, setuptools 39.0. +* Updated hooks for PyQt5 (:issue:`#1930`, :issue:`#1988`, :issue:`#2141`, + :issue:`#2156`, :issue:`#2220`, :issue:`#2518`, :issue:`#2566`, + :issue:`#2573`, :issue:`#2577`, :issue:`#2857`, :issue:`#2924`, + :issue:`#2976`, :issue:`#3175`, :issue:`#3211`, :issue:`#3233`, + :issue:`#3308`, :issue:`#3338`, :issue:`#3417`, :issue:`#3439`, + :issue:`#3458`, :issue:`#3505`), among others: + + - All QML is now loaded by ``QtQml.QQmlEngine``. + - Improve error reporting when determining the PyQt5 library location. + - Improved method for finding ``qt.conf``. + - Include OpenGL fallback DLLs for PyQt5. (:issue:`#3568`). + - Place PyQt5 DLLs in the correct location (:issue:`#3583`). +* Fix hooks for cryptodome (:issue:`#3405`), + PySide2 (style mismatch) (:issue:`#3374`, :issue:`#3578`) +* Fix missing SSL libraries on Windows with ``PyQt5.QtNetwork``. (:issue:`#3511`, + :issue:`#3520`) +* Fix zmq on Windows Python 2.7. (:issue:`#2147`) +* (GNU/Linux) Fix hook usb: Resolve library name reported by usb.backend. + (:issue:`#2633`, :issue:`#2831`, :issue:`#3269`) +* Clean up the USB hook logic. + + +Bootloader +~~~~~~~~~~ + +* Forward all signals to the child process if option + ``pyi-bootloader-ignore-signals`` to be set in the archive. (:issue:`#208`, + :issue:`#3515`) +* Use ``waitpid`` instead of ``wait`` to avoid the bootloder parent process gets + signaled. (:issue:`#2966`) +* (OS X) Don't make the application a GUI app by default, even in + ``--windowed`` mode. Not enforcing this programmatically in the bootloader + allows to control behavior using ``Info.plist`` options - which can by set in + PyInstaller itself or in the `.spec`-file. (:issue:`#1917`, :issue:`#2075`, + :issue:`#3566`) +* (Windows) Show respectivly print utf-8 debug messages ungarbled. + (:issue:`#3477`) +* Fix ``setenv()`` call when ``HAVE_UNSETENV`` is not defined. (:issue:`#3722`, + :issue:`#3723`) + + +Module Loader +~~~~~~~~~~~~~ + +* Improved error message in case importing an extension module fails. + (:issue:`#3017`) + + +Documentation +~~~~~~~~~~~~~ + +* Fix typos, smaller errors and formatting errors in documentation. + (:issue:`#3442`, :issue:`#3521`, :issue:`#3561`, :issue:`#3638`) +* Make clear that ``--windowed`` is independent of ``--onedir``. + (:issue:`#3383`) +* Mention imports using imports ``imp.find_module()`` are not detected. +* Reflect actual behavior regarding ``LD_LIBRARY_PATH``. (:issue:`#3236`) +* (OS X) Revise section on ``info_plist`` for ``plistlib`` functionality and + use an example more aligned with real world usage. (:issue:`#3532`, + :issue:`#3540`, :issue:`#3541`) +* (developers) Overhaul guidelines for commit and commit-messages. + (:issue:`#3466`) +* (developers) Rework developer’s quick-start guide. + + +Project & Process +~~~~~~~~~~~~~~~~~ + +* Add a pip ``requirements.txt`` file. +* Let `pyup` update package requirements for “Test – Libraries†every month + only. +* Use `towncrier` to manage the change log entries. (:issue:`#2756`, + :issue:`#2837`, :issue:`#3698`) + + +PyInstaller Core +~~~~~~~~~~~~~~~~ + +* Add ``requirements_for_package()`` and ``collect_all()`` helper functions for + hooks. +* Add a explanatory header to the warn-file, hopefully reducing the number of + those posting the file to the issue tracker. +* Add module ``enum`` to base_library.zip, required for module ``re`` in + Python 3.6 (and ``re`` is required by ``warnings``). +* Always write the `warn` file. +* Apply ``format_binaries_and_datas()`` (which converts hook-style tuples into + ``TOC``-style tuples) to binaries and datas added through the hook api. +* Avoid printing a useless exceptions in the ``get_module_file_attribute()`` + helper function.. +* Don't gather Python extensions in ``collect_dynamic_libc()``. +* Fix several ResourceWarnings and DeprecationWarnings (:issue:`#3677`) +* Hint users to install necessary development packages if, in + ``format_binaries_and_datas()``, the file not found is ``pyconfig.h``. + (:issue:`#1539`, :issue:`#3348`) +* Hook helper function ``is_module_satisfies()`` returns ``False`` for packages + not found. (:issue:`#3428`, :issue:`#3481`) +* Read data for cache digest in chunks. (:issue:`#3281`) +* Select correct file extension for C-extension file-names like + ``libzmq.cp36-win_amd64.pyd``. +* State type of import (conditional, delayed, etc.) in the *warn* file again. +* (modulegraph) Unbundle `altgraph` library, use from upstream. + (:issue:`#3058`) +* (OS X) In ``--console`` mode set ``LSBackgroundOnly=True`` in``Info.plist`` to + hide the app-icon in the dock. This can still be overruled by passing + ``info_plist`` in the `.spec`-file. (:issue:`#1917`, :issue:`#3566`) +* (OS X) Use the python standard library ``plistlib`` for generating the + ``Info.plist`` file. (:issue:`#3532`, :issue:`#3541`) +* (Windows) Completely remove `pywin32` dependency, which has erratic releases + and the version on pypi may no longer have future releases. Require + `pywin32-ctypes` instead, which is pure python. (:issue:`#3141`) +* (Windows) Encode manifest before updating resource. (:issue:`#3423`) +* (Windows) Make import compatible with python.net, which uses an incompatible + signature for ``__import__``. (:issue:`#3574`) + + +Test-suite and Continuous Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Add script and dockerfile for running tests in docker. (Contributed, not + maintained) (:issue:`#3519`) +* Avoid log messages to be written (and captured) twice. +* Fix decorator ``skipif_no_compiler``. +* Fix the test for the "W" run-time Python option to verify module *warnings* + can actually be imported. (:issue:`#3402`, :issue:`#3406`) +* Fix unicode errors when not capturing output by pytest. +* Run ``pyinstaller -h`` to verify it works. +* ``test_setuptools_nspkg`` no longer modifies source files. +* Appveyor: + + - Add documentation for Appveyor variables used to ``appveyor.yml``. + - Significantly clean-up appveyor.yml (:issue:`#3107`) + - Additional tests produce > 1 hour runs. Split each job into two + jobs. + - Appveyor tests run on 2 cores; therefore, run 2 jobs in parallel. + - Reduce disk usage. + - Split Python 2.7 tests into two jobs to avoid the 1 hour limit. + - Update to use Windows Server 2016. (:issue:`#3563`) +* Travis + + - Use build-stages. + - Clean-up travis.yml (:issue:`#3108`) + - Fix Python installation on OS X. (:issue:`#3361`) + - Start a X11 server for the "Test - Libraries" stage only. + - Use target python interpreter to compile bootloader to check if the + build tool can be used with that this Python version. + + +Bootloader build +~~~~~~~~~~~~~~~~ + +* Print invoking python version when compiling. +* Update `waf` build-tool to 2.0.9 and fix our ``wscript`` for `waf` 2.0. +* (GNU/Linux) When building with ``--debug`` turn of FORTIFY_SOURCE to ease + debugging. + + +.. _v3.4 known issues: + +Known Issues +~~~~~~~~~~~~~~~~~~ + +* Anaconda's PyQt5 packages are not supported + because its ``QlibraryInfo`` implementation reports incorrect values. +* All scripts frozen into the package, as well as all run-time hooks, share + the same global variables. This issue exists since v3.2 but was discovered + only lately, see :issue:`3037`. This may lead to leaking global variables + from run-time hooks into the script and from one script to subsequent ones. + It should have effects in rare cases only, though. +* Data-files from wheels, unzipped eggs or not ad egg at all are not included + automatically. This can be worked around using a hook-file, but may not + suffice when using ``--onefile`` and something like `python-daemon`. + +* The multipackage (MERGE) feature (:issue:`1527`) is currently broken. +* (OSX) Support for OpenDocument events (:issue:`1309`) is broken. +* (Windows) With Python 2.7 the frozen application may not run if the + user-name (more specifically ``%TEMPDIR%``) includes some Unicode + characters. This does not happen with all Unicode characters, but only some + and seems to be a windows bug. As a work-around please upgrade to Python 3 + (:issue:`2754`, :issue:`2767`). +* (Windows) For Python >= 3.5 targeting *Windows < 10*, the developer needs to + take special care to include the Visual C++ run-time .dlls. Please see the + section :ref:`Platform-specific Notes ` + in the manual. (:issue:`1566`) + + +3.3.1 (2017-12-13) +------------------ + +Hooks +~~~~~~~~~~ + +* Fix imports in hooks accessible_output and sound_lib (#2860). +* Fix ImportError for sysconfig for 3.5.4 Conda (#3105, #3106). +* Fix shapely hook for conda environments on Windows (#2838). +* Add hook for unidecode. + +Bootloader +~~~~~~~~~~~~~~ + +* (Windows) Pre-build bootloaders (and custom-build ones using MSVC) can be + used on Windows XP again. Set minimum target OS to XP (#2974). + +Bootloader build +~~~~~~~~~~~~~~~~~~~ + +* Fix build for FreeBSD (#2861, #2862). + +PyInstaller Core +~~~~~~~~~~~~~~~~~~~~~~~ + +* Usage: Add help-message clarifying use of options when a spec-file is + provided (#3039). + +* Add printing infos on UnicodeDecodeError in exec_command(_all). +* (win32) Issue an error message on errors loading the icon file (#2039). +* (aarch64) Use correct bootloader for 64-bit ARM (#2873). +* (OS X) Fix replacement of run-time search path keywords (``@…`` ) (#3100). + +* Modulegraph + + * Fix recursion too deep errors cause by reimporting SWIG-like modules + (#2911, #3040, #3061). + * Keep order of imported identifiers. + + +Test-suite and Continuous Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* In Continuous Integration tests: Enable flake8-diff linting. This will + refuse all changed lines not following PEP 8. + +* Enable parallel testing on Windows, +* Update requirements. +* Add more test cases for modulegraph. +* Fix a test-case for order of module import. + +* Add test-cases to check scripts do not share the same global vars (see + :ref:`v3.3.1 known issues`). + +Documentation +~~~~~~~~~~~~~~~~~~~ + +* Add clarification about treatment of options when a spec-file is provided + (#3039). +* Add docs for running PyInstaller with Python optimizations (#2905). + +* Add notes about limitations of Cython support. +* Add information how to handle undetected ctypes libraries. +* Add notes about requirements and restrictions of SWIG support. +* Add note to clarify what `binary files` are. + +* Add a Development Guide. +* Extend "How to Contribute". +* Add "Running the Test Suite". + +* Remove badges from the Readme (#2853). + +* Update outdated sections in man-pages and otehr enhancements to the + man-page. + + +.. _v3.3.1 known issues: + +Known Issues +~~~~~~~~~~~~~~~~~~ + +* All scripts frozen into the package, as well as all run-time hooks, share + the same global variables. This issue exists since v3.2 but was discovered + only lately, see :issue:`3037`. This may lead to leaking global variables + from run-time hooks into the script and from one script to subsequent ones. + It should have effects in rare cases only, though. + +* Further see the :ref:`Known Issues for release 3.3 `. + + +3.3 (2017-09-21) +---------------- + +* **Add Support for Python 3.6!** Many thanks to xiovat! (#2331, #2341) + +* New command line options for adding data files (``--datas``, #1990) and + binaries (``--binaries``, #703) + +* Add command line option '--runtime-tmpdir'. + +* Bootloaders for Windows are now build using MSVC and statically linked with + the run-time-library (CRT). This solved a lot of issues related to .dlls + being incompatible with the ones required by ``python.dll``. + +* Bootloaders for GNU/Linux are now officially no LSB binaries. This was + already the case since release 3.1, but documented the other way round. Also + the build defaults to non-LSB binaries now. (#2369) + +* We improved and stabilized both building the bootloaders and the continuous + integration tests. See below for details. Many thanks to all who worked on + this. + +* To ease solving issues with packages included wrongly, the html-file with a + cross-reference is now always generated. It's visual appearance has been + modernized (#2765). + +Incompatible changes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Command-line option obsoleted several version ago are not longer handled + gracefully but raise an error (#2413) + +* Installation: PyInstaller removed some internal copies of 3rd-party + packages. These are now taken from their official releases at PyPI (#2589). + This results in PyInstaller to no longer can be used from just an unpacked + archive, but needs to be installed like any Python package. This should + effect only a few people, e.g. the developers. + +* Following :pep:`527`, we only release one source archive now and decided to + use `.tar.gz` (#2754). + +Hooks +~~~~~~~~~~ + +* New and Updated hooks: accessible_output2 (#2266), ADIOS (#2096), CherryPy + (#2112), PySide2 (#2471, #2744) (#2472), Sphinx (#2612, 2708) (#2708), + appdir (#2478), clr (#2048), cryptodome (#2125), cryptography (#2013), dclab + (#2657), django (#2037), django migrations (#1795), django.contrib (#2336), + google.cloud, google.cloud.storage, gstreamer (#2603), imageio (#2696), + langcodes (#2682), libaudioverse (#2709), mpl_toolkits (#2400), numba, + llvmlite (#2113), openpyxl (#2066), pylint, pymssql, pyopencl, pyproj + (#2677), pytest (#2119), qtawesome (#2617), redmine, requests (#2334), + setuptools, setuptools (#2565), shapely (#2569), sound_lib (#2267), + sysconfig, uniseg (#2683), urllib3, wx.rc (#2295), + + * numpy: Look for .dylib libraries, too ( (#2544), support numpy MKL builds + (#1881, #2111) + + * osgeo: Add conda specific places to check for auxiliary data (#2401) + + * QT and related + + - Add hooks for PySide2 + - Eliminate run-time hook by placing files in the correct directory + - Fix path in homebrew for searching for qmake (#2354) + - Repair Qt dll location (#2403) + - Bundle PyQT 5.7 DLLs (#2152) + - PyQt5: Return qml plugin path including subdirectory (#2694) + - Fix hooks for PyQt5.QtQuick (#2743) + - PyQt5.QtWebEngineWidgets: Include files needed by QWebEngine + + * GKT+ and related + + - Fix Gir file path on windows. + - Fix unnecessary file search & generation when GI's typelib is exists + - gi: change gir search path when running from a virtualenv + - gi: package gdk-pixbuf in osx codesign agnostic dir + - gi: rewrite the GdkPixbuf loader cache at runtime on Linux + - gi: support onefile mode for GdkPixbuf + - gi: support using gdk-pixbuf-query-loaders-64 when present + - gi: GIR files are only required on OSX + - gio: copy the mime.cache also + - Fix hooks for PyGObject on windows platform (#2306) + +* Fixed hooks: botocore (#2384), clr (#1801), gstreamer (#2417), h5py + (#2686), pylint, Tix data files (#1660), usb.core (#2088), win32com on + non-windows-systems (#2479) + +* Fix ``multiprocess`` spawn mode on POSIX OSs (#2322, #2505, #2759, #2795). + +Bootloader +~~~~~~~~~~~~~~ + +* Add `tempdir` option to control where bootloader will extract files (#2221) +* (Windows) in releases posted on PyPI requires msvcr*.dll (#2343) +* Fix unsafe string manipulation, resource and memory leaks. Thanks to Vito + Kortbeek (#2489, #2502, #2503) +* Remove a left-over use of ``getenv()`` +* Set proper LISTEN_PID (set by `systemd`) in child process (#2345) +* Adds PID to bootloader log messages (#2466, #2480) + +* (Windows) Use _wputenv_s() instead of ``SetEnvironmentVariableW()`` +* (Windows) Enhance error messages (#1431) +* (Windows) Add workaround for a Python 3 issue + http://bugs.python.org/issue29778 (#2496, #2844) + +* (OS X): Use single process for --onedir mode (#2616, #2618) + +* (GNU/Linux) Compile bootloaders with --no-lsb by default (#2369) +* (GNU/Linux) Fix: linux64 bootloader requires glibc 2.14 (#2160) +* (GNU/Linux) set_dynamic_library_path change breaks plugin library use + (#625) + +Bootloader build +~~~~~~~~~~~~~~~~~~~ + +The bootloader build was largely overhauled. In the wscript, the build no +longer depends on the Python interpreter's bit-size, but on the compiler. We +have a machine for building bootloaders for Windows and cross-building for +OS X. Thus all mainteriner are now able to build the bootloaders for all +supported platforms. + +* Add "official" build-script. + +* (GNU/Linux) Make --no-lsb the default, add option --lsb. + +* Largely overhauled Vagrantfile: + + - Make Darwin bootloaders build in OS X box (unused) + - Make Windows bootloaders build using MSVC + - Allow specifying cross-target on linux64. + - Enable cross-building for OS X. + - Enable cross-building for Windows (unused) + - Add box for building osxcross. + +* Largely overhauled wscript: + + - Remove options --target-cpu. + - Use compiler's target arch, not Python's. + - Major overhaul of the script + - Build zlib if required, not if "on windows". + - Remove obsolete warnings. + - Update Solaris, AIX and HPUX support. + - Add flags for 'strip' tool in AIX platform. + - Don't set POSIX / SUS version defines. + +* (GNU/Linux) for 64-bit arm/aarch ignore the :program:`gcc` flag ``-m64`` + (#2801). + +Module loader +~~~~~~~~~~~~~~~~~~~~~~ + +* Implement PEP-451 ModuleSpec type import system (#2377) +* Fix: Import not thread-save? (#2010, #2371) + +PyInstaller Core +~~~~~~~~~~~~~~~~~~~~~~~ + +* Analyze: Check Python version when testing whether to rebuild. +* Analyze: Don't fail on syntax error in modules, simply ignore them. +* Better error message when `datas` are not found. (#2308) +* Building: OSX: Use unicode literals when creating Info.plist XML +* Building: Don't fail if "datas" filename contain glob special characters. + (#2314) +* Building: Read runtime-tmpdir from .spec-file. +* Building: Update a comment. +* building: warn users if bincache gets corrupted. (#2614) +* Cli-utils: Remove graceful handling of obsolete command line options. +* Configure: Create new parent-dir when moving old cache-dir. (#2679) +* Depend: Include vcruntime140.dll on Windows. (#2487) +* Depend: print nice error message if analyzed script has syntax error. +* Depend: When scanning for ctypes libs remove non-basename binaries. +* Enhance run-time error message on ctypes import error. +* Fix #2585: py2 non-unicode sys.path been tempted by os.path.abspath(). + (#2585) +* Fix crash if extension module has hidden import to ctypes. (#2492) +* Fix handling of obsolete command line options. (#2411) +* Fix versioninfo.py breakage on Python 3.x (#2623) +* Fix: "Unicode-objects must be encoded before hashing" (#2124) +* Fix: UnicodeDecodeError - collect_data_files does not return filenames as + unicode (#1604) +* Remove graceful handling of obsolete command line options. (#2413) +* Make grab version more polite on non-windows (#2054) +* Make utils/win32/versioninfo.py round trip the version info correctly. +* Makespec: Fix version number processing for PyCrypto. (#2476) +* Optimizations and refactoring to modulegraph and scanning for ctypes + dependencies. +* pyinstaller should not crash when hitting an encoding error in source code + (#2212) +* Remove destination for COLLECT and EXE prior to copying it (#2701) +* Remove uninformative traceback when adding not found data files (#2346) +* threading bug while processing imports (#2010) +* utils/hooks: Add logging to collect_data_files. + +* (win32) Support using pypiwin32 or pywin32-ctypes (#2602) +* (win32) Use os.path.normpath to ensure that system libs are excluded. +* (win32) Look for libpython%.%.dll in Windows MSYS2 (#2571) +* (win32) Make versioninfo.py round trip the version info correctly (#2599) +* (win32) Ensure that pywin32 isn't imported before check_requirements is + called + +* (win32) pyi-grab_version and --version-file not working? (#1347) +* (win32) Close PE() object to avoid mmap memory leak (#2026) +* (win32) Fix: ProductVersion in windows version info doesn't show in some + cases (#846) +* (win32) Fix multi-byte path bootloader import issue with python2 (#2585) +* (win32) Forward DYLD_LIBRARY_PATH through `arch` command. (#2035) +* (win32) Add ``vcruntime140.dll`` to_win_includes for Python 3.5 an 3.6 + (#2487) + +* (OS X) Add libpython%d.%dm.dylib to Darwin (is_darwin) PYDYLIB_NAMES. + (#1971) +* (OS X) macOS bundle Info.plist should be in UTF-8 (#2615) +* (OS X) multiprocessing spawn in python 3 does not work on macOS (#2322) +* (OS X) Pyinstaller not able to find path (@rpath) of dynamic library (#1514) + +* Modulegraph + + - Align with upstream version 0.13. + - Add the upstream test-suite + - Warn on syntax error and unicode error. (#2430) + - Implement ``enumerate_instructions()`` (#2720) + - Switch byte-code analysis to use `Instruction` (like dis3 does) (#2423) + - Log warning on unicode error instead of only a debug message (#2418) + - Use standard logging for messages. (#2433) + - Fix to reimport failed SWIG C modules (1522, #2578). + +* Included 3rd-party libraries + + - Remove bundled ``pefile`` and ``macholib``, use the releases from PyPI. + (#1920, #2689) + - altgraph: Update to altgraph 0.13, add upstream test-suite. + +Utilities +~~~~~~~~~~~~~~~ + +* :program:`grab_version.py`: Display a friendly error message when utility + fails (#859, #2792). + + +Test-suite and Continuous Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Rearrange requirements files. +* Pin required versions – now updated using pyup (#2745) +* Hide useless trace-backs of helper-functions. +* Add a test for PyQt5.QtQuick. +* Add functional tests for PySide2 +* Add test for new feature --runtime-tmpdir. +* Fix regression-test for #2492. +* unit: Add test-cases for PyiModuleGraph. +* unit/altgraph: Bringing in upstream altgraph test-suite. +* unit/modulegraph: Bringing in the modulegraph test-suite. + +* Continuous Integration + + - Lots of enhancements to the CI tests to make them more stabile and + reliable. + - Pin required versions – now updated using pyup (#2745) + - OS X is now tested along with GNU/Linux at Travis CI (#2508) + - Travis: Use stages (#2753) + - appveyor: Save cache on failure (#2690) + - appveyor: Verify built bootloaders have the expected arch. + +Documentation +~~~~~~~~~~~~~~~~~~~ + +* Add information how to donate (#2755, #2772). +* Add how to install the development version using pip. +* Fix installation instructions for development version. (#2761) +* Better examples for hidden imports. +* Clarify and fix "Adding Data Files" and "Adding Binary Files". (#2482) +* Document new command line option '--runtime-tmpdir'. +* pyinstaller works on powerpc linux, big endian arch (#2000) +* Largely rewrite section "Building the Bootloader", update from the wiki + page. +* Describe building LSB-compliant bootloader as (now) special case. +* help2rst: Add cross-reference labels for option-headers. +* Enable sphinx.ext.intersphinx and links to our website. +* Sphinx should not "adjust" display of command line documentation (#2217) + +.. _v3.3 known issues: + +Known Issues +~~~~~~~~~~~~~~~~~~ + +* Data-files from wheels, unzipped eggs or not ad egg at all are not included + automatically. This can be worked around using a hook-file, but may not + suffice when using ``--onefile`` and something like `python-daemon`. + +* The multipackage (MERGE) feature (#1527) is currently broken. + +* (OSX) Support for OpenDocument events (#1309) is broken. + +* (Windows) With Python 2.7 the frozen application may not run if the + user-name (more specifically ``%TEMPDIR%``) includes some Unicode + characters. This does not happen with all Unicode characters, but only some + and seems to be a windows bug. As a work-around please upgrade to Python 3 + (#2754, #2767). + +* (Windows) For Python >= 3.5 targeting *Windows < 10*, the developer needs to + take special care to include the Visual C++ run-time .dlls. Please see the + section :ref:`Platform-specific Notes ` + in the manual. (#1566) + +* For Python 3.3, imports are not thread-safe (#2371#). Since Python 3.3 is + end of live at 2017-09-29, we are not going to fix this. + + +3.2.1 (2017-01-15) +------------------ + +- New, updated and fixed hooks: botocore (#2094), gi (#2347), jira (#2222), + PyQt5.QtWebEngineWidgets (#2269), skimage (#2195, 2225), sphinx (#2323,) + xsge_gui (#2251). + +Fixed the following issues: + +- Don't fail if working directory already exists (#1994) +- Avoid encoding errors in main script (#1976) +- Fix hasher digest bytes not str (#2229, #2230) + +- (Windows) Fix additional dependency on the msvcrt10.dll (#1974) +- (Windows) Correctly decode a bytes object produced by pefile (#1981) +- (Windows) Package ``pefile`` with pyinstaller. This partially + undoes some changes in 3.2 in which the packaged pefiles were + removed to use the pypi version instead. The pypi version was + considerably slower in some applications, and still has a couple + of small issues on PY3. (#1920) + +- (OS X) PyQt5 packaging issues on MacOS (#1874) +- (OS X) Replace run-time search path keyword (#1965) +- (OS X) (Re-) add argv emulation for OSX, 64-bit (#2219) +- (OS X) use decode("utf-8") to convert bytes in getImports_macholib() (#1973) + +- (Bootloader) fix segfaults (#2176) +- (setup.py) pass option --no-lsb on GNU/Linux only (#1975) + +- Updates and fixes in documentation, manuals, et al. (#1986, 2002, #2153, + #2227, #2231) + + +3.2 (2016-05-03) +---------------- + +- Even the "main" script is now byte-compiled (#1847, #1856) +- The manual is on readthedocs.io now (#1578) +- On installation try to compile the bootloader if there is none for + the current plattform (#1377) + +- (Unix) Use ``objcopy`` to create a valid ELF file (#1812, #1831) +- (Linux): Compile with ``_FORTIFY_SOURCE`` (#1820) + +- New, updated and fixed hooks: CherryPy (#1860), Cryptography (#1425, + #1861), enchant (1562), gi.repository.GdkPixbuf (#1843), gst + (#1963), Lib2to3 (#1768), PyQt4, PyQt5, PySide (#1783, #1897, + #1887), SciPy (#1908, #1909), sphinx (#1911, #1912), sqlalchemy + (#1951), traitlets wx.lib.pubsub (#1837, #1838), + +- For windowed mode add ``isatty()`` for our dummy NullWriter (#1883) +- Suppress "Failed to execute script" in case of SystemExit (#1869) +- Do not apply Upx compressor for bootloader files (#1863) +- Fix absolute path for lib used via ctypes (#1934) +- (OSX) Fix binary cache on NFS (#1573, #1849) +- (Windows) Fix message in grab_version (#1923) +- (Windows) Fix wrong icon paramter in Windows example (#1764) +- (Windows) Fix win32 unicode handling (#1878) +- (Windows) Fix unnecessary rebuilds caused by rebuilding winmanifest + (#1933) +- (Cygwin) Fix finding the Python library for Cygwin 64-bit (#1307, + #1810, #1811) +- (OSX) Fix compilation issue (#1882) +- (Windows) No longer bundle ``pefile``, use package from pypi for windows + (#1357) +- (Windows) Provide a more robust means of executing a Python script +- AIX fixes. + +- Update waf to version 1.8.20 (#1868) +- Fix excludedimports, more predictable order how hooks are applied + #1651 +- Internal impovements and code clean-up (#1754, #1760, #1794, #1858, + #1862, #1887, #1907, #1913) +- Clean-ups fixes and improvements for the test suite + +**Known Issues** + +- Apps built with Windows 10 and Python 3.5 may not run on Windows versions + earlier than 10 (#1566). +- The multipackage (MERGE) feature (#1527) is currently broken. +- (OSX) Support for OpenDocument events (#1309) is broken. + + +3.1.1 (2016-01-31) +------------------ + +Fixed the following issues: + +- Fix problems with setuptools 19.4 (#1772, #1773, #1790, #1791) +- 3.1 does not collect certain direct imports (#1780) +- Git reports wrong version even if on unchanged release (#1778) +- Don't resolve symlinks in modulegraph.py (#1750, #1755) +- ShortFileName not returned in win32 util (#1799) + + +3.1 (2016-01-09) +---------------- + +- Support reproducible builds (#490, #1434, #1582, #1590). +- Strip leading parts of paths in compiled code objects (#1059, #1302, + #1724). + +- With ``--log-level=DEBUG``, a dependency graph-file is emitted in + the build-directory. + +- Allow running pyinstaller as user `root`. By popular demand, see + e.g. #1564, #1459, #1081. + +- New Hooks: botocore, boto3, distorm3, GObject, GI (G Introspection), + GStreamer, GEvent, kivy, lxml.isoschematron, pubsub.core, + PyQt5.QtMultimedia, scipy.linalg, shelve. +- Fixed or Updated Hooks: astroid, django, jsonschema logilab, PyQt4, + PyQt5, skimage, sklearn. +- Add option ``--hiddenimport`` as an alias for ``--hidden-import``. + +- (OSX): Fix issues with ``st_flags`` (#1650). +- (OSX) Remove warning message about 32bit compatibility (#1586). +- (Linux) The cache is now stored in ``$XDG_CACHE_HOME/pyinstaller`` + instead of ``$XDG_DATA_HOME`` - the cache is moved automatically (#1118). +- Documentation updates, e.g. about reproducible builds + +- Put back full text of GPL license into COPYING.txt. +- Fix crashes when looking for ctypes DLLs (#1608, #1609, #1620). +- Fix: Imports in byte-code not found if code contains a function (#1581). +- Fix recursion into bytes-code when scanning for ctypes (#1620). +- Fix PyCrypto modules to work with crypto feature (``--key`` option) + (#1663). +- Fix problems with ``excludedimports`` in some hook excluding the + named modules even if used elswhere (#1584, #1600). +- Fix freezing of pip 7.1.2 (#1699). +- FreeBSD and Solaris fixes. + +- Search for ``ldconfig`` in $PATH first (#1659) +- Deny processing outdated package ``_xmlplus``. + +- Improvements to the test-suite, testing infrastructure and + continuous integration. +- For non-release builds, the exact git revision is not used. +- Internal code refactoring. +- Enhancements and clean-ups to the hooks API - only relevant for hook + authors. See the manual for details. E.g: + + - Removed ``attrs`` in hooks - they were not used anymore anyway. + - Change ``add/del_import()`` to accept arbitrary number of module + names. + - New hook utility function ``copy_metadata()``. + +**Known Issues** + +- Apps built with Windows 10 and Python 3.5 may not run on Windows versions + earlier than 10 (#1566). +- The multipackage (MERGE) feature (#1527) is currently broken. +- (OSX) Support for OpenDocument events (#1309) is broken. + + + +3.0 (2015-10-04) +---------------- + +- Python 3 support (3.3 / 3.4 / 3.5). +- Remove support for Python 2.6 and lower. +- Full unicode support in the bootloader (#824, #1224, #1323, #1340, #1396) + + - (Windows) Python 2.7 apps can now run from paths with non-ASCII characters + - (Windows) Python 2.7 onefile apps can now run for users whose usernames + contain non-ASCII characters + - Fix ``sys.getfilesystemencoding()`` to return correct values (#446, #885). + +- (OSX) Executables built with PyInstaller under OS X can now be digitally + signed. +- (OSX) 32bit precompiled bootloader no longer distributed, only 64bit. +- (Windows) for 32bit bootloader enable flag LARGEADDRESSAWARE that allows + to use 4GB of RAM. +- New hooks: amazon-product-api, appy, certifi, countrycode, cryptography, gi, + httplib2, jsonschema, keyring, lensfunpy, mpl_toolkits.basemap, ncclient, + netCDF4, OpenCV, osgeo, patsy, PsychoPy, pycountry, pycparser, PyExcelerate, + PyGobject, pymssql, PyNaCl, PySiDe.QtCore, PySide.QtGui, rawpy, requests, + scapy, scipy, six, SpeechRecognition, u1db, weasyprint, Xlib. +- Hook fixes: babel, ctypes, django, IPython, pint, PyEnchant, Pygments, PyQt5, + PySide, pyusb, sphinx, sqlalchemy, tkinter, wxPython. +- Add support for automatically including data files from eggs. +- Add support for directory eggs support. +- Add support for all kind of namespace packages e.g. + ``zope.interface``, PEP302 (#502, #615, #665, #1346). +- Add support for ``pkgutil.extend_path()``. +- New option ``--key`` to obfuscate the Python bytecode. +- New option ``--exclude-module`` to ignore a specific module or package. +- (Windows) New option ``--uac-admin`` to request admin permissions + before starting the app. +- (Windows) New option ``--uac-uiaccess`` allows an elevated + application to work with Remote Desktop. +- (Windows) New options for Side-by-side Assembly searching: + + - ``--win-private-assemblies`` bundled Shared Assemblies into the + application will be changed into Private Assemblies + - ``--win-no-prefer-redirects`` while searching for Assemblies + PyInstaller will prefer not to follow policies that redirect to + newer versions. + +- (OSX) New option ``--osx-bundle-identifier`` to set .app bundle identifier. +- (Windows) Remove old COM server support. +- Allow override PyInstaller default config directory by environment + variable ``PYINSTALLER_CONFIG_DIR``. +- Add FreeBSD support. +- AIX fixes. +- Solaris fixes. +- Use library modulegraph for module dependency analysis. +- Bootloader debug messages ``LOADER: ...`` printed to stderr. +- PyInstaller no longer extends ``sys.path`` and bundled 3rd-party + libraries do not interfere with their other versions. +- Enhancemants to ``Analysis()``: + + - New arguments ``excludedimports`` to exclude Python modules in + import hooks. + - New argument ``binaries`` to bundle dynamic libraries in `.spec` + file and in import hooks. + - New argument ``datas`` to bundle additional data files in `.spec` + file and in import hooks. + +- A lot of internal code refactoring. +- Test suite migrated to pytest framework. +- Improved testing infrastructure with continuous integration (Travis - Linux, + Appveyor - Windows) +- Wiki and bug tracker migrated to github. + + +**Known Issues** + +- Apps built with Windows 10 and Python 3.5 may not run on Windows versions + earlier than 10 (#1566). +- The multipackage (MERGE) feature (#1527) is currenty broken. +- (OSX) Support for OpenDocument events (#1309) is broken. + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/CHANGES.rst b/3rdparty/pyinstaller-4.3/doc/CHANGES.rst new file mode 100644 index 0000000000000000000000000000000000000000..eccffc604906f141d597b982f1248bcd1df9e0f4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/CHANGES.rst @@ -0,0 +1,647 @@ +Changelog for PyInstaller +========================= + +.. NOTE: + + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + + To add a new change log entry, please see + https://pyinstaller.readthedocs.io/en/latest/development/changelog-entries.html + +.. towncrier release notes start + +4.3 (2021-04-16) +---------------- + +Features +~~~~~~~~ + +* Provide basic implementation for ``FrozenImporter.get_source()`` that + allows reading source from ``.py`` files that are collected by hooks as + data files. (:issue:`#5697`) +* Raise the maximum allowed size of ``CArchive`` (and consequently ``onefile`` + executables) from 2 GiB to 4 GiB. (:issue:`#3939`) +* The `unbuffered stdio` mode (the ``u`` option) now sets the + ``Py_UnbufferedStdioFlag`` + flag to enable unbuffered stdio mode in Python library. (:issue:`#1441`) +* Windows: Set EXE checksums. Reduces false-positive detection from antiviral + software. (:issue:`#5579`) +* Add new command-line options that map to collect functions from hookutils: + ``-collect-submodules``, ``--collect-data``, ``--collect-binaries``, + ``--collect-all``, and ``--copy-metadata``. (:issue:`#5391`) +* Add new hook utility :func:`~PyInstaller.utils.hooks.collect_entry_point` for + collecting plugins defined through setuptools entry points. (:issue:`#5734`) + + +Bugfix +~~~~~~ + +* (macOS) Fix ``Bad CPU type in executable`` error in helper-spawned python + processes when running under ``arm64``-only flavor of Python on Apple M1. + (:issue:`#5640`) +* (OSX) Suppress missing library error messages for system libraries as + those are never collected by PyInstaller and starting with Big Sur, + they are hidden by the OS. (:issue:`#5107`) +* (Windows) Change default cache directory to ``LOCALAPPDATA`` + (from the original ``APPDATA``). + This is to make sure that cached data + doesn't get synced with the roaming profile. + For this and future versions ``AppData\Roaming\pyinstaller`` + might be safely deleted. (:issue:`#5537`) +* (Windows) Fix ``onefile`` builds not having manifest embedded when icon is + disabled via ``--icon NONE``. (:issue:`#5625`) +* (Windows) Fix the frozen program crashing immediately with + ``Failed to execute script pyiboot01_bootstrap`` message when built in + ``noconsole`` mode and with import logging enabled (either via + ``--debug imports`` or ``--debug all`` command-line switch). (:issue:`#4213`) +* ``CArchiveReader`` now performs full back-to-front file search for + ``MAGIC``, allowing ``pyi-archive_viewer`` to open binaries with extra + appended data after embedded package (e.g., digital signature). + (:issue:`#2372`) +* Fix ``MERGE()`` to properly set references to nested resources with their + full shared-package-relative path instead of just basename. (:issue:`#5606`) +* Fix ``onefile`` builds failing to extract files when the full target + path exceeds 260 characters. (:issue:`#5617`) +* Fix a crash in ``pyi-archive_viewer`` when quitting the application or + moving up a level. (:issue:`#5554`) +* Fix extraction of nested files in ``onefile`` builds created in MSYS + environments. (:issue:`#5569`) +* Fix installation issues stemming from unicode characters in + file paths. (:issue:`#5678`) +* Fix the build-time error under python 3.7 and earlier when ``ctypes`` + is manually added to ``hiddenimports``. (:issue:`#3825`) +* Fix the return code if the frozen script fails due to unhandled exception. + The return code 1 is used instead of -1, to keep the behavior consistent + with that of the python interpreter. (:issue:`#5480`) +* Linux: Fix binary dependency scanner to support `changes to ldconfig +`_ + introduced in ``glibc`` 2.33. (:issue:`#5540`) +* Prevent ``MERGE`` (multipackage) from creating self-references for + duplicated TOC entries. (:issue:`#5652`) +* PyInstaller-frozen onefile programs are now compatible with ``staticx`` + even if the bootloader is built as position-independent executable (PIE). + (:issue:`#5330`) +* Remove dependence on a `private function +`_ + removed in ``matplotlib`` 3.4.0rc1. (:issue:`#5568`) +* Strip absolute paths from ``.pyc`` modules collected into + ``base_library.zip`` + to enable reproducible builds that are invariant to Python install location. + (:issue:`#5563`) +* (OSX) Fix issues with ``pycryptodomex`` on macOS. (:issue:`#5583`) +* Allow compiled modules to be collected into ``base_library.zip``. + (:issue:`#5730`) +* Fix a build error triggered by scanning ``ctypes.CDLL('libc.so')`` on certain + Linux C compiler combinations. (:issue:`#5734`) +* Improve performance and reduce stack usage of module scanning. + (:issue:`#5698`) + + +Hooks +~~~~~ + +* Add support for Conda Forge's distribution of ``NumPy``. (:issue:`#5168`) +* Add support for package content listing via ``pkg_resources``. The + implementation enables querying/listing resources in a frozen package + (both PYZ-embedded and on-filesystem, in that order of precedence) via + ``pkg_resources.resource_exists()``, ``resource_isdir()``, and + ``resource_listdir()``. (:issue:`#5284`) +* Hooks: Import correct typelib for GtkosxApplication. (:issue:`#5475`) +* Prevent ``matplotlib`` hook from collecting current working directory when it + fails to determine the path to matplotlib's data directory. (:issue:`#5629`) +* Update ``pandas`` hook for compatibility with version 1.2.0 and later. + (:issue:`#5630`) +* Update hook for ``distutils.sysconfig`` to be compatible with + pyenv-virtualenv. (:issue:`#5218`) +* Update hook for ``sqlalchemy`` to support version 1.4.0 and above. + (:issue:`#5679`) +* Update hook for ``sysconfig`` to be compatible with pyenv-virtualenv. + (:issue:`#5018`) + + +Bootloader +~~~~~~~~~~ + +* Implement full back-to-front file search for the embedded archive. + (:issue:`#5511`) +* Perform file extraction from the embedded archive in a streaming manner + in order to limit memory footprint when archive contains large files. + (:issue:`#5551`) +* Set the ``__file__`` attribute in the ``__main__`` module (entry-point + script) to the absolute file name inside the ``_MEIPASS``. (:issue:`#5649`) +* Enable cross compiling for FreeBSD from Linux. (:issue:`#5733`) + + +Documentation +~~~~~~~~~~~~~ + +* Doc: Add version spec file option for macOS Bundle. (:issue:`#5476`) +* Update the ``Run-time Information`` section to reflect the changes in + behavior of ``__file__`` inside the ``__main__`` module. (:issue:`#5649`) + + +PyInstaller Core +~~~~~~~~~~~~~~~~ + +* Drop support for python 3.5; EOL since September 2020. (:issue:`#5439`) +* Collect python extension modules that correspond to built-ins into + ``lib-dynload`` sub-directory instead of directly into bundle's root + directory. This prevents them from shadowing shared libraries with the + same basename that are located in a package and loaded via ``ctypes`` or + ``cffi``, and also declutters the bundle's root directory. (:issue:`#5604`) + + +4.2 (2021-01-13) +---------------- + +Features +~~~~~~~~ + +* Add hooks utilities to find binary dependencies of Anaconda distributions. + (:issue:`#5213`) +* (OSX) Automatically remove the signature from the collected copy of the + ``Python`` shared library, using ``codesign --remove-signature``. This + accommodates both ``onedir`` and ``onefile`` builds with recent python + versions for macOS, where invalidated signature on PyInstaller-collected + copy of the ``Python`` library prevents the latter from being loaded. + (:issue:`#5451`) +* (Windows) PyInstaller's console or windowed icon is now added at freeze-time + and + no longer built into the bootloader. Also, using ``--icon=NONE`` allows to + not + apply any icon, thereby making the OS to show some default icon. + (:issue:`#4700`) +* (Windows) Enable ``longPathAware`` option in built application's manifest in + order to support long file paths on Windows 10 v.1607 and later. + (:issue:`#5424`) + + +Bugfix +~~~~~~ + +* Fix loading of plugin-type modules at run-time of the frozen application: + If the plugin path is one character longer than sys._MEIPATH + (e.g. "$PWD/p/plugin_1" and "$PWD/dist/main"), + the plugin relative-imports a sub-module (of the plugin) + and the frozen application contains a module of the same name, + the frozen application module was imported. (:issue:`#4141`, :issue:`#4299`) +* Ensure that spec for frozen packages has ``submodule_search_locations`` set + in order to fix compatibility with ``importlib_resources`` 3.2.0 and later. + (:issue:`#5396`) +* Fix: No rebuild if "noarchive" build-option changes. (:issue:`#5404`) +* (OSX) Fix the problem with ``Python`` shared library collected from + recent python versions not being loaded due to invalidated signature. + (:issue:`#5062`, :issue:`#5272`, :issue:`#5434`) +* (Windows) PyInstaller's default icon is no longer built into the bootloader, + but + added at freeze-time. Thus, when specifiying an icon, only that icon is + contained in the executable and displayed for a shortcut. (:issue:`#870`, + :issue:`#2995`) +* (Windows) Fix "toc is bad" error messages + when passing a ``VSVersionInfo`` + as the ``version`` parameter to ``EXE()`` + in a ``.spec`` file. (:issue:`#5445`) +* (Windows) Fix exception when trying to read a manifest from an exe or dll. + (:issue:`#5403`) +* (Windows) Fix the ``--runtime-tmpdir`` option by creating paths if they don't + exist and expanding environment variables (e.g. ``%LOCALAPPDATA%``). + (:issue:`#3301`, :issue:`#4579`, :issue:`#4720`) + + +Hooks +~~~~~ + +* (GNU/Linux) Collect ``xcbglintegrations`` and ``egldeviceintegrations`` + plugins as part of ``Qt5Gui``. (:issue:`#5349`) +* (macOS) Fix: Unable to code sign apps built with GTK (:issue:`#5435`) +* (Windows) Add a hook for ``win32ctypes.core``. (:issue:`#5250`) +* Add hook for ``scipy.spatial.transform.rotation`` to fix compatibility with + SciPy 1.6.0. (:issue:`#5456`) +* Add hook-gi.repository.GtkosxApplication to fix TypeError with Gtk macOS + apps. (:issue:`#5385`) +* Add hooks utilities to find binary dependencies of Anaconda distributions. + (:issue:`#5213`) +* Fix the ``Qt5`` library availability check in ``PyQt5`` and ``PySide2`` hooks + to re-enable support for ``Qt5`` older than 5.8. (:issue:`#5425`) +* Implement ``exec_statement_rc()`` and ``exec_script_rc()`` as exit-code + returning counterparts of ``exec_statement()`` and ``exec_script()``. + Implement ``can_import_module()`` helper for hooks that need to query module + availability. (:issue:`#5301`) +* Limit the impact of a failed sub-package import on the result of + ``collect_submodules()`` to ensure that modules from all other sub-packages + are collected. (:issue:`#5426`) +* Removed obsolete ``pygame`` hook. (:issue:`#5362`) +* Update ``keyring`` hook to collect metadata, which is required for backend + discovery. (:issue:`#5245`) + + +Bootloader +~~~~~~~~~~ + +* (GNU/Linux) Reintroduce executable resolution via ``readlink()`` on + ``/proc/self/exe`` and preserve the process name using ``prctl()`` with + ``PR_GET_NAME`` and ``PR_SET_NAME``. (:issue:`#5232`) +* (Windows) Create temporary directories with user's SID instead of + ``S-1-3-4``, + to work around the lack of support for the latter in ``wine``. + This enables ``onefile`` builds to run under ``wine`` again. (:issue:`#5216`) +* (Windows) Fix a bug in path-handling code with paths exceeding ``PATH_MAX``, + which is caused by use of ``_snprintf`` instead of ``snprintf`` when + building with MSC. Requires Visual Studio 2015 or later. + Clean up the MSC codepath to address other compiler warnings. + (:issue:`#5320`) +* (Windows) Fix building of bootloader's test suite under Windows with Visual + Studio. + This fixes build errors when ``cmocka`` is present in the build environment. + (:issue:`#5318`) +* (Windows) Fix compiler warnings produced by MinGW 10.2 in order to allow + building the bootloader without having to suppress the warnings. + (:issue:`#5322`) +* (Windows) Fix ``windowed+debug`` bootloader variant not properly + displaying the exception message and traceback information when the + frozen script terminates due to uncaught exception. (:issue:`#5446`) + + +PyInstaller Core +~~~~~~~~~~~~~~~~ + +* (Windows) Avoid using UPX with DLLs that have control flow guard (CFG) + enabled. (:issue:`#5382`) +* Avoid using ``.pyo`` module file suffix (removed since PEP-488) in + ``noarchive`` mode. (:issue:`#5383`) +* Improve support for ``PEP-420`` namespace packages. (:issue:`#5354`) +* Strip absolute paths from ``.pyc`` modules collected in the CArchive (PKG). + This enables build reproducibility without having to match the location of + the build environment. (:issue:`#5380`) + + +4.1 (2020-11-18) +---------------- + +Features +~~~~~~~~ + +* Add support for Python 3.9. (:issue:`#5289`) +* Add support for Python 3.8. (:issue:`#4311`) + + +Bugfix +~~~~~~ + +* Fix endless recursion if a package's ``__init__`` module is an extension + module. (:issue:`#5157`) +* Remove duplicate logging messages (:issue:`#5277`) +* Fix sw_64 architecture support (:issue:`#5296`) +* (AIX) Include python-malloc labeled libraries in search for libpython. + (:issue:`#4210`) + + +Hooks +~~~~~ + +* Add ``exclude_datas``, ``include_datas``, and ``filter_submodules`` to + ``collect_all()``. These arguments map to the ``excludes`` and ``includes`` + arguments of ``collect_data_files``, and to the `filter` argument of + ``collect_submodules``. (:issue:`#5113`) +* Add hook for difflib to not pull in doctests, which is only + required when run as main programm. +* Add hook for distutils.util to not pull in lib2to3 unittests, which will be + rearly used in frozen packages. +* Add hook for heapq to not pull in doctests, which is only + required when run as main programm. +* Add hook for multiprocessing.util to not pull in python test-suite and thus + e.g. tkinter. +* Add hook for numpy._pytesttester to not pull in pytest. +* Add hook for pickle to not pull in doctests and argpargs, which are only + required when run as main programm. +* Add hook for PIL.ImageFilter to not pull + numpy, which is an optional component. +* Add hook for setuptools to not pull in numpy, which is only imported if + installed, not mean to be a dependency +* Add hook for zope.interface to not pull in pytest unittests, which will be + rearly used in frozen packages. +* Add hook-gi.repository.HarfBuzz to fix Typelib error with Gtk apps. + (:issue:`#5133`) +* Enable overriding Django settings path by `DJANGO_SETTINGS_MODULE` + environment variable. (:issue:`#5267`) +* Fix `collect_system_data_files` to scan the given input path instead of its + parent. + File paths returned by `collect_all_system_data` are now relative to the + input path. (:issue:`#5110`) +* Fix argument order in ``exec_script()`` and ``eval_script()``. + (:issue:`#5300`) +* Gevent hook does not unnecessarily bundle HTML documentation, __pycache__ + folders, tests nor generated .c and .h files (:issue:`#4857`) +* gevent: Do not pull in test-suite (still to be refined) +* Modify hook for ``gevent`` to exclude test submodules. (:issue:`#5201`) +* Prevent .pyo files from being collected by collect_data_files when + include_py_files is False. (:issue:`#5141`) +* Prevent output to ``stdout`` during module imports from ending up in the + modules list collected by ``collect_submodules``. (:issue:`#5244`) +* Remove runtime hook and fix regular hook for matplotlib's data to support + ``matplotlib>=3.3.0``, fix deprecation warning on version 3.1<= & <3.3, + and behave normally for versions <3.1. (:issue:`#5006`) +* Remove support for deprecated PyQt4 and PySide (:issue:`#5118`, + :issue:`#5126`) +* setuptools: Exclude outdated compat modules. +* Update ``sqlalchemy`` hook to support v1.3.19 and later, by adding + ``sqlalchemy.ext.baked`` as a hidden import (:issue:`#5128`) +* Update ``tkinter`` hook to collect Tcl modules directory (``tcl8``) in + addition to Tcl/Tk data directories. (:issue:`#5175`) +* (GNU/Linux) {PyQt5,PySide2}.QtWebEngineWidgets: fix search for extra NSS + libraries to prevent an error on systems where /lib64/nss/\*.so + comes up empty. (:issue:`#5149`) +* (OSX) Avoid collecting data from system Tcl/Tk framework in ``tkinter`` hook + as we do not collect their shared libraries, either. + Affects only python versions that still use the system Tcl/Tk 8.5. + (:issue:`#5217`) +* (OSX) Correctly locate the tcl/tk framework bundled with official + python.org python builds from v.3.6.5 on. (:issue:`#5013`) +* (OSX) Fix the QTWEBENGINEPROCESS_PATH set in PyQt5.QtWebEngineWidgets rthook. + (:issue:`#5183`) +* (OSX) PySide2.QtWebEngineWidgets: add QtQmlModels to included libraries. + (:issue:`#5150`) +* (Windows) Remove the obsolete python2.4-era ``_handle_broken_tcl_tk`` + work-around for old virtual environments from the ``tkinter`` hook. + (:issue:`#5222`) + + +Bootloader +~~~~~~~~~~ + +* Fix freeing memory allocted by Python using ``free()`` instead of + ``PyMem_RawFree()``. (:issue:`#4441`) +* (GNU/Linux) Avoid segfault when temp path is missing. (:issue:`#5255`) +* (GNU/Linux) Replace a ``strncpy()`` call in ``pyi_path_dirname()`` with + ``snprintf()`` to ensure that the resulting string is always null-terminated. + (:issue:`#5212`) +* (OSX) Added capability for already-running apps to accept URL & drag'n drop + events via Apple Event forwarding (:issue:`#5276`) +* (OSX) Bump ``MACOSX_DEPLOYMENT_TARGET`` from 10.7 to 10.13. (:issue:`#4627`, + :issue:`#4886`) +* (OSX) Fix to reactivate running app on "reopen" (:issue:`#5295`) +* (Windows) Use ``_wfullpath()`` instead of ``_fullpath()`` in + ``pyi_path_fullpath`` to allow non-ASCII characters in the path. + (:issue:`#5189`) + + +Documentation +~~~~~~~~~~~~~ + +* Add zlib to build the requirements in the Building the Bootlooder section of + the docs. (:issue:`#5130`) + + +PyInstaller Core +~~~~~~~~~~~~~~~~ + +* Add informative message what do to if RecurrsionError occurs. + (:issue:`#4406`, :issue:`#5156`) +* Prevent a local directory with clashing name from shadowing a system library. + (:issue:`#5182`) +* Use module loaders to get module content instea of an quirky way semming from + early Python 2.x times. (:issue:`#5157`) +* (OSX) Exempt the ``Tcl``/``Tk`` dynamic libraries in the system framework + from relative path overwrite. Fix missing ``Tcl``/``Tk`` dynlib on older + python.org builds that still make use of the system framework. + (:issue:`#5172`) + + +Test-suite and Continuous Integration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Replace ``skipif_xxx`` for platform-specific tests by markers. + (:issue:`#1427`) +* Test/CI: Test failures are automatically retried once. (:issue:`#5214`) + + +Bootloader build +~~~~~~~~~~~~~~~~ + +* Fix AppImage builds that were broken since PyInstaller 3.6. (:issue:`#4693`) +* Update build system to use Python 3. +* OSX: Fixed the ineffectiveness of the ``--distpath`` argument for the + ``BUNDLE`` step. (:issue:`#4892`) +* OSX: Improve codesigning and notarization robustness. (:issue:`#3550`, + :issue:`#5112`) +* OSX: Use high resolution mode by default for GUI applications. + (:issue:`#4337`) + + +4.0 (2020-08-08) +---------------- + +Features +~~~~~~~~ + +* Provide setuptools entrypoints to enable other packages to provide + PyInstaller hooks specific to that package, along with tests for these + hooks. + + Maintainers of Python packages requiring hooks are invited to use this new + feature and provide up-to-date PyInstaller support along with their package. + This is quite easy, see our `sample project`__ for more information + (:issue:`#4232`, :issue:`#4301`, :issue:`#4582`). + Many thanks to Bryan A. Jones for implementing the important parts. + + __ https://github.com/pyinstaller/hooksample + +* A new package `pyinstaller-hooks-contrib`__ provides monthly updated hooks + now. This package is installed automatically when installing PyInstaller, + but can be updated independently. + Many thanks to Legorooj for setting up the new package + and moving the hooks there. + + __ https://github.com/pyinstaller/pyinstaller-hooks-contrib + +* Added the ``excludes`` and ``includes`` arguments to the hook utility + function ``collect_data_files``. +* Change the hook collection order so that the hook-priority is command line, + then entry-point, then PyInstaller builtins. (:issue:`#4876`) + + +Bugfix +~~~~~~ + +* (AIX) Include python-malloc labeled libraries in search for libpython. + (:issue:`#4738`) +* (win32) Fix Security Alerts caused by subtle implementation differences + between posix anf windows in ``os.path.dirname()``. (:issue:`#4707`) +* (win32) Fix struct format strings for versioninfo. (:issue:`#4861`) +* (Windows) cv2: bundle the `opencv_videoio_ffmpeg*.dll`, if available. + (:issue:`#4999`) +* (Windows) GLib: bundle the spawn helper executables for `g_spawn*` API. + (:issue:`#5000`) +* (Windows) PySide2.QtNetwork: search for SSL DLLs in `PrefixPath` in addition + to `BinariesPath`. (:issue:`#4998`) +* (Windows) When building with 32-bit python in onefile mode, set the + ``requestedExecutionLevel`` manifest key every time and embed the manifest. + (:issue:`#4992`) +* * (AIX) Fix uninitialized variable. (:issue:`#4728`, :issue:`#4734`) +* Allow building on a different drive than the source. (:issue:`#4820`) +* Consider Python as possible library binary path. Fixes issue where + python is not found if Python3 is installed via brew on OSX (:issue:`#4895`) +* Ensure shared dependencies from onefile packages can be opened in the + bootloader. +* Ensuring repeatable builds of base_library.zip. (:issue:`#4654`) +* Fix ``FileNotFoundError`` showing up in ``utils/misc.py`` which occurs when a + namespace was processed as an filename. (:issue:`#4034`) +* Fix multipackaging. The `MERGE` class will now have the correct relative + paths + between shared dependencies which can correctly be opened by the bootloader. + (:issue:`#1527`, :issue:`#4303`) +* Fix regression when trying to avoid hard-coded paths in .spec files. +* Fix SIGTSTP signal handling to allow typing Ctrl-Z from terminal. + (:issue:`#4244`) +* Update the base library to support encrypting Python bytecode (``--key`` + option) again. Many thanks to Matteo Bertini for finally fixing this. + (:issue:`#2365`, :issue:`#3093`, :issue:`#3133`, :issue:`#3160`, + :issue:`#3198`, :issue:`#3316`, :issue:`#3619`, :issue:`#4241`, + :issue:`#4652`) +* When stripping the leading parts of paths in compiled code objects, the + longest possible import path will now be stripped. (:issue:`#4922`) + + +Incompatible Changes +~~~~~~~~~~~~~~~~~~~~ + +* Remove support for Python 2.7. The minimum required version is now Python + 3.5. The last version supporting Python 2.7 was PyInstaller 3.6. + (:issue:`#4623`) +* Many hooks are now part of the new `pyinstaller-hooks-contrib` + repository. See below for a detailed list. + + +Hooks +~~~~~ + +* Add hook for ``scipy.stats._stats`` (needed for scipy since 1.5.0). + (:issue:`#4981`) +* Prevent hook-nltk from adding non-existing directories. (:issue:`#3900`) +* Fix ``importlib_resources`` hook for modern versions (after 1.1.0). + (:issue:`#4889`) +* Fix hidden imports in `pkg_resources`__ and `packaging`__ (:issue:`#5044`) + + - Add yet more hidden imports to pkg_resources hook. + - Mirror the pkg_resources hook for packaging which may or may not be + duplicate of ``pkg_resources._vendor.packaging``. + + __ https://setuptools.readthedocs.io/en/latest/pkg_resources.html + __ https://packaging.pypa.io/en/latest/ + +* Update pkg_resources hook for setuptools v45.0.0. +* Add QtQmlModels to included libraries for QtWebEngine on OS X + (:issue:`#4631`). +* Fix detecting Qt5 libraries and dependencies from conda-forge builds + (:issue:`#4636`). +* Add an AssertionError message so that users who get an error due + to Hook conflicts can resolve it (:issue:`#4626`). + +* These hooks have been moved to the new + `pyinstaller-hooks-contrib`__ repository: + BTrees, Crypto, Cryptodome, IPython, OpenGL, OpenGL_accelerate, + Xlib, accessible_output2, adios, aliyunsdkcore, amazonproduct, + appdirs, appy, astor, astroid, astropy, avro, bacon, boto, boto3, + botocore, certifi, clr, countrycode, cryptography, cv2, cx_Oracle, + cytoolz, dateparser, dclab, distorm3, dns, docutils, docx, dynaconf, + enchant, enzyme, eth_abi, eth_account, eth_hash, eth_keyfile, + eth_utils, faker, flex, fmpy, gadfly, gooey, google.*, gst, gtk, + h5py, httplib, httplib2, imageio, imageio_ffmpeg, jedi, jinja2, + jira, jsonpath_rw_ext, jsonschema, jupyterlab, kinterbasdb, + langcodes, lensfunpy, libaudioverse, llvmlite, logilab, lxml, lz4, + magic, mako, markdown, migrate, mpl_toolkits, mssql, mysql, nacl, + names, nanite, nbconvert, nbdime, nbformat, ncclient, netCDF4, nltk, + nnpy, notebook, numba, openpyxl, osgeo, passlib, paste, patsy, + pendulum, phonenumbers, pint, pinyin, psychopy, psycopg2, pubsub, + pyarrow, pycountry, pycparser, pyexcel, pyexcelerate, pylint, + pymssql, pyodbc, pyopencl, pyproj, pysnmp, pytest, pythoncom, + pyttsx, pywintypes, pywt, radicale, raven, rawpy, rdflib, redmine, + regex, reportlab, reportlab, resampy, selenium, shapely, skimage, + sklearn, sound_lib, sounddevice, soundfile, speech_recognition, + storm, tables, tcod, tensorflow, tensorflow_corethon, + text_unidecode, textdistance, torch, ttkthemes, ttkwidgets, u1db, + umap, unidecode, uniseg, usb, uvloop, vtkpython, wavefile, + weasyprint, web3, webrtcvad, webview, win32com, wx, xml.dom, + xml.sax, xsge_gui, zeep, zmq. + + __ https://github.com/pyinstaller/pyinstaller-hooks-contrib + +* These hooks have been added while now moved to the new + `pyinstaller-hooks-contrib` repository: astor (:issue:`4400`, + :issue:`#4704`), argon2 (:issue:`#4625`) bcrypt. (:issue:`#4735`), + (Bluetooth Low Energy platform Agnostic Klient for Python) (:issue:`#4649`) + jaraco.text (:issue:`#4576`, :issue:`#4632`), LightGBM. (:issue:`#4634`), + xmldiff (:issue:`#4680`), puremagic (identify a file based off it's magic + numbers) (:issue:`#4709`) webassets (:issue:`#4760`), tensorflow_core (to + support tensorflow module forwarding logic (:issue:`4400`, :issue:`#4704`) + +* These changes have been applied to hooks now moved to the new + `pyinstaller-hooks-contrib` repository + + - Update Bokeh hook for v2.0.0. (:issue:`#4742`, :issue:`#4746`) + - Fix shapely hook on Windows for non-conda shapely installations. + (:issue:`#2834`, :issue:`#4749`) + + +Bootloader +~~~~~~~~~~ + +* Rework bootloader from using strcpy/strncpy with "is this string + terminated"-check to use snprintf(); check succes at more places. (This + started from fixing GCC warnings for strncpy and strncat.) +* Fix: When copying files, too much data was copied in most cases. This + corrupted the file and inhibited using shared dependencies. (:issue:`#4303`) +* In debug and windowed mode, show the traceback in dialogs to help debug + pyiboot01_bootstrap errors. (:issue:`#4213`, :issue:`#4592`) +* Started a small test-suite for bootloader basic functions. (:issue:`#4585`) + + +Documentation +~~~~~~~~~~~~~ + +* Add platform-specific usage notes and bootloader build notes for AIX. + (:issue:`#4731`) + + +PyInstaller Core +~~~~~~~~~~~~~~~~ + +* Provide setuptools entrypoints to enable other packages to provide + PyInstaller hooks specific to that package, along with tests for these hooks. + See https://github.com/pyinstaller/hooksample for more information. + (:issue:`#4232`, :issue:`#4582`) + + +Bootloader build +~~~~~~~~~~~~~~~~ + +* (AIX) The argument -X32 or -X64 is not recognized by the AIX loader - so this + code needs to be removed. (:issue:`#4730`, :issue:`#4731`) +* (OSX) Allow end users to override MACOSX_DEPLOYMENT_TARGET and + mmacosx-version-min + via environment variables and set 10.7 as the fallback value for both. + (:issue:`#4677`) +* Do not print info about ``--noconfirm`` when option is already being used. + (:issue:`#4727`) +* Update :command:`waf` to version 2.0.20 (:issue:`#4839`) + + + +Older Versions +----------------- + +.. toctree:: + :maxdepth: 1 + :caption: Older Versions + + CHANGES-3 + CHANGES-2 + CHANGES-1 + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/CREDITS.rst b/3rdparty/pyinstaller-4.3/doc/CREDITS.rst new file mode 100644 index 0000000000000000000000000000000000000000..c92e0e01ff0a328f8a1ebef8029c35a1ec5afebc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/CREDITS.rst @@ -0,0 +1,395 @@ +Credits +======= + +Thanks goes to all the kind PyInstaller contributors who have contributed +new code, bug reports, fixes, comments and ideas. A brief list follows, +please let us know if your name is omitted by accident: + +Contributions to PyInstaller 4.3 +-------------------------------- + +* Rok Mandeljc - Core Developer +* Brénainn Woodsend - Core Developer +* Jasper Harrison (Legorooj) - Core Developer, Maintainer, Release Manager +* Hartmut Goebel, Core Developer, Maintainer +* xoviat + +* Dan Yeaw, Bruno Oliveira, Maxim Kalinchenko, Max Mäusezahl, Olivier FAURAX, richardsheridan, memo-off + + +Contributions to PyInstaller 4.2 +---------------------------------- + +* Rok Mandeljc +* Hartmut Goebel - Core developer, maintainer and release manager. +* Legorooj - Core developer. +* Bryan A. Jones - Core developer and PyQt5-tamer. +* Mickaël Schoentgen +* Brénainn Woodsend + +* Damien Elmes, Dan Yeaw, hdf, Diggy, Filip Gospodinov, Kyle Altendorf, + Matt Simpson, Nathan Summers, Phoenix, Starbuck5, Tom Hu, rockwalrus + + +Contributions to PyInstaller 4.1 +---------------------------------- + +* Hartmut Goebel - Core developer, maintainer and release manager. +* Legorooj - Core developer. +* Bryan A. Jones - Core developer and PyQt5-tamer. +* Rok Mandeljc +* Mickaël Schoentgen +* Brénainn Woodsend + +* Aaron Althauser, Alex, Andrew Nelson, Benedikt Brückmann, Brénainn Woodsend, + Calin Culianu, Dan Yeaw, Ievgen Popovych, Loïc Messal, Åukasz Stolcman, + Matt, Mohamed, Petrus, Riz, Riz Syed, Santi Santichaivekin, Sid Gupta, + Victor Stinner, byehack, dcgloe, johnthagen, ozelikov, + + +Contributions to PyInstaller 4.0 +---------------------------------- + +* Hartmut Goebel - Core developer, maintainer and release manager. +* Legorooj - Core developer. +* Bryan A. Jones - Core developer and PyQt5-tamer. + +* M Felt aka aixtools, jonnyhsu, Corey Dexter, Rok Mandeljc, Dan Yeaw, Florian + Baumann, Ievgen Popovych, Ram Rachum, coreydexter, AndCycle, Dan Cutright, + David Kiliani, David Maiden Mueller, FeralRobot, Frederico, Ilya Orson, + ItsCinnabar, Juan Sotomayor, Matt M, Matteo Bertini, Michael Felt, Mohamed + Feddad, Nehal J Wani, Or Groman, Sebastian Hohmann, Vaclav Dvorak, Ville + Ilvonen, bwoodsend, eldadr, jeremyd2019, kraptor, seedgou. + + +Contributions to PyInstaller 3.6 +---------------------------------- + +* Hartmut Goebel - Core developer, maintainer and release manager. +* Bryan A. Jones - Core developer and PyQt5-tamer. + +* Dan Yeaw, Amir Rossert, Hugo Martins, Felix Schwarz, Giuseppe Corbelli, + HoLuLuLu, Jonathan Springer, Matt Khan, Min'an, Oracizan, Victor Stinner, + Andres, Andrew Chow, Bernát Gábor, Charles Duffy, Chris, Chrisg2000, + FranzPio, Lee Jeonghun, Lukasz Stolcman, Lyux, László Kiss Kollár, Mathias + Lohne, Michael Felt, Noodle-Head, Ogi Moore, Patryk, RedFantom, Rémy Roy, + Sean McGuire, Thomas Robitaille, Tim, Toby, Tuomo, V.Shkaberda, VojtÄ›ch + Drábek, Wilmar den Ouden, david, ethframe, lnv42, ripdog, satvidh, + thisisivanfong + + +Contributions to PyInstaller 3.5 +---------------------------------- + +* Hartmut Goebel - Core developer, maintainer and release manager. +* Bryan A. Jones - Core developer and PyQt5-tamer. + +* Dave Cortesi, Kuisong Tong, melvyn2, Giuseppe Corbelli, Florian Bruhin, Amir + Ramezani, Cesar Vandevelde, Paul Müller, Thomas Robitaille, zachbateman, + Addison Elliott, Amir Rossert, AndCycle, Atomfighter10101, Chris Berthiaume, + Craig Younkins (bot), Don Krueger, Edward Chen, Exane Server Team, Hannes, + Iwan, Jakob Schnitzer, Janzert, Jendrik Seipp, Jonathan Springer, Kirill + German, Laszlo Kiss-Kollar, Loran425, Lori J, M*C*O, Nikita Melentev, Peter + Bittner, RedFantom, Roman, Roman Yurchak, Ruslan Kuprieiev, Spencer Brown, + Suzumizaki, Tobias Gruetzmacher, Tobias V. Langhoff, TobiasRzepka, Tom + Hacohen, Yuval Shkolar, cclauss, charlesoblack, djl197, matias morant, + satejkhedekar, zhu + + +Contributions to PyInstaller 3.4 +---------------------------------- + +* Hartmut Goebel - Core developer, maintainer and release manager. +* Bryan A. Jones - Core developer and PyQt5-tamer. +* David Vierra - Core developer and encoding specialist. +* xoviat - brave contributor +* Hugo vk - brave contributor + +* Mickaël Schoentgen, Charles Nicholson, Jonathan Springer, Benoît + Vinot, Brett Higgins, Dustin Spicuzza, Marco Nenciarini, Aaron + Hampton, Cody Scot, Dave Cortesi, Helder Eijs, Innokenty Lebedev, + Joshua Klein, Matthew Clapp, Misha Turnbull, ethframe, Amir + Ramezani, Arthur Silva, Blue, Craig MacEachern, Cédric RICARD, + Fredrik Ahlberg, Glenn Ramsey, Jack Mordaunt, Johann Bauer, Joseph + Heck, Kyle Stewart, Lev Maximov, Luo Shawn, Marco Nenciarini, Mario + Costa, Matt Reynolds, Matthieu Gautier, Michael Herrmann, Moritz + Kassner, Natanael Arndt, Nejc Habjan, PaweÅ‚ Kowalik, Pedro de + Medeiros, Peter Conerly, Peter Würtz, Rémy Roy, Saurabh Yadav, Siva + Prasad, Steve Peak, Steven M. Vascellaro, Steven M. Vascellaro, + Suzumizaki-Kimitaka, ThomasV, Timothée Lecomte, Torsten Sommer, + Weliton Freitas, Zhen Zhang, dimitriepirghie, lneuhaus, s3goat, + satarsa, + + +Contributions to PyInstaller 3.3.1 +---------------------------------- + +* Hartmut Goebel - Core developer and release manager. +* Bryan A. Jones - Core developer. +* David Vierra - Core developer and encoding specialist. +* xoviat - brave contributor + +* Dave Cortesi, David Hoese, John Daytona, Nejc Habjan, Addison Elliott, + Bharath Upadhya, Bill Dengler, Chris Norman, Miles Erickson, Nick Dimou, + Thomas Waldmann, David Weil, Placinta + + +Contributions to PyInstaller 3.3 +---------------------------------- + +Special Thanks xiovat for implementing Python3.6 support and to Jonathan +Springer and xoviat for stabilizing the continuous integration tests. + +* Hartmut Goebel - Core developer and release manager. +* Bryan A. Jones - Core developer. +* David Vierra - Core developer and encoding specialist. +* xoviat - brave programmer +* Jonathan Springer +* Vito Kortbeek +* Dustin Spicuzza + +* Ben Hagen +* Paavo +* Brian Teague +* Chris Norman +* Jonathan Stewmon +* Guillaume Thiolliere +* Justin Harris +* Kenneth Zhao +* Paul Müller +* giumas +* y2kbugger +* 肖寅东 + +* Adam Clark, AndCycle, Andreas Schiefer, Arthur Silva, Aswa Paul, Bharath + Upadhya, Brian Teague, Charles Duffy, Chris Coutinho, Cody Scott, Czarek + Tomczak, Dang Mai, Daniel Hyams, David Hoese, Eelco van Vliet, Eric + Drechsel, Erik Bjäreholt, Hatem AlSum, Henry Senyondo, Jan ÄŒapek, Jeremy T. + Hetzel, Jonathan Dan, Julie Marchant, Luke Lee, Marc Abramowitz, Matt + Wilkie, Matthew Einhorn, Michael Herrmann, Niklas Rosenstein, Philippe + Ombredanne, Piotr Radkowski, Ronald Oussoren, Ruslan Kuprieiev, Segev Finer, + Shengjing Zhu 朱晟è, Steve, Steven Noonan, Tibor Csonka, Till Bey, Tobias + Gruetzmacher, 陳鵬宇 (float) + + +Contributions to PyInstaller 3.2.1 +---------------------------------- + +Special Thanks to Thomas Waldmann and David Vierra for support when working on +the new build system. + +- Hartmut Goebel - Core developer and release manager. +- Martin Zibricky - Core developer. +- David Cortesi - Core developer and documentation manager. +- Bryan A. Jones - Core developer. +- David Vierra - Core developer and encoding specialist. +- Cecil Curry - brave bug-fixing and code-refactoring + +- Amane Suzuki +- Andy Cycle +- Axel Huebl +- Bruno Oliveira +- Dan Auerbach +- Daniel Hyams +- Denis Akhiyarov +- Dror Asaf +- Dustin Spicuzza +- Emanuele Bertoldi +- Glenn Ramsey +- Hugh Dowling +- Jesse Suen +- Jonathan Dan +- Jonathan Springer +- Jonathan Stewmon +- Julie Marchant +- Kenneth Zhao +- Linus Groh +- Mansour Moufid +- Martin Zibricky +- Matteo Bertini +- Nicolas Dickreuter +- Peter Würtz +- Ronald Oussoren +- Santiago Reig +- Sean Fisk +- Sergei Litvinchuk +- Stephen Rauch +- Thomas Waldmann +- Till Bald +- xoviat + + + +Contributions to PyInstaller 3.2 +---------------------------------- + +- Hartmut Goebel - Core developer and release manager. +- Martin Zibricky - Core developer. +- David Cortesi - Core developer and documentation manager. +- Bryan A. Jones - Core developer. +- David Vierra - Core developer and encoding specialist. +- Cecil Curry - brave bug-fixing and code-refactoring + +- And Cycle - unicode fixes. +- Chris Hager - QtQuick hook. +- David Schoorisse - wrong icon paramter in Windows example. +- Florian Bruhin - typo hunting. +- Garth Bushell - Support for objcopy. +- Insoleet - lib2to3 hook +- Jonathan Springer - hook fixes, brave works on PyQt. +- Matteo Bertini - code refactoring. +- Jonathan Stewmon - bug hunting. +- Kenneth Zhao - waf update. +- Leonid Rozenberg - typo hunting. +- Merlijn Wajer - bug fixing. +- Nicholas Chammas - cleanups. +- nih - hook fixes. +- Olli-Pekka Heinisuo - CherryPy hook. +- Rui Carmo - cygwin fixes. +- Stephen Rauch - hooks and fixes for unnecessary rebuilds. +- Tim Stumbaugh - bug hunting. + + +Contributions to PyInstaller 3.1.1 +---------------------------------- + +- Hartmut Goebel - Core developer and release manager. +- David Vierra - Core developer and encoding specialist. +- Torsten Landschoff - Fix problems with setuptools +- Peter Inglesby - resolve symlinks in modulegraph.py +- syradium - bug hunting +- dessant - bug hunting +- Joker Qyou - bug hunting + + +Contributions to PyInstaller 3.1 +-------------------------------- + +- Hartmut Goebel - Core developer and release manager. +- Martin Zibricky - Core developer. +- David Cortesi - Core developer and documentation manager. +- Bryan A. Jones - Core developer. +- David Vierra - Core developer and encoding specialist. + +- Andrei Kopats - Windows fixes. +- Andrey Malkov - Django runtime hooks. +- Ben Hagen - kivy hook, GStreamer realtime hook. +- Cecil Curry - Module Version Comparisons and and reworking hooks. +- Dustin Spicuzza - Hooks for GLib, GIntrospection, Gstreamer, etc. +- giumas - lxml.isoschematron hook. +- Jonathan Stewmon - Hooks for botocore, boto, boto3 and gevent.monkey. +- Kenneth Zhao - Solaris fixes. +- Matthew Einhorn - kivy hook. +- mementum - pubsub.core hook. +- Nicholas Chammas - Documentation updates. +- Nico Galoppo - Hooks for skimage and sklearn. +- Panagiotis H.M. Issaris - weasyprint hook. +- Penaz - shelve hook. +- Roman Yurchak - scipy.linalg hook. +- Starwarsfan2099 - Distorm3 hook. +- Thomas Waldmann - Fixes for Bootloader and FreeBSD. +- Tim Stumbaugh - Bug fixes. +- zpin - Bug fixes. + + +Contributions to PyInstaller 3.0 +-------------------------------- + +- Martin Zibricky - Core developer and release manager. +- Hartmut Goebel - Core developer. +- David Cortesi - Initial work on Python 3 support, Python 3 fixes, documentation updates, various hook fixes. +- Cecil Curry - 'six' hook for Python 3, various modulegraph improvements, wxPython hook fixes, +- David Vierra - unicode support in bootloader, Windows SxS Assembly Manifest fixes and many other Windows improvements. +- Michael Mulley - keyring, PyNaCl import hook. +- Rainer Dreyer - OS X fixes, hook fixes. +- Bryan A. Jones - test suite fixes, various hook fixes. +- Philippe Pepiot - Linux fixes. +- Emanuele Bertoldi - pycountry import hook, Django import hook fixes. +- Glenn Ramsey - PyQt5 import hook - support for QtWebEngine on OSX, various hook fixes, Windows fixes. +- Karol Woźniak - import hook fixes. +- Jonathan Springer - PyGObject hooks. ctypes, PyEnchant hook fixes, OS X fixes. +- Giuseppe Masetti - osgeo, mpl_toolkits.basemap and netCDF4 import hooks. +- Yuu Yamashita - OS X fixes. +- Thomas Waldmann - FreeBSD fixes. +- Boris Savelev - FreeBSD and Solaris fixes. +- Guillermo Gutiérrez - Python 3 fixes. +- Jasper Geurtz - gui fixes, hook fixes. +- Holger Pandel - Windows fixes. +- Anthony Zhang - SpeechRecognition import hook. +- Andrei Fokau - Python 3.5 fixes. +- Kenneth Zhao - AIX fixes. +- Maik Riechert - lensfunpy, rawpy import hooks. +- Tim Stumbaugh - hook fixes. +- Andrew Leech - Windows fixes. +- Patrick Robertson - tkinter import hook fixes. +- Yaron de Leeuw - import hook fixes. +- Bryan Cort - PsychoPy import hook. +- Phoebus Veiz - bootloader fixes. +- Sean Johnston - version fix. +- Kevin Zhang - PyExcelerate import hook. +- Paulo Matias - unicode fixes. +- Lorenzo Villani - crypto feature, various fixes. +- Janusz Skonieczny - hook fixes. +- Martin Gamwell Dawids - Solaris fixes. +- Volodymyr Vitvitskyi - typo fixes. +- Thomas Kho - django import hook fixes. +- Konstantinos Koukopoulos - FreeBSD support. +- Jonathan Beezley - PyQt5 import hook fixes. +- Andraz Vrhovec - various fixes. +- Noah Treuhaft - OpenCV import hook. +- Michael Hipp - reportlab import hook. +- Michael Sverdlik - certifi, httplib2, requests, jsonschema import hooks. +- Santiago Reig - appy import hook. + + +Contributions to PyInstaller 2.1 and older +------------------------------------------ + +- Glenn Ramsey - PyQt5 import hook. +- David Cortesi - PyInstaller manual rewrite. +- Vaclav Smilauer - IPython import hook. +- Shane Hansen - Linux arm support. +- Bryan A. Jones - docutils, jinja2, sphinx, pytz, idlelib import hooks. +- Patrick Stewart - scipy import hook. +- Georg Schoelly - storm ORM import hook. +- Vinay Sajip - zmq import hook. +- Martin Gamwell Dawids - AIX support. +- Hywel Richards - Solaris support. +- Brandyn White - packaged executable return code fix. +- Chien-An "Zero" Cho - PyUSB import hook. +- Daniel Hyams - h2py, wx.lib.pubsub import hooks. +- Hartmut Goebel - Python logging system for message output. Option --log-level. +- Florian Hoech - full Python 2.6 support on Windows including automatic + handling of DLLs, CRT, manifest, etc. Read and write resources from/to Win32 + PE files. +- Martin Zibricky - rewrite the build system for the bootloader using waf. + LSB compliant precompiled bootloaders for Linux. Windows 64-bit support. +- Peter Burgers - matplotlib import hook. +- Nathan Weston - Python architecture detection on OS X. +- Isaac Wagner - various OS X fixes. +- Matteo Bertini - OS X support. +- Daniele Zannotti - OS X support. +- David Mugnai - Linux support improvements. +- Arve Knudsen - absolute imports in Python 2.5+ +- Pascal Veret - PyQt4 import hook with Qt4 plugins. +- Don Dwiggins - pyodbc import hook. +- Allan Green - refactoring and improved in-process COM servers. +- Daniele Varrazzo - various bootloader and OS X fixes. +- Greg Copeland - sqlalchemy import hook. +- Seth Remington - PyGTK hook improvements. +- Marco Bonifazi - PyGTK hook improvements. PyOpenGL import hook. +- Jamie Kirkpatrick - paste import hook. +- Lorenzo Mancini - PyXML import hook fixes under Windows. OS X support. App + bundle creation on OS X. Tkinter on OS X. Precompiled bootloaders for OS X. +- Lorenzo Berni - django import hook. +- Louai Al-Khanji - fixes with optparse module. +- Thomas Heller - set custom icon of Windows exe files. +- Eugene Prigorodov - KInterasDB import hook. +- David C. Morrill - vtkpython import hook. +- Alan James Salmoni - Tkinter interface to PyInstaller. + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/Makefile b/3rdparty/pyinstaller-4.3/doc/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fe936c46ab4cd0862bfe2872b8807de43f9d2faa --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/Makefile @@ -0,0 +1,193 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyInstaller.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyInstaller.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/PyInstaller" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyInstaller" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + cp -a $(BUILDDIR)/man/* . + @echo + @echo "Build finished. The manual pages are in doc/" + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/3rdparty/pyinstaller-4.3/doc/_common_definitions.txt b/3rdparty/pyinstaller-4.3/doc/_common_definitions.txt new file mode 100644 index 0000000000000000000000000000000000000000..1f3890d87c7e18b5d3f38309d3430403cf0b39ec --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/_common_definitions.txt @@ -0,0 +1,89 @@ +.. _PyInstaller: http://www.pyinstaller.org + +.. |Homepage| replace:: http://www.pyinstaller.org +.. |Manual| replace:: https://pyinstaller.readthedocs.io/ +.. |PyInstaller| replace:: `PyInstaller` +.. |PyInstallerVersion| replace:: PyInstaller |release| + +.. _issue: https://github.com/pyinstaller/pyinstaller/issues/ + +.. how we punctuate boot loader as a proper noun "the bootloader" +.. |bootloader| replace:: bootloader + +.. _`activation context`: http://msdn.microsoft.com/en-us/library/windows/desktop/aa374153(v=vs.85).aspx +.. _`Apple bundle overview`: https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html +.. _`Apple code signing overview`: https://developer.apple.com/library/mac/technotes/tn2206/_index.html +.. _`Apple document types`: https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-101685 +.. _Cython: http://www.cython.org/ +.. _Django: https://www.djangoproject.com/ +.. _dnspython: http://www.dnspython.org/ +.. _`easy_install`: http://peak.telecommunity.com/DevCenter/EasyInstall +.. _`Microsoft COM`: http://www.microsoft.com/com/default.mspx +.. _`GPL License`: https://raw.github.com/pyinstaller/pyinstaller/develop/COPYING.txt +.. _FAQ: https://github.com/pyinstaller/pyinstaller/wiki/FAQ +.. _Git: http://git-scm.com/downloads +.. _GraphicConverter: http://www.lemkesoft.de/en/products/graphic-converter/ +.. _GraphViz: https://graphviz.org/ +.. _Homebrew: http://brew.sh/ +.. _`How to Contribute`: https://github.com/pyinstaller/pyinstaller/wiki/How-to-Contribute +.. _`How to Report Bugs`: https://github.com/pyinstaller/pyinstaller/wiki/How-to-Report-Bugs +.. _ImageMagick: http://www.imagemagick.org/script/index.php +.. _`importlib.metadata`: https://docs.python.org/3/library/importlib.metadata.html +.. _imputil: http://docs.python.org/2.7/library/imputil.html +.. _`Info Property List`: https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPRuntimeConfig/Articles/ConfigFiles.html +.. _MacPorts: https://www.macports.org/ +.. _makeicns: https://bitbucket.org/mkae/makeicns +.. _marshalled: http://docs.python.org/library/marshal +.. _MinGW: http://sourceforge.net/downloads/mingw/ +.. _MinGW-w64: http://mingw-w64.sourceforge.net/ +.. _Modulefinder: http://docs.python.org/2.7/library/modulefinder.html +.. _netpbm package: http://netpbm.sourceforge.net/ +.. _NextCloud: https://nextcloud.org +.. _Parallels: http://www.parallels.com +.. _pathlib: https://docs.python.org/3/library/pathlib.html +.. _pefile: https://pypi.python.org/pypi/pefile/ +.. _PIL: http://www.pythonware.com/products/pil/ +.. _pip: http://www.pip-installer.org/ +.. _pip-Win: https://sites.google.com/site/pydatalog/python/pip-for-windows +.. _plistlib: https://docs.python.org/3/library/plistlib.html +.. _png2icns: http://icns.sourceforge.net/ +.. _tinyaes: https://github.com/naufraghi/tinyaes-py +.. _PyInstaller.org: https://github.com/pyinstaller/pyinstaller/wiki/Community +.. _`PyInstaller at GitHub`: https://github.com/pyinstaller/pyinstaller +.. _`PyInstaller code signing recipe`: https://github.com/pyinstaller/pyinstaller/wiki/Recipe-OSX-Code-Signing +.. _`PyInstaller Downloads`: https://github.com/pyinstaller/pyinstaller/releases +.. _PyInstaller\/hooks\/hook-win32com.py: http://www.pyinstaller.org/browser/trunk/PyInstaller/hooks/hook-win32com.py?rev=latest +.. _`PyInstaller Email List`: https://groups.google.com/forum/#!forum/pyinstaller +.. _`PyInstaller Issue #1309`: https://github.com/pyinstaller/pyinstaller/issues/1309 +.. _`PyInstaller Recipes`: https://github.com/pyinstaller/pyinstaller/wiki/Recipes +.. _pypi: https://pypi.python.org/pypi/PyInstaller/ +.. _pypiwin32: https://pypi.python.org/pypi/pypiwin32/219 +.. _PyQt: http://www.riverbankcomputing.co.uk/software/pyqt/intro +.. _PySide: http://qt-project.org/wiki/About-PySide +.. _PyWin32: http://sourceforge.net/projects/pywin32/files/ +.. _Qt: http://www.qt-project.org +.. _Recipe: http://www.pyinstaller.org/wiki/Recipe +.. _setup_tools: https://pypi.python.org/pypi/setuptools +.. _`Package resources`: https://pythonhosted.org/setuptools/pkg_resources.html#requirements-parsing +.. _source/common/launch.c: http://www.pyinstaller.org/browser/trunk/source/common/launch.c?rev=latest +.. _`Supported Packages`: https://github.com/pyinstaller/pyinstaller/wiki/Supported-Packages +.. _TDM-GCC: http://tdm-gcc.tdragon.net/ +.. _TkInter: http://wiki.python.org/moin/TkInter +.. _`towncrier`: https://pypi.org/project/towncrier/ +.. _UPX: https://upx.github.io/ +.. _venv: https://docs.python.org/3/library/venv.html +.. _virtualenv: https://virtualenv.pypa.io +.. _`Visual Studio Express`: http://www.microsoft.com/express/ +.. _wiki: http://www.pyinstaller.org/wiki +.. _`Version Information Structures`: http://msdn.microsoft.com/en-us/library/ff468916(v=vs.85).aspx +.. _virtualBox: https://www.virtualbox.org +.. _VMWare: http://www.vmware.com/solutions/desktop/ +.. _Wine: http://www.winehq.org/ +.. _WxPython: http://www.wxpython.org/ +.. _Xcode: http://developer.apple.com/xcode + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/_extensions/pyi_sphinx_roles.py b/3rdparty/pyinstaller-4.3/doc/_extensions/pyi_sphinx_roles.py new file mode 100644 index 0000000000000000000000000000000000000000..382687c7abf28460fd72ec3db96ac47dedff17be --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/_extensions/pyi_sphinx_roles.py @@ -0,0 +1,77 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2017-2021, 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) +#----------------------------------------------------------------------------- +""" +Documentation roles for PyInstaller + +:issue:`1234` - link to github issue + +:commit:`1a2b3c4d5e6fa23b` - link to commit-id, text will be shortened to 8 digits +for readability +""" +# Based on examples taken from +# and +# + +from docutils import nodes +from docutils.parsers.rst.roles import set_classes + +def commit(name, rawtext, text, lineno, inliner, options={}, content=[]): + msg = None + try: + # verify this is a hex string + int(text, 16) + except ValueError: + msg = 'The commit-id must be a hex-string; "%s" is invalid.' % text + if len(text) < 8: + msg = ('The commit-id "%s" is to short, ' + 'please provide at least 8 characters.' % text) + if msg: + msg = inliner.reporter.error(msg, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + options.setdefault('classes', []).append("commitid") + url = "https://github.com/pyinstaller/pyinstaller/commit/" + text + node = nodes.reference(rawtext, text[:8], refuri=url, **options) + return [node], [] + + +def issue(name, rawtext, text, lineno, inliner, options={}, content=[]): + msg = None + try: + # strip leading numner sign which e.g. towncrier adds + if text.startswith("#"): + text = text[1:] + # verify this is a number + num = int(text) + if num <= 0: + raise ValueError + except ValueError: + msg = inliner.reporter.error( + 'The issue number must be a number larger then zero; ' + '"%s" is invalid.' % text, line=lineno) + prb = inliner.problematic(rawtext, rawtext, msg) + return [prb], [msg] + options.setdefault('classes', []).append("issue") + url = "https://github.com/pyinstaller/pyinstaller/issues/%i" % num + node = nodes.reference(rawtext, "#%i" % num, refuri=url, **options) + return [node], [] + + +def autolink(pattern): + def role(name, rawtext, text, lineno, inliner, options={}, content=[]): + url = pattern % (text,) + node = nodes.reference(rawtext, text, refuri=url, **options) + return [node], [] + return role + +def setup(app): + app.add_role('issue', issue) + app.add_role('commit', commit) diff --git a/3rdparty/pyinstaller-4.3/doc/_static/CArchive.png b/3rdparty/pyinstaller-4.3/doc/_static/CArchive.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0658841568da8959fb8ade3f58c63508527e2c Binary files /dev/null and b/3rdparty/pyinstaller-4.3/doc/_static/CArchive.png differ diff --git a/3rdparty/pyinstaller-4.3/doc/_static/SE_exe.png b/3rdparty/pyinstaller-4.3/doc/_static/SE_exe.png new file mode 100644 index 0000000000000000000000000000000000000000..69ef2d490bac86514f4a418ec22776e4fcc13d57 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/doc/_static/SE_exe.png differ diff --git a/3rdparty/pyinstaller-4.3/doc/_static/ZlibArchive.png b/3rdparty/pyinstaller-4.3/doc/_static/ZlibArchive.png new file mode 100644 index 0000000000000000000000000000000000000000..b9fba5c7199e0a429ccace4911afdbb0326dffb7 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/doc/_static/ZlibArchive.png differ diff --git a/3rdparty/pyinstaller-4.3/doc/_static/css/pyinstaller.css b/3rdparty/pyinstaller-4.3/doc/_static/css/pyinstaller.css new file mode 100644 index 0000000000000000000000000000000000000000..0e9b93ce1721adf7d1749cf86931c635d60ed6ab --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/_static/css/pyinstaller.css @@ -0,0 +1,91 @@ +@import url("theme.css"); + +/* tune option lists */ +.rst-content table.docutils.option-list tr td { + white-space: normal !important; +} +.rst-content table.docutils.option-list tr:nth-child(2n-1) td { + background-color: transparent !important; +} + +.wy-nav-side { background-color: white ; } + + +tt, span.literal { + font-family: monospace; + font-size: 0.95em; + background-color: #ECF0F3; + padding: 0 1px; +} + +dl, table { + margin-left: 2em; + margin-right: 2em; +} + +h1, h2, h3, h4, h5, h6 { + background-color: #F2F2F2; + border-bottom: 1px solid #CCCCCC; + color: #20435C !important; + font-family: 'Trebuchet MS',sans-serif; + font-weight: normal; + margin: 20px -20px 10px; + padding: 3px 0 3px 10px; +} + +h1 { + text-align: center; + font-variant: small-caps; +} + +a:link, a:visited { color: #006699; } +a:hover { background-color:#006699; color:white; } + +h1 a, h2 a, h3 a, h4 a, h5 a, h6 a, +h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover, +h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited { + text-decoration: inherit; + background-color: inherit; + color:inherit; +} +h2 a:hover::after, h3 a:hover::after { + color: red; + content: " back to contents"; + text-decoration: unterline; + font-size: x-small; + margin-left: 1em; +} + +pre { + background-color: #EEFFCC; + border-color: #AACC99; + border-style: solid none; + border-width: 1px medium; + color: #333333; + line-height: 120%; + padding: 0.5em; + margin-left: 4em; + margin-right: 4em; + font-family: monospace; + font-size: 0.95em; +} + +code, .rst-content tt, .rst-content code { + background-color: #ECF0F3; +} + +a.reference.commitid { + font-size: 95%; +} + +// Admonitions + +div.note p.admonition-title { + font-weight: bold; + float: left; + margin-right: 1em; +} + +div.note p.admonition-title:after { + content: ": " +} diff --git a/3rdparty/pyinstaller-4.3/doc/_static/pyinstaller-draft1a-100_trans.png b/3rdparty/pyinstaller-4.3/doc/_static/pyinstaller-draft1a-100_trans.png new file mode 100644 index 0000000000000000000000000000000000000000..562b965abf8c545a896a8e9c0ab20af66cf46224 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/doc/_static/pyinstaller-draft1a-100_trans.png differ diff --git a/3rdparty/pyinstaller-4.3/doc/_static/pyinstaller-draft1a.ico b/3rdparty/pyinstaller-4.3/doc/_static/pyinstaller-draft1a.ico new file mode 100644 index 0000000000000000000000000000000000000000..7b89ec48b6509a1f224c46302d3537edf3761d77 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/doc/_static/pyinstaller-draft1a.ico differ diff --git a/3rdparty/pyinstaller-4.3/doc/advanced-topics.rst b/3rdparty/pyinstaller-4.3/doc/advanced-topics.rst new file mode 100644 index 0000000000000000000000000000000000000000..018e06b311fcc57a4a2ea9f79c46abc5bf7b6b58 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/advanced-topics.rst @@ -0,0 +1,489 @@ +Advanced Topics +================ + +The following discussions cover details of |PyInstaller| internal methods. +You should not need this level of detail for normal use, +but such details are helpful if you want to investigate +the |PyInstaller| code and possibly contribute to it, +as described in `How to Contribute`_. + + +.. _the bootstrap process in detail: + +The Bootstrap Process in Detail +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are many steps that must take place before the bundled +script can begin execution. +A summary of these steps was given in the Overview +(:ref:`How the One-Folder Program Works` and +:ref:`How the One-File Program Works`). +Here is more detail to help you understand what the |bootloader| +does and how to figure out problems. + + +Bootloader +---------- + +The bootloader prepares everything for running Python code. +It begins the setup and then returns itself in another process. +This approach of using two processes allows a lot of flexibility +and is used in all bundles except one-folder mode in Windows. +So do not be surprised if you will see your bundled app +as two processes in your system task manager. + +What happens during execution of bootloader: + +A. First process: bootloader starts. + + 1. If one-file mode, extract bundled files to + :file:`{temppath}/_MEI{xxxxxx}`. + + 2. Modify various environment variables: + + - GNU/Linux: If set, save the original value of LD_LIBRARY_PATH + into LD_LIBRARY_PATH_ORIG. + Prepend our path to LD_LIBRARY_PATH. + + - AIX: same thing, but using LIBPATH and LIBPATH_ORIG. + + - OSX: unset DYLD_LIBRARY_PATH. + + 3. Set up to handle signals for both processes. + + 4. Run the child process. + + 5. Wait for the child process to finish. + + 6. If one-file mode, delete :file:`{temppath}/_MEI{xxxxxx}`. + +B. Second process: bootloader itself started as a child process. + + 1. On Windows set the `activation context`_. + + 2. Load the Python dynamic library. + The name of the dynamic library is embedded in the + executable file. + + 3. Initialize Python interpreter: set sys.path, sys.prefix, sys.executable. + + 4. Run python code. + +Running Python code requires several steps: + +1. Run the Python initialization code which + prepares everything for running the user's main script. + The initialization code can use only the Python built-in modules + because the general import mechanism is not yet available. + It sets up the Python import mechanism to load modules + only from archives embedded in the executable. + It also adds the attributes ``frozen`` + and ``_MEIPASS`` to the ``sys`` built-in module. + +2. Execute any run-time hooks: first those specified by the + user, then any standard ones. + +3. Install python "egg" files. + When a module is part of a zip file (.egg), + it has been bundled into the :file:`./eggs` directory. + Installing means appending .egg file names to ``sys.path``. + Python automatically detects whether an + item in ``sys.path`` is a zip file or a directory. + +4. Run the main script. + + +Python imports in a bundled app +------------------------------------- + +|PyInstaller| embeds compiled python code +(``.pyc`` files) within the executable. +|PyInstaller| injects its code into the +normal Python import mechanism. +Python allows this; +the support is described in :pep:`302` "New Import Hooks". + +PyInstaller implements the PEP 302 specification for +importing built-in modules, +importing "frozen" modules (compiled python code +bundled with the app) and for C-extensions. +The code can be read in :file:`./PyInstaller/loader/pyi_mod03_importers.py`. + +At runtime the PyInstaller :pep:`302` hooks are appended +to the variable ``sys.meta_path``. +When trying to import modules the interpreter will +first try PEP 302 hooks in ``sys.meta_path`` +before searching in ``sys.path``. +As a result, the Python interpreter +loads imported python modules from the archive embedded +in the bundled executable. + +This is the resolution order of import statements +in a bundled app: + +1. Is it a built-in module? + A list of built-in modules is in variable + ``sys.builtin_module_names``. + +2. Is it a module embedded in the executable? + Then load it from embedded archive. + +3. Is it a C-extension? + The app will try to find a file with name + :file:`{package.subpackage.module}.pyd` or + :file:`{package.subpackage.module}.so`. + +4. Next examine paths in the ``sys.path``. + There could be any additional location with python modules + or ``.egg`` filenames. + +5. If the module was not found then + raise ``ImportError``. + + +.. _the toc and tree classes: + +The TOC and Tree Classes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +|PyInstaller| manages lists of files using the ``TOC`` +(Table Of Contents) class. +It provides the ``Tree`` class as a convenient way to build a ``TOC`` +from a folder path. + +TOC Class (Table of Contents) +--------------------------------- + +Objects of the ``TOC`` class are used as input to the classes created in +a spec file. +For example, the ``scripts`` member of an Analysis object is a TOC +containing a list of scripts. +The ``pure`` member is a TOC with a list of modules, and so on. + +Basically a ``TOC`` object contains a list of tuples of the form + + ``(``\ *name*\ ``,``\ *path*\ ``,``\ *typecode*\ ``)`` + +In fact, it acts as an ordered set of tuples; +that is, it contains no duplicates +(where uniqueness is based on the *name* element of each tuple). +Within this constraint, a TOC preserves the order of tuples added to it. + +A TOC behaves like a list and supports the same methods +such as appending, indexing, etc. +A TOC also behaves like a set, and supports taking differences and intersections. +In all of these operations a list of tuples can be used as one argument. +For example, the following expressions are equivalent ways to +add a file to the ``a.datas`` member:: + + a.datas.append( [ ('README', 'src/README.txt', 'DATA' ) ] ) + a.datas += [ ('README', 'src/README.txt', 'DATA' ) ] + +Set-difference makes excluding modules quite easy. For example:: + + a.binaries - [('badmodule', None, None)] + +is an expression that produces a new ``TOC`` that is a copy of +``a.binaries`` from which any tuple named ``badmodule`` has been removed. +The right-hand argument to the subtraction operator +is a list that contains one tuple +in which *name* is ``badmodule`` and the *path* and *typecode* elements +are ``None``. +Because set membership is based on the *name* element of a tuple only, +it is not necessary to give accurate *path* and *typecode* elements when subtracting. + +In order to add files to a TOC, you need to know the *typecode* values +and their related *path* values. +A *typecode* is a one-word string. +|PyInstaller| uses a number of *typecode* values internally, +but for the normal case you need to know only these: + + ++---------------+--------------------------------------+-----------------------+--------------------------------------+ +| **typecode** | **description** | **name** | **path** | ++===============+======================================+=======================+======================================+ +| 'DATA' | Arbitrary files. | Run-time name. | Full path name in build. | ++---------------+--------------------------------------+-----------------------+--------------------------------------+ +| 'BINARY' | A shared library. | Run-time name. | Full path name in build. | ++---------------+--------------------------------------+-----------------------+--------------------------------------+ +| 'EXTENSION' | A binary extension to Python. | Run-time name. | Full path name in build. | ++---------------+--------------------------------------+-----------------------+--------------------------------------+ +| 'OPTION' | A Python run-time option. | Option code | ignored. | ++---------------+--------------------------------------+-----------------------+--------------------------------------+ + +The run-time name of a file will be used in the final bundle. +It may include path elements, for example :file:`extras/mydata.txt`. + +A ``BINARY`` file or an ``EXTENSION`` file is assumed to be loadable, executable code, +for example a dynamic library. +The types are treated the same. +``EXTENSION`` is generally used for a Python extension module, +for example a module compiled by Cython_. +|PyInstaller| will examine either type of file for dependencies, +and if any are found, they are also included. + +The Tree Class +------------------ + +The Tree class is a way of creating a TOC that describes some or all of the +files within a directory: + + ``Tree(``\ *root*\ ``, prefix=``\ *run-time-folder*\ ``, excludes=``\ *string_list*\ ``, typecode=``\ *code* | ``'DATA' )`` + +* The *root* argument is a path string to a directory. + It may be absolute or relative to the spec file directory. + +* The *prefix* argument, if given, is a name for a subfolder + within the run-time folder to contain the tree files. + If you omit *prefix* or give ``None``, + the tree files will be at + the top level of the run-time folder. + +* The *excludes* argument, if given, is a list of one or more + strings that match files in the *root* that should be omitted from the Tree. + An item in the list can be either: + + - a name, which causes files or folders with this basename to be excluded + + - ``*.ext``, which causes files with this extension to be excluded + +* The *typecode* argument, if given, specifies the TOC typecode string + that applies to all items in the Tree. + If omitted, the default is ``DATA``, which is appropriate for most cases. + +For example:: + + extras_toc = Tree('../src/extras', prefix='extras', excludes=['tmp','*.pyc']) + +This creates ``extras_toc`` as a TOC object that lists +all files from the relative path :file:`../src/extras`, +omitting those that have the basename (or are in a folder named) ``tmp`` +or that have the type ``.pyc``. +Each tuple in this TOC has: + +* A *name* composed of :file:`extras/{filename}`. + +* A *path* consisting of a complete, absolute path to that file in the + :file:`../src/extras` folder (relative to the location of the spec file). + +* A *typecode* of ``DATA`` (by default). + +An example of creating a TOC listing some binary modules:: + + cython_mods = Tree( '..src/cy_mods', excludes=['*.pyx','*.py','*.pyc'], typecode='EXTENSION' ) + +This creates a TOC with a tuple for every file in the :file:`cy_mods` folder, +excluding any with the ``.pyx``, ``.py`` or ``.pyc`` suffixes +(so presumably collecting the ``.pyd`` or ``.so`` modules created by Cython). +Each tuple in this TOC has: + +* Its own filename as *name* (no prefix; the file will be at the top level of the bundle). + +* A *path* as an absolute path to that file in :file:`../src/cy_mods` + relative to the spec file. + +* A *typecode* of ``EXTENSION`` (``BINARY`` could be used as well). + + +.. _inspecting archives: + +Inspecting Archives +~~~~~~~~~~~~~~~~~~~~~~ + +An archive is a file that contains other files, +for example a ``.tar`` file, a ``.jar`` file, or a ``.zip`` file. +Two kinds of archives are used in |PyInstaller|. +One is a ZlibArchive, which +allows Python modules to be stored efficiently and, +with some import hooks, imported directly. +The other, a CArchive, is similar to a ``.zip`` file, +a general way of packing up (and optionally compressing) arbitrary blobs of data. +It gets its name from the fact that it can be manipulated easily from C +as well as from Python. +Both of these derive from a common base class, making it fairly easy to +create new kinds of archives. + + +ZlibArchive +-------------- + +A ZlibArchive contains compressed ``.pyc`` or ``.pyo`` files. +The ``PYZ`` class invocation in a spec file creates a ZlibArchive. + +The table of contents in a ZlibArchive +is a Python dictionary that associates a key, +which is a member's name as given in an ``import`` statement, +with a seek position and a length in the ZlibArchive. +All parts of a ZlibArchive are stored in the +`marshalled`_ format and so are platform-independent. + +A ZlibArchive is used at run-time to import bundled python modules. +Even with maximum compression this works faster than the normal import. +Instead of searching ``sys.path``, there's a lookup in the dictionary. +There are no directory operations and no +file to open (the file is already open). +There's just a seek, a read and a decompress. + +A Python error trace will point to the source file from which the archive +entry was created (the ``__file__`` attribute from the time the +``.pyc`` was compiled, captured and saved in the archive). +This will not tell your user anything useful, +but if they send you a Python error trace, +you can make sense of it. + +.. figure:: _static/ZlibArchive.png + :alt: Structure of the ZlibArchive + + Structure of the ZlibArchive + + +CArchive +------------- + +A CArchive can contain any kind of file. +It's very much like a ``.zip`` file. +They are easy to create in Python and easy to unpack from C code. +A CArchive can be appended to another file, such as +an ELF and COFF executable. +To allow this, the archive is made with its table of contents at the +end of the file, followed only by a cookie that tells where the +table of contents starts and +where the archive itself starts. + +A CArchive can be embedded within another CArchive. +An inner archive can be opened and used in place, +without having to extract it. + +Each table of contents entry has variable length. +The first field in the entry gives the length of the entry. +The last field is the name of the corresponding packed file. +The name is null terminated. +Compression is optional for each member. + +There is also a type code associated with each member. +The type codes are used by the self-extracting executables. +If you're using a ``CArchive`` as a ``.zip`` file, you don't need to worry about the code. + +The ELF executable format (Windows, GNU/Linux and some others) allows arbitrary +data to be concatenated to the end of the executable without disturbing its +functionality. For this reason, a CArchive's Table of Contents is +at the end of the archive. The executable can open itself as a binary +file, seek to the end and 'open' the CArchive. + +.. figure:: _static/CArchive.png + :alt: CArchive + + Structure of the CArchive + +.. figure:: _static/SE_exe.png + :alt: Structure of the Self Extracting Executable + + Structure of the Self Extracting Executable + + +Using pyi-archive_viewer +-------------------------- + +Use the ``pyi-archive_viewer`` command to inspect any type of archive: + + ``pyi-archive_viewer`` *archivefile* + +With this command you can examine the contents of any archive built with +|PyInstaller| (a ``PYZ`` or ``PKG``), or any executable (``.exe`` file +or an ELF or COFF binary). +The archive can be navigated using these commands: + +O *name* + Open the embedded archive *name* (will prompt if omitted). + For example when looking in a one-file executable, you + can open the ``PYZ-00.pyz`` archive inside it. + +U + Go up one level (back to viewing the containing archive). + +X *name* + Extract *name* (will prompt if omitted). + Prompts for an output filename. + If none given, the member is extracted to stdout. + +Q + Quit. + +The ``pyi-archive_viewer`` command has these options: + +-h, --help + Show help. + +-l, --log + Quick contents log. + +-b, --brief + Print a python evaluable list of contents filenames. + +-r, --recursive + Used with -l or -b, applies recursive behaviour. + + + +.. _inspecting executables: + +Inspecting Executables +~~~~~~~~~~~~~~~~~~~~~~~~ + +You can inspect any executable file with ``pyi-bindepend``: + + ``pyi-bindepend`` *executable_or_dynamic_library* + +The ``pyi-bindepend`` command analyzes the executable or DLL you name +and writes to stdout all its binary dependencies. +This is handy to find out which DLLs are required by +an executable or by another DLL. + +``pyi-bindepend`` is used by |PyInstaller| to +follow the chain of dependencies of binary extensions +during Analysis. + + +.. _creating a reproducible build: + +Creating a Reproducible Build +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In certain cases it is important that when you build the same application twice, +using exactly the same set of dependencies, +the two bundles should be exactly, bit-for-bit identical. + +That is not the case normally. +Python uses a random hash to make dicts and other hashed types, +and this affects compiled byte-code as well as |PyInstaller| +internal data structures. +As a result, two builds may not produce bit-for-bit identical results +even when all the components of the application bundle are the same +and the two applications execute in identical ways. + +You can assure that a build will produce the same bits +by setting the ``PYTHONHASHSEED`` environment variable to a known +integer value before running |PyInstaller|. +This forces Python to use the same random hash sequence until +``PYTHONHASHSEED`` is unset or set to ``'random'``. +For example, execute |PyInstaller| in a script such as +the following (for GNU/Linux and OS X):: + + # set seed to a known repeatable integer value + PYTHONHASHSEED=1 + export PYTHONHASHSEED + # create one-file build as myscript + pyinstaller myscript.spec + # make checksum + cksum dist/myscript/myscript | awk '{print $1}' > dist/myscript/checksum.txt + # let Python be unpredictable again + unset PYTHONHASHSEED + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/bootloader-building.rst b/3rdparty/pyinstaller-4.3/doc/bootloader-building.rst new file mode 100644 index 0000000000000000000000000000000000000000..1883845aacd185dff2fa605a27ce828f4e610165 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/bootloader-building.rst @@ -0,0 +1,541 @@ +.. _building the bootloader: + +========================= +Building the Bootloader +========================= + +PyInstaller comes with pre-compiled bootloaders for some platforms in +the ``bootloader`` folder of the distribution folder. +When there is no pre-compiled bootloader for +the current platform (operating-system and word-size), +the pip_ setup will attempt to build one. + +If there is no precompiled bootloader for your platform, +or if you want to modify the |bootloader| source, +you need to build the |bootloader|. +To do this, + +* Download and install Python, which is required for running :command:`waf`, +* `git clone` or download the source (see the + :ref:`Download section on the web-site `), +* ``cd`` into the folder where you cloned or unpacked the source to, +* ``cd bootloader``, and +* make the bootloader with: ``python ./waf all``, +* test the build by ref:`running (parts of) the test-suite + `. + +This will produce the |bootloader| executables for your current platform +(of course, for Windows these files will have the ``.exe`` extension): + +* :file:`../PyInstaller/bootloader/{OS_ARCH}/run`, +* :file:`../PyInstaller/bootloader/{OS_ARCH}/run_d`, +* :file:`../PyInstaller/bootloader/{OS_ARCH}/runw` (OS X and Windows only), and +* :file:`../PyInstaller/bootloader/{OS_ARCH}/runw_d` (OS X and Windows only). + +The bootloaders architecture defaults to the machine's one, but can be changed +using the ``--target-arch=`` option – given the appropriate compiler and +development files are installed. E.g. to build a 32-bit bootloader on a 64-bit +machine, run:: + + python ./waf all --target-arch=32bit + + +If this reports an error, read the detailed notes that follow, +then ask for technical help. + +Supported platforms are + +* GNU/Linux (using gcc) +* Windows (using Visual C++ (VS2015 or later) or MinGW's gcc) +* Mac OX X (using clang) + +Contributed platforms are + +* AIX (using gcc or xlc) +* HP-UX (using gcc or xlc) +* Solaris + +For more information about cross-building please read on +and mind the section about the virtual machines +provided in the Vagrantfile. + + +Building for GNU/Linux +======================== + +Development Tools +---------------------- + +For building the bootloader you'll need a development environment. +You can run the following to install everything required: + +* On Debian- or Ubuntu-like systems:: + + sudo apt-get install build-essential zlib1g-dev + +* On Fedora, RedHat and derivates:: + + sudo yum groupinstall "Development Tools" + sudo yum install zlib-devel + +* For other Distributions please consult the distributions documentation. + +Now you can build the bootloader as shown above. + +Alternatively you may want to use the `linux64` build-guest +provided by the Vagrantfile (see below). + + +Building Linux Standard Base (LSB) compliant binaries (optional) +----------------------------------------------------------------- + +By default, the bootloaders on GNU/Linux are â€normal“, non-LSB binaries, which +should be fine for all GNU/Linux distributions. + +If for some reason you want to build Linux Standard Base (LSB) compliant +binaries [#]_, you can do so by specifying ``--lsb`` on the waf command line, +as follows:: + + python ./waf distclean all --lsb + +LSB version 4.0 is required for successfully building of |bootloader|. Please +refer to ``python ./waf --help`` for further options related to LSB building. + +.. [#] Linux Standard Base (LSB) is a set of open standards that should + increase compatibility among GNU/Linux distributions. Unfortunately it is + not widely adopted and both Debian and Ubuntu dropped support for LSB + in autumn 2015. Thus |PyInstaller| bootloader are no longer provided + as LSB binary. + + +Building for Mac OS X +======================== + +On Mac OS X please install Xcode_, Apple's suite of tools for developing +software for Mac OS X. +This will get you the `clang` compiler. +Any version suitable for your platform should be fine. +`Xcode` can be also installed from your Mac OS X Install DVD. + +Now you can build the bootloader as shown above. + +Alternatively you may want to use the `darwin64` build-guest +provided by the Vagrantfile (see below). + +By default, the build script targets Mac OSX 10.7, which can be overridden by +exporting the MACOSX_DEPLOYMENT_TARGET environment variable. + +.. _cross-building for mac os x: + +Cross-Building for Mac OS X +----------------------------------- + +For cross-compiling for OS X you need the Clang/LLVM compiler, the +`cctools` (ld, lipo, …), and the OSX SDK. Clang/LLVM is a cross compiler by +default and is available on nearly every GNU/Linux distribution, so you just +need a proper port of the cctools and the OS X SDK. + +This is easy to get and needs to be done only once and the result can be +transferred to you build-system. The build-system can then be a normal +(somewhat current) GNU/Linux system. [#]_ + +.. [#] Please keep in mind that to avoid problems, the system you are using + for the preparation steps should have the same architecture (and + possible the same GNU/Linux distribution version) as the build-system. + +Preparation: Get SDK and Build-tools +....................................... + +For preparing the SDK and building the cctools, we use the very helpful +scripts from the `OS X Cross ` +toolchain. If you re interested in the details, and what other features OS X +Cross offers, please refer to it's homepage. + +Side-note: For actually accessing the OS X disk image file (`.dmg`), +`darling-dmg `_ is used. It allows +mounting `.dmg` s under GNU/Linux via FUSE. + +For saving you reading OSXCross' documentation we prepared a virtual box +description performing all required steps. +If you are interested in the precise commands, please refer to +``packages_osxcross_debianoid``, ``prepare_osxcross_debianiod``, and +``build_osxcross`` in the Vagrantfile. + +Please proceed as follows: + +1. Download `XCode 7.3.x + `_ package manager. + While at a first glance it looks like overdose, this is the easiest + way to install the C++ build-tools. It comes down to two lines in an + administrative powershell:: + + … one-line-install as written on the chocolatey homepage + choco install -y python vcbuildtools + +* Useful Links: + + * `Microsoft Visual C++ Build-Tools 2015 + `_ + * `Microsoft Build-Tools for Visual Studio 2017. + `_ + + +After installing the C++ build-tool +you can build the bootloader as shown above. + + +Build using MinGW-w64 +----------------------- + +Please be aware of the restrictions mentioned above. + +If Visual Studio is not convenient, +you can download and install the MinGW distribution from one of the +following locations: + +* `MinGW-w64`_ required, uses gcc 4.4 and up. + +* `TDM-GCC`_ - MinGW (not used) and MinGW-w64 installers + +Note: Please mind that using cygwin's python or MinGW +when running ``./waf`` will +create executables for cygwin, not for Windows. + +On Windows, when using MinGW-w64, add :file:`{PATH_TO_MINGW}\bin` +to your system ``PATH``. variable. Before building the +|bootloader| run for example:: + + set PATH=C:\MinGW\bin;%PATH% + +Now you can build the bootloader as shown above. +If you have installed both Visual C++ and MinGW, +you might need to add run ``python ./waf --gcc all``. + + + +Build using cygwin and MinGW +-------------------------------- + +Please be aware that +this will create executables for cygwin, not for 'plain' Windows. + +Use cygwin's ``setup.exe`` to install `python` and `mingw`. + +Now you can build the bootloader as shown above. + + +Building for AIX +=================== + +* By default AIX builds 32-bit executables. +* For 64-bit executables set the environment variable :envvar:`OBJECT_MODE`. + +If Python was built as a 64-bit executable +then the AIX utilities that work with binary files +(e.g., .o, and .a) may need the flag ``-X64``. +Rather than provide this flag with every command, +the preferred way to provide this setting +is to use the environment variable :envvar:`OBJECT_MODE`. +Depending on whether Python was build as a 32-bit or a 64-bit executable +you may need to set or unset +the environment variable :envvar:`OBJECT_MODE`. + +To determine the size the following command can be used:: + + $ python -c "import sys; print(sys.maxsize) <= 2**32" + True + +When the answer is ``True`` (as above) Python was build as a 32-bit +executable. + +When working with a 32-bit Python executable proceed as follows:: + + unset OBJECT_MODE + ./waf configure all + +When working with a 64-bit Python executable proceed as follows:: + + export OBJECT_MODE=64 + ./waf configure all + +.. note:: The correct setting of :envvar:`OBJECT_MODE` is also needed when you + use PyInstaller to package your application. + +To build the bootloader you will need a compiler compatible (identical) +with the one used to build python. + +.. note:: Python compiled with a different version of gcc that you are using + might not be compatible enough. + GNU tools are not always binary compatible. + +If you do not know which compiler that was, +this command can help you determine +if the compiler was gcc or an IBM compiler:: + + python -c "import sysconfig; print(sysconfig.get_config_var('CC'))" + +If the compiler is gcc you may need additional RPMs installed +to support the GNU run-time dependencies. + +When the IBM compiler is used no additional prerequisites are expected. +The recommended value for :envvar:`CC` with the IBM compilers is +`:command:xlc_r`. + + +Building for FreeBSD +==================== + +A FreeBSD bootloader may be built with clang using :ref:`the usual steps +` on a FreeBSD machine. +Beware, however that any executable compiled natively on FreeBSD will only run +on equal or newer versions of FreeBSD. +In order to support older versions of FreeBSD, you must compile the oldest OS +version you wish to support. + +Alternatively, the FreeBSD bootloaders may be cross compiled from Linux using +Docker and a `FreeBSD cross compiler image +`_. +This image is kept in sync with the oldest non end of life FreeBSD release so +that anything compiled on it will work on all active FreeBSD versions. + +In a random directory: + +* Start the docker daemon (usually with ``systemctl start docker`` - possibly + requiring ``sudo`` if you haven't setup rootless docker). +* Download the latest cross compiler ``.tar.xz`` image from `here + `_. +* Import the image: ``docker image load -i freebsd-cross-build.tar.xz``. + The cross compiler image is now saved under the name ``freebsd-cross-build``. + You may discard the ``.tar.xz`` file if you wish. + +Then from the root of this repository: + +* Run: + + .. code-block:: bash + + docker run -v $(pwd):/io -it freebsd-cross-build bash -c "cd /io/bootloader; ./waf all" + + + +Vagrantfile Virtual Machines +================================ + +PyInstaller maintains a set of virtual machine description for testing and +(cross-) building. For managing these boxes, we use `vagrant +`_. + +All guests [#]_ will automatically build the bootloader when running +`vagrant up GUEST` or +`vagrant provision GUEST`. They will build both 32- and 64-bit bootloaders. + +.. [#] Except of guest `osxcross`, which will build the OS X SDK and cctools + as described in section :ref:`cross-building for mac os x`. + +All guests (except of `darwin64`), when building the bootloaders, are sharing +the PyInstaller distribution folder and will put the built executables onto +the build-host (into :file:`../PyInstaller/bootloader/`). + +Most boxes requires two `Vagrant` plugins to be installed:: + + vagrant plugin install vagrant-reload vagrant-scp + + +Example usage:: + + vagrant up linux64 # will also build the bootloader + vagrant halt linux64 # or `destroy` + + # verify the bootloader has been rebuild + git status ../PyInstaller/bootloader/ + + +You can pass some parameters for configuring the Vagrantfile by setting +environment variables, like this:: + + GUI=1 TARGET=OSX vagrant up darwin64 + +or like this:: + + export TARGET=OSX + vagrant provision linux64 + + +We currently provide this guests: + +:linux64: GNU/Linux (some recent version) used to build the GNU/Linux + bootloaders. + + * If ``TARGET=OS`` is set, cross-builds the bootloaders for OS X + (see :ref:`cross-building for mac os x`). + + * If ``TARGET=WINDOWS`` is set, cross-builds the bootloaders + for Windows using mingw. Please have in mind that this imposes + the restrictions mentioned above. + + * Otherwise (which is the default) bootloaders for GNU/Linux are + build. + +:darwin64: Mac OS X 'Yosemite' – not actually used by the PyInstaller team, + but provided for testing. + + This guest, when building the bootloaders, does *not* put the + built executables onto the build-host. You need to fetch them + using:: + + vagrant plugin install vagrant-scp vagrant-reload # required only once + vagrant scp -a darwin64:/vagrant/PyInstaller/bootloader/Darwin-* \ + ../PyInstaller/bootloader/ + + This is due the fact that this machine doesn't include the + Virtualbox guest additions and thus doesn't support shared + folders. + +:windows10: Windows 10, used for building the Windows bootloaders + using Visual C++. + + * If ``MINGW=1`` is set, the bootloaders will be build using + MinGW. Please be aware of the restrictions mentioned above. + + .. note:: The Windows box uses password authentication, so in + some cases you need to enter the password (which is + `Passw0rd!`). + +:build-osxcross: GNU/Linux guest used to build the OS X SDK and `cctools` as + described in section :ref:`cross-building for mac os x`. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/conf.py b/3rdparty/pyinstaller-4.3/doc/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..5e4febed213c896a0226b1c7f5e58767400074f4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/conf.py @@ -0,0 +1,422 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# PyInstaller documentation build configuration file, created by +# sphinx-quickstart on Sun Mar 27 16:53:34 2016. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('.')) +sys.path.insert(1, os.path.abspath('..')) +# Add path to your sphinx extensions +sys.path.insert(3, os.path.abspath('./_extensions')) + +from PyInstaller import __version__ +import help2rst + +#-- PyInstaller HACK ----------------------------------------------- + +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +if on_rtd: + # running on 'Read the Docs' + + # Read the Docs is adding stuff to this file when building the + # docs, thus the repository is modified. Remove this marker. + if __version__.endswith('.mod'): + __version__ = __version__[:-4] + +# FIXME: This should become something more sophisticated, e.g. a sphinx +# extension +for prog, outfile in ( + ('../pyinstaller.py', 'man/_pyinstaller-options.tmp'), + ('../makespec.py', 'man/_pyi-makespec-options.tmp'), + ): + prog = os.path.abspath(os.path.join(os.path.dirname(__file__), prog)) + help2rst.to_file(prog, True, '-', outfile) +# Create a version with different labels to avoid warnings +with open('man/_pyinstaller-options.tmp') as fh: + text = fh.read() +text = text.replace('\n.. _pyinstaller ', '\n.. _options-group ') +with open('_pyinstaller-options.tmp', 'w') as fh: + fh.write(text) +del prog, outfile, fh, text + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['sphinx.ext.intersphinx', + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + 'sphinx_autodoc_typehints', + 'pyi_sphinx_roles'] + +intersphinx_mapping = { + 'website': ('http://www.pyinstaller.org//', None), + 'python': ('http://docs.python.org/3', None), +} + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = 'PyInstaller' +copyright = 'This document has been placed in the public domain.' +author = "David Cortesi, based on structure by Giovanni Bajo & William Caban based on Gordon McMillan's manual" + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = __version__ +# The full version, including alpha/beta/rc tags. +release = __version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +today_fmt = '%Y-%m-%d' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build', 'tools', 'source'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +add_module_names = False + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + html_style = 'css/pyinstaller.css' +else: + # RTD adds some defaults to conf.py which make it behave + # differently. So we need a different way to specify our css. + html_context = { + 'css_files': [ + 'https://media.readthedocs.org/css/sphinx_rtd_theme.css', + 'https://media.readthedocs.org/css/readthedocs-doc-embed.css', + '_static/css/pyinstaller.css', + ], + } + + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = '_static/pyinstaller-draft1a-100_trans.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = '_static/pyinstaller-draft1a.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%Y-%m-%d %H:%M UTC" + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PyInstallerdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'PyInstaller.tex', 'PyInstaller Documentation', + 'David Cortesi', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('man/pyinstaller', 'pyinstaller', + 'Configure and build a PyInstaller project in one run', + ['Hartmut Goebel'], 1), + ('man/pyi-makespec', 'pyi-makespec', + 'Create a spec file for your PyInstaller project', + ['Hartmut Goebel'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + +#-- config prior to swiching to sphinx +#source-link: off +#generator: off +#datestamp: + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'PyInstaller', 'PyInstaller Documentation', + author, 'PyInstaller', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = project +epub_author = author +epub_publisher = author +epub_copyright = copyright + +# The basename for the epub file. It defaults to the project name. +#epub_basename = project + +# The HTML theme for the epub output. Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or 'en' if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the Pillow. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True diff --git a/3rdparty/pyinstaller-4.3/doc/contributing.rst b/3rdparty/pyinstaller-4.3/doc/contributing.rst new file mode 100644 index 0000000000000000000000000000000000000000..0bfbe859fdc5c96b77e8233cb3c2279c42312c1d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/contributing.rst @@ -0,0 +1,141 @@ + +.. _how-to-contribute: + +===================== +How To Contribute +===================== + +**You are very welcome to contribute!** +|PyInstaller| is a maintained by a group of volunteers. +All contributions, +like community support, bug reports, bug fixes, +documentation improvements, enhancements and ideas are welcome. + +|PyInstaller| is an free software project that is created and +maintained by volunteers. +It lives-and-dies based on the support it receives from others, and the fact +that you're even considering contributing to |PyInstaller| is very +generous of you. + +.. TODO: Add this: At `Pyinstaller.org`_ you find links to the mailing list, + IRC channel, and Git repository, + and the important `How to Contribute`_ link. + Contributions to code and documentation are welcome, + as well as tested hooks for installing other packages. + + * The code can be found in a Git repository, at + https://github.com/sphinx-doc/sphinx/. + * Issues and feature requests should be raised in the tracker. + * The mailing list for development is at Google Groups. + * There is also the #sphinx-doc IRC channel on freenode. + +Since as of now all core-developers are working on PyInstaller in their +spare-time, you can help us (and the project) most if you are following some +simple guidelines. The higher the quality of your contribution, the less work +we have incorporating them and the earlier we will be able to incorporate them +:-) + + +If you get stuck at any point you can ask on the +`PyInstaller Email List`_ +or `create a ticket on GitHub +`_. + + +For more about our development process and methods, see the +:ref:`development guide`. + + +Some ideas how you can help +============================== + +Some ideas how you can help: + +* **Subscribe** to the `mailing list + `_ (low traffic) or + **join** the `IRC channel + `_ and share your + experience or answer questions from the community. + +* **Answer** |support tickets:|_ Often the user just needs to be pointed + to the fitting section in the manual. + + .. |support tickets:| replace:: **support tickets:** + .. _`support tickets:`: https://github.com/pyinstaller/pyinstaller/issues?q=is%3Aopen+is%3Aissue+label%3Asupport + +* **Triage** |open issues,|_ which means: read the report; ask the issue + requester to provide missing information and to try with the latest + development version; ensure there is a *minimal* example; ensure the + issue-reporter followed all steps in :ref:`When things go wrong`. + If you are able reproduce the + problem and track down the bug, this would be a *great* help for the + core developers. + + .. |open issues,| replace:: **open issues,** + .. _`open issues,`: https://github.com/pyinstaller/pyinstaller/issues?q=is%3Aopen + +* **Help improving the documentation:** There is a list of `documentation + issues`__ you can pick one from. Please provide a pull-request for your + changes. :ref:`Read more »» ` + + __ https://github.com/pyinstaller/pyinstaller/issues?q=is%3Aopen+is%3Aissue+label%3Adocumentation + +* **Pick an** |pull-request-issue|_ and provide one. + + .. |pull-request-issue| replace:: **issue requesting a pull-request** + .. _`pull-request-issue`: https://github.com/pyinstaller/pyinstaller/issues?q=is%3Aopen+is%3Aissue+label%3A%22needs+a+PR%22 + +* **Review** |pull requests:|_ Are the commit messages following the + guideline :ref:`Commit Messages`; do all new files have a + copyright-header (esp. for hooks this is often missing); is the code okay; + etc. + + .. |pull requests:| replace:: **pull requests:** + .. _`pull requests:`: https://github.com/pyinstaller/pyinstaller/pulls + +* Scan the `list of open issues`__ and pick some task :-) + + __ https://github.com/pyinstaller/pyinstaller/issues?utf8=%E2%9C%93&q=is%3Aopen%20is%3Aissue + +Thank you very much! + +If you plan to contribute frequently, just ask for write access to the main +git repository. We would be glad to welcome you in the team! + + +Sponsorship and Project Grant +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Please consider sponsoring |PyInstaller| development, especially if your +company benefits from this project. + +We welcome your patronage on `Bountysource`__: + + __ https://www.bountysource.com/teams/pyinstaller + +* Contribute a recurring amount to the team +* Place a bounty on a specific feature + +Your contribution will go towards adding new features to |PyInstaller| and +making sure all functionality continues to meet our high quality standards. + +A grant for contiguous full-time development has the biggest impact for +progress. Periods of 3 to 10 days allow a contributor to tackle substantial +complex issues which are otherwise left to linger until somebody can’t afford +to not fix them. + +Contact `Hartmut Goebel `_ to arrange a +grant for a core contributor. + +Huge thanks to all the companies and individuals who financially contributed +to the development of |PyInstaller|. Please send a PR if you’ve donated and +would like to be listed on the web-site. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/branch-model.rst b/3rdparty/pyinstaller-4.3/doc/development/branch-model.rst new file mode 100644 index 0000000000000000000000000000000000000000..660570a4d59d700fa422b64d0ec03b0c6decfffc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/branch-model.rst @@ -0,0 +1,47 @@ +.. _branch model: + +|PyInstaller|'s Branch Model +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:`develop` branch: We consider `origin/develop` to be the main branch where the + source code of HEAD always reflects a state with the latest delivered + development changes for the next release. Some would call this the + “integration branchâ€. + +:`master` branch: We consider `origin/master` to be the main branch where the + source code of HEAD always reflects a *production-ready* state. Each commit + to master is considered a new release and will be tagged. + +The |PyInstaller| project doesn't use long living branches (beside `master` +and `develop`) as we don't support bugfixes for several major releases in +parallel. + +Occasionally you might find these branches in the repository: [#]_ + +:`release/` branches: These branches are for preparing the next release. This + is for example: updating the version numbers, completing the change-log, + recompiling the bootloader, rebuilding the manuals. + See ref:`release-workflow` for details about the release process and what + steps have to be performed. + +:`hotfix/` branches: These branches are also meant to prepare for a new + production release, albeit unplanned. + This is what is commonly known as a "hotfix". + +:`feature/` branches: Feature branches (or sometimes called topic branches) + are used to develop new features for the upcoming or a distant future + release. + + +.. [#] This branching-model is basically the same as `Vincent Driessen + described `_ in + this blog. But currently we are not following it strictly. + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/changelog-entries.rst b/3rdparty/pyinstaller-4.3/doc/development/changelog-entries.rst new file mode 100644 index 0000000000000000000000000000000000000000..a6103bcec050d93dac27b6d9d8c1586ebe5b235d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/changelog-entries.rst @@ -0,0 +1,83 @@ +.. _changelog entries: + + +Changelog Entries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If your change is noteworthy, there needs to be a changelog entry so our users +can learn about it! + +To avoid merge conflicts, we use the towncrier_ package to manage our +changelog. ``towncrier`` uses independent files for each pull request -- +called *news fragments* -- instead of one monolithic changelog file. On +release, those news fragments are compiled into our ``doc/CHANGELOG.rst``. + +You don't need to install ``towncrier`` yourself, you just have to abide by a +few simple rules: + +* For each pull request, add a new file into `news/` with a filename + adhering to the ``pr#.(feature|bugfix|breaking).rst`` schema: + For example, :file:`news/42.feature.rst` for a new feature that is + proposed in pull request #42. + + Our categories are: + ``feature``, + ``bugfix``, + ``break`` (breaking changes), + ``hooks`` (all hook-related changes), + ``bootloader``, + ``moduleloader``, + ``doc``, + ``process`` (project infrastructure, development process, etc.), + ``core``, + ``build`` (the bootloader build process), + and + ``tests``. + + +* As with other docs, please use `semantic newlines`_ within news fragments. + +* Prefer present tense or constructions with "now" or "new". + For example: + + - Add hook for my-fancy-library. + - Fix crash when trying to add resources to Windows executable using + ``--resource`` option. + + If the change is relavant only fo a specific platform, use a prefix, + like here: + + - (GNU/Linux) When building with ``--debug`` turn of FORTIFY_SOURCE to ease + debugging. + +* Wrap symbols like modules, functions, or classes into double backticks so + they are rendered in a monospace font. + If you mention functions or other callables, add parentheses at the end of + their names: ``is_module()``. + This makes the changelog a lot more readable. + +* If you want to reference multiple issues, + copy the news fragment to another filename. + ``towncrier`` will merge all news fragments with identical contents + into one entry with multiple links to the respective pull requests. + You may also reference to an existing newsfragment by copying that one. + +* If your pull-request includes several distinct topics, you may want to add + several news fragment files. + For example + ``4242.feature.rst`` for the new feature, + ``4242.bootloader`` for the accompanying change to the bootloader. + +Remember that a news entry is meant for end users +and should only contain details relevant to an end user. + +.. _semantic newlines: http://rhodesmill.org/brandon/2012/one-sentence-per-line/ + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/coding-conventions.rst b/3rdparty/pyinstaller-4.3/doc/development/coding-conventions.rst new file mode 100644 index 0000000000000000000000000000000000000000..53806d1ba135a9c257ba8bebafcdc9683acd0c7a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/coding-conventions.rst @@ -0,0 +1,24 @@ +.. _Coding conventions: + +Coding conventions +=========================== + +The |PyInstaller| project follows the :pep:`8` Style Guide for Python Code for +new code. + +.. TODO: precise how to use flake8 + +Please check your code with a style guide checker, e.g. flake8. + +Please abstain from reformatting existing code, even it it doesn't follow +PEP 8. We will not accept reformatting changes since they make it harder to +review the changes and to follow changes in the long run. For a complete +rationale please see :issue:`2727`. + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/commit-messages.rst b/3rdparty/pyinstaller-4.3/doc/development/commit-messages.rst new file mode 100644 index 0000000000000000000000000000000000000000..405672698f366d8f827d1f8a0744ce512f60e139 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/commit-messages.rst @@ -0,0 +1,296 @@ + +.. _Guidelines for Commits: + +============================= +Guidelines for Commits +============================= + +**Please help keeping code and changes comprehensible for years. +Provide a readable commit-history following this guideline.** + +A commit + +* stands alone as a single, complete, logical change, + +* has a descriptive commit message (see :ref:`below `), + +* has no extraneous modifications (whitespace changes, + fixing a typo in an unrelated file, etc.), + +* follows established coding conventions (:pep:`8`) closely. + +Avoid committing several unrelated changes in one go. It makes merging +difficult, and also makes it harder to determine which change is the culprit +if a bug crops up. + +If you did several unrelated changes before committing, ``git gui`` makes +committing selected parts and even selected lines easy. Try the context menu +within the windows diff area. + +This results in a more readable history, which makes it easier to understand +why a change was made. In case of an issue, it's easier to `git bisect` to +find breaking changes any revert those breaking changes. + + +In Detail +==================== + +A commit should be one (and just one) logical unit. +It should be something that someone might want to patch or +revert in its entirety, and never piece-wise. +If it could be useful in pieces, make separate commits. + +* Make small patches (i.e. work in consistent increments). + +* Reformatting code without functional changes will generally not be + accepted (for rationale see :issue:`2727`). + If such changes are required, separate it into a commit of its own + and document as such. + + This means + that when looking at patches later, we don't have to wade through loads of + non-functional changes to get to the relevant parts of the patch. + +* Especially don't mix different types of change, and put a standard prefix + for each type of change to identify it in your commit message. + +* Abstain refactorings! + If any, restrict refactorings (that should not change functionality) to + their own commit (and document). + +* Restrict functionality changes (bug fix or new feature) to their own + changelists (and document). + +* If your commit-series includes any "fix up" commits + ("Fix typo.", "Fix test.", "Remove commented code.") + please use ``git rebase -i …`` to clean them up + prior to submitting a pull-request. + +* Use ``git rebase -i`` to sort, squash, and fixup commits + prior to submitting the pull-request. + Make it a readable history, easy to understand what you've done. + + +.. _commit messages: + +=================================== +Please Write Good Commit Messages +=================================== + +**Please help keeping code and changes comprehensible for years. +Write good commit messages following this guideline.** + +Commit messages should provide enough information to enable a third party to +decide if the change is relevant to them and if they need to read the change +itself. + +|PyInstaller| is maintained since 2005 and we often need to +comprehend years later why a certain change has been implemented as it is. +What seemed to be obvious when the change was applied may be just obscure +years later. The original contributor may be out of reach, while another +developer needs to comprehend the reasons, side-effects and decisions the +original author considered. + +We learned that commit messages are important to comprehend changes and +thus we are a bit picky about them. + +We may ask you to reword your commit messages. In this case, use ``git +rebase -i …`` and ``git push -f …`` to update your pull-request. See +:ref:`updating pull-request` for details. + + +Content of the commit message +================================== + +**Write meaningful commit messages.** + +* The first line shall be a short sentence + that can stand alone as a short description of the change, + written in the present tense, and + prefixed with the :ref:`subsystem-name `. + See :ref:`below ` for details. + +* The body of the commit message should explain or justify the change, + see :ref:`below ` for details. + +Examples of good commit messages are +:commit:`5c1628e66e18e2bb1c44faa88387b1f627181b43` or +:commit:`73d7710613e26c3d59212e9e031f41a916c1e892`. + + +.. _commit message first line: + +The first Line +===================== + +The first line of the commit message shall + +* be a short sentence (≤ 72 characters maximum, but shoot for ≤ 50), + +* use the present tense ("Add awesome feature.") [#]_, + +* be prefixed with an identifier for the + :ref:`subsystem ` + this commit is related to + ("tests: Fix the frob." or "building: Make all nodes turn faster."), + +* always end with a period. + +* Ending punctuation other than a + period should be used to indicate that the summary line is incomplete and + continues after the separator; "..." is conventional. + +.. [#] Consider these messages as the instructions for what applying the + commit will do. Further this convention matches up with commit messages + generated by commands like git merge and git revert. + + +.. _commit message body: + +The Commit-Message Body +================================== + +The body of a commit log should: + +* explain or justify the change, + + - If you find yourself describing implementation details, this most probably + should go into a source code comment. + + - Please include motivation for the change, and contrasts its + implementation with previous behavior. + + - For more complicate or serious changes please document relevant decisions, + contrast them with other possibilities for chosen, + side-effect you experienced, + or other thinks to keep in mind when touching this peace of code again. + (Although the later *might* better go into a source code comment.) + +* for a bug fix, provide a ticket number or link to the ticket, + +* explain what changes were made at a high level + (`The GNU ChangeLog + `_ + standard is worth a read), + +* be word-wrapped to 72 characters per line, don't go over 80; and + +* separated by a blank line from the first line. + +* Bullet points and numbered lists are okay, too:: + + * Typically a hyphen or asterisk is used for the bullet, preceded by a + single space, with blank lines in between, but conventions vary here. + + * Use a hanging indent. + +* Do not start your commit message with a hash-mark (``#``) as git some git + commands may dismiss these message. (See `this discussion + `_. + for details.) + + +.. _commit message standard prefixes: + +Standard prefixes +======================== + +Please state the "subsystem" this commit is related to as a prefix in the +first line. Do learn which prefixes others used for the files you changed you +can use ``git log --oneline path/to/file/or/dir``. + +Examples for "subsystems" are: + +* ``Hooks`` for hook-related changes + +* ``Bootloader``, ``Bootloader build`` for the bootloader or it's build system + +* ``depend`` for the dependency detection parts (:file:`PyInstaller/depend`) + +* ``building`` for the building part (:file:`PyInstaller/building`) + +* ``compat`` for code related to compatibility of different Python versions + (primary :file:`PyInstaller/compat.py`) + +* ``loader`` + +* ``utils``, ``utils/hooks`` + +* ``Tests``, ``Test/CI``: For changes to the test suite (incl. requirements), + resp. the CI. + +* ``modulegraph``: changes related to :file:`PyInstaller/lib/modulegraph` + +* ``Doc``, ``Doc build`` for the documentation content resp. it's build + system. You may want to specify the chapter or section too. + + +Please set the correct Author +==================================== + +.. highlight: bash + +Please make sure you have setup git to use the correct name and email for your +commits. Use the same name and email on all machines you may push from. +Example:: + + # Set name and email + git config --global user.name "Firstname Lastname" + git config --global user.email "your_email@youremail.com" + +This will set this name and email-address to be used for all git-repos you are +working on on this system. To set it for just the PyInstaller repo, remove the +``--global`` flag. + +Alternatively you may use :command:`git gui` :menuselection:`--> Edit --> +Options ...` to set these values. + + +Further Reading +======================= + +Further hints and tutorials about writing good commit messages can also be +found at: + +* `FreeBSD Committer's Guide + `_ + +* http://365git.tumblr.com/post/3308646748/writing-git-commit-messages + +* http://wincent.com/blog/commit-messages: The Good, the Bad and the Ugly. + +* http://wiki.scummvm.org/index.php/Commit_Guidelines + +* http://lbrandy.com/blog/2009/03/writing-better-commit-messages/ + +* http://blog.looplabel.net/2008/07/28/best-practices-for-version-control/ + +* http://subversion.apache.org/docs/community-guide/conventions.html (Targeted + a bit too much to subversion usage, which does not use such fine-grained + commits as we ask you strongly to use.) + +Credits +========================= + +This page was composed from material found at + +* http://hackage.haskell.org/trac/ghc/wiki/WorkingConventions/Git + +* http://lbrandy.com/blog/2009/03/writing-better-commit-messages/ + +* http://365git.tumblr.com/post/3308646748/writing-git-commit-messages + +* http://www.catb.org/esr/dvcs-migration-guide.html + +* https://git.dthompson.us/presentations.git/tree/HEAD:/happy-patching + +* and other places. + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/documentation.rst b/3rdparty/pyinstaller-4.3/doc/development/documentation.rst new file mode 100644 index 0000000000000000000000000000000000000000..364b50074c76dbda1988c277c8ab5e22022d5c4b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/documentation.rst @@ -0,0 +1,93 @@ + +.. _writing documentation: + +============================================ +Improving and Building the Documentation +============================================ + +|PyInstaller|'s documentation is created using Sphinx_. +Sphinx uses reStructuredText_ as its markup language, and many of its +strengths come from the power and straightforwardness of reStructuredText and +its parsing and translating suite, Docutils_. + +.. _reStructuredText: http://docutils.sourceforge.net/rst.html +.. _Sphinx: http://www.sphinx-doc.org/ +.. _Docutils: http://docutils.sourceforge.net/ + +The documentation is maintained in the Git repository along with the code +and pushing to the ``develop`` branch will create +a new version at https://pyinstaller.readthedocs.io/en/latest/. + + +For **small changes** (like typos) you may just fork |PyInstaller| on Github, +edit the documentation online and create a pull-request. + +For anything else we ask you to clone the repository and verify your changes +like this:: + + pip install sphinx sphinx_rtd_theme + cd doc + make html + xdg-open _build/html/index.html + + +Please watch out for any warnings and errors while building the documentation. +In your browser check if the markup is valid +prior to pushing your changes and creating the pull-request. +Please also run:: + + make clean + ... + make html + +to verify once again everything is fine. Thank you! + + +We may ask you to rework your changes or reword your commit messages. In this +case, use ``git rebase -i …`` and ``git push -f …`` to update your +pull-request. See :ref:`updating pull-request` for details. + + +|PyInstaller| extensions +---------------------------- + +For the |PyInstaller| documentation there are roles available [*]_ +in additon to the ones from `Sphinx`__ and docutils__. + +__ http://www.sphinx-doc.org/en/stable/markup/inline.html +__ http://www.sphinx-doc.org/en/stable/rest.html#inline-markup + +.. rst:role:: commit + + Refer to a commit, creating a web-link to the online git repository. + The commit-id will be shortened to 8 digits for readability. + Example: ``:commit:`a1b2c3d4e5f6a7b8c9``` will become + :commit:`a1b2c3d4e5f6a7b8c9`. + +.. rst:role:: issue + + Link to an issue or pull-request number at Github. + Example: ``:issue:`123``` will become :issue:`123`. + + +.. [*] Defined in :file:`doc/_extensions/pyi_sphinx_roles.py` + + +reStructuredText Cheat-sheet +--------------------------------------- + +* Combining markup and links:: + + The easies way to install PyInstaller is using |pip|_:: + + .. |pip| replace:: :command:`pip` + .. _pip: https://pip.pypa.io/ + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/git.rst b/3rdparty/pyinstaller-4.3/doc/development/git.rst new file mode 100644 index 0000000000000000000000000000000000000000..2b37b95280567d64865ef4925ddb5eaf23cdd823 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/git.rst @@ -0,0 +1,32 @@ + +New to GitHub or Git? +========================== + +Our development workflow is build around Git and GitHub. +Please take your time to become familiar with these. +If you are new to GitHub, +`GitHub has instructions `_ +for getting you started. +If you are new to Git there are a +`tutorial `_ and an +`excellent book available online `_. + + +.. Don't use a toctree here to avoid the sections to show up twice in then + toc of development/index. + +**Further Reading** + +* :ref:`commit messages` +* :ref:`creating pull-requests` +* :ref:`updating pull-request` +* :ref:`branch model` + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/index.rst b/3rdparty/pyinstaller-4.3/doc/development/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..02f866091dea4c16d92701f172206449e8bd3a0f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/index.rst @@ -0,0 +1,31 @@ + +.. _development guide: + +============================= +Development Guide +============================= + + +.. include:: quickstart.txt + + +.. toctree:: + :maxdepth: 2 + :caption: Developer Documentation + + git + coding-conventions + testing + commit-messages + documentation + pull-requests + changelog-entries + venv + branch-model + + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/pull-requests.rst b/3rdparty/pyinstaller-4.3/doc/development/pull-requests.rst new file mode 100644 index 0000000000000000000000000000000000000000..c1315c2b2a586632d9c2af324983a736fc58b8a2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/pull-requests.rst @@ -0,0 +1,116 @@ +.. _creating pull-requests: + +Creating Pull-Requests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. highlight: bash + +Example +............. + +* Create an account on https://github.com + +* Create a fork of project `pyinstaller/pyinstaller + `_ on github. + +* Set up your git client by following `this documentation on github + `_. + +* Clone your fork to your local machine.:: + + git clone git@github.com:YOUR_GITHUB_USERNAME/pyinstaller.git + cd pyinstaller + +* Develop your changes (aka "hack") + + * Create a branch to work on (optional):: + + git checkout -b my-patch + + * If you are going to implement a hook, start with creating a minimalistic + build-test (see below). You will need to test your hook anyway, so why not + use a build-test from the start? + + * Incorporate your changes into |PyInstaller|. + + * Test your changes by running *all* build tests to ensure nothing else is + broken. Please test on as many platform as you can. + + * You may reference relevant issues in commit messages (like #1259) to make + GitHub link issues and commits together, and with phrase like “fixes #1259†+ you can even close relevant issues automatically. + +* Synchronize your fork with the PyInstaller upstream repository. There are two + ways for this: + + 1. Rebase you changes on the current development head (preferred, as it + results in a straighter history and conflicts are easier to solve):: + + git remote add upstream https://github.com/pyinstaller/pyinstaller.git + git checkout my-patch + git pull --rebase upstream develop + git log --online --graph + + 2. Merge the current development head into your changes:: + + git remote add upstream https://github.com/pyinstaller/pyinstaller.git + git fetch upstream develop + git checkout my-patch + git merge upstream/develop + git log --online --graph + + For details see `syncing a fork at github + `_. + +* Push your changes up to your fork:: + + git push + +* Open the *Pull Requests* page at + https://github.com/YOUR_GITHUB_USERNAME/pyinstaller/pulls + and click “New pull requestâ€. + That’s it. + + +.. _updating pull-request: + +Updating a Pull-Request +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We may ask you to update your pull-request to improve it's quality or for +other reasons. In this case, use ``git rebase -i …`` and ``git push -f …`` as +explained below. [#]_ Please *do not* close the pull-request and open a new +one – this would kill the discussion thread. + +This is the workflow without actually changing the base:: + + git checkout my-branch + # find the commit your branch forked from 'develop' + mb=$(git merge-base --fork-point develop) + # rebase interactively without actually changing the base + git rebase -i $mb + # … process rebase + git push -f my-fork my-branch + + +Or if you want to actually base your code on the current development head:: + + git checkout my-branch + # rebase interactively on 'develop' + git rebase -i develop + # … process rebase + git push -f my-fork my-branch + + +.. [#] There are other ways to update a pull-request, e.g. by "amending" a + commit. But for casual (and not-so-casual :-) users ``rebase -i`` might be + the easiest way. + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/quickstart.txt b/3rdparty/pyinstaller-4.3/doc/development/quickstart.txt new file mode 100644 index 0000000000000000000000000000000000000000..90b1b55a31e929bc11084c79f2f763be59c2b2f2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/quickstart.txt @@ -0,0 +1,67 @@ + + +Quick-start +============================= + +* Our git repository is at https://github.com/pyinstaller/pyinstaller:: + + git clone https://github.com/pyinstaller/pyinstaller + + - Development is done on the `develop` branch. Pull-request shall be filed + against this branch. + + - Releases will reside on the `master` branch. + +* Install required testing tools:: + + pip install -r tests/requirements-tools.txt + +* Commit as often as you’d like, but squash or otherwise + rewrite your commits into logical patches before asking + for code review. ``git rebase -i`` is your friend. + Read the :ref:`»» Detailed Commit Guideline ` + for more information. + + Reformatting code without functional changes will generally not be accepted + (for rational see :issue:`2727`). + +* Write meaningful commit messages. + + - The first line shall be a short sentence + that can stand alone as a short description of the change, + written in the present tense, and + prefixed with the :ref:`subsystem-name `. + + - The body of the commit message should explain or justify the change. + Read the :ref:`»» Detailed Commit Message Rules ` + for more information. + +* Provide tests that cover your changes and try to run the tests locally + first. + +* Submit pull-requests against the ``develop`` branch. + Mind adding a :ref:`changelog entry ` + so our users can learn about your change! + +* For new files mind adding the copyright header, see + |PyInstaller/init.py|_ + (also mind updating to the current year). + + .. |PyInstaller/init.py| replace:: :file:`PyInstaller/__init__.py` + .. _PyInstaller/init.py: https://github.com/pyinstaller/pyinstaller/blob/develop/PyInstaller/__init__.py + +* In response to feedback, squash the new "fix up" commits + into the respective commit that is being fixed + with an interactive rebase (``git rebase -i``). + :ref:`Push the new, rewritten branch ` + with a ``git push --force``. + (Scary! But github doesn’t play nicely with a safer method.) + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/testing.rst b/3rdparty/pyinstaller-4.3/doc/development/testing.rst new file mode 100644 index 0000000000000000000000000000000000000000..d38b6dde63763533b30180bc0ff66b97b4247620 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/testing.rst @@ -0,0 +1,73 @@ +.. TODO: incorporate test/README.md + +.. _running-the-test-suite: + +Running the Test Suite +=========================== + +To run the test-suite, please proceed as follows. + +1. If you don't have a git clone of |PyInstaller|, first fetch the current + development head, either using pip, …:: + + pip download --no-deps https://github.com/pyinstaller/pyinstaller/archive/develop.zip + unzip develop.zip + cd pyinstaller-develop/ + + … or using git:: + + git clone https://github.com/pyinstaller/pyinstaller.git + cd pyinstaller + +2. Then setup a fresh virtualenv_ for running the test suite in and install + all required tools:: + + pip install --user virtualenv + virtualenv /tmp/venv + . /tmp/venv/bin/activate + pip install -r tests/requirements-tools.txt + +3. To run a single test use e.g.:: + + pytest tests/unit -k test_collect_submod_all_included + +4. Run the test-suite:: + + pytest tests/unit tests/functional + + This only runs the tests for the core functionality and some packages from + the Python standard library. + +5. To get better coverage, including many of the available hooks, you need to + download the Python packages to be tested. For this please run:: + + pip install -U -r tests/requirements-libraries.txt + pytest tests/unit tests/functional + +.. note: + + This section is still incomplete. For now please refer to the + |tests/README|_ file. + +.. |tests/README| replace:: ``tests/README.md`` +.. _tests/README: https://github.com/pyinstaller/pyinstaller/blob/develop/tests/README.md + + +To learn how we run the test-suite in the continuous integration tests please +have a look at |.travis.yml|_ (for GNU/Linux and OS X) and |appveyor.yml|_ +(for Windows). + + +.. |.travis.yml| replace:: ``.travis.yml`` +.. _.travis.yml: https://github.com/pyinstaller/pyinstaller/blob/develop/.travis.yml + +.. |appveyor.yml| replace:: ``appveyor.yml`` +.. _appveyor.yml: https://github.com/pyinstaller/pyinstaller/blob/develop/appveyor.yml + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/development/venv.rst b/3rdparty/pyinstaller-4.3/doc/development/venv.rst new file mode 100644 index 0000000000000000000000000000000000000000..ccffe7fb4cb77de967b3481a46673055cddac273 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/development/venv.rst @@ -0,0 +1,49 @@ + +pyenv and PyInstaller +============================= + +.. TODO: finalize this section + +.. note:: + This section is a still a draft. + Please :ref:`help extending it `. + + +* clone pyenv repository:: + + git clone https://github.com/yyuu/pyenv.git ~/.pyenv + +* clone virtualenv plugin:: + + git clone https://github.com/yyuu/pyenv-virtualenv.git \ + ~/.pyenv/plugins/pyenv-virtualenv + +* add to `.bashrc` or `.zshrc`:: + + # Add 'pyenv' to PATH. + export PYENV_ROOT="$HOME/.pyenv" + export PATH="$PYENV_ROOT/bin:$PATH" + + # Enable shims and autocompletion for pyenv. + eval "$(pyenv init -)" + # Load pyenv-virtualenv automatically by adding + # # the following to ~/.zshrc: + # + eval "$(pyenv virtualenv-init -)" + +* Install python version with shared libpython (necessary for PyInstaller to + work):: + + env PYTHON_CONFIGURE_OPTS="--enable-shared" pyenv install 3.5.0 + +* setup virtualenv ``pyenv virtualenv 3.5.0 venvname`` +* activate virtualenv ``pyenv activate venvname`` +* deactivate virtualenv ``pyenv deactivate`` + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/feature-notes.rst b/3rdparty/pyinstaller-4.3/doc/feature-notes.rst new file mode 100644 index 0000000000000000000000000000000000000000..891ac739eb25acf8fe414a2bf391d20102a468fc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/feature-notes.rst @@ -0,0 +1,142 @@ +.. _feature notes: + +=============================== +Notes about specific Features +=============================== + +This sections describes details about specific features. For a +:ref:`full list of features ` +please refer to the website. + + +Ctypes Dependencies +========================= + +Ctypes is a foreign function library for Python, that allows calling functions +present in shared libraries. Those libraries are not imported as Python +packages, because they are not picked up via Python imports: their path is +passed to ctypes instead, which deals with the shared library directly; this +caused <1.4 PyInstaller import detect machinery to miss those libraries, +failing the goal to build self-contained PyInstaller executables:: + + from ctypes import * + # This will pass undetected under PyInstaller detect machinery, + # because it's not a direct import. + handle = CDLL("/usr/lib/library.so") + handle.function_call() + + +Solution in |PyInstaller| +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +PyInstaller contains a pragmatic implementation of Ctypes dependencies: it +will search for simple standard usages of ctypes and **automatically** track +and bundle the referenced libraries. The following usages will be correctly +detected:: + + CDLL("library.so") + WinDLL("library.so") + ctypes.DLL("library.so") + cdll.library # Only valid under Windows - a limitation of ctypes, not PyInstaller's + windll.library # Only valid under Windows - a limitation of ctypes, not PyInstaller's + cdll.LoadLibrary("library.so") + windll.LoadLibrary("library.so") + + +More in detail, the following restrictions apply: + +* **only libraries referenced by bare filenames (e.g. no leading paths) will + be handled**; handling absolute paths would be impossible without modifying + the bytecode as well (remember that while running frozen, ctypes would keep + searching the library at that very absolute location, whose presence on the + host system nobody can guarantee), and relative paths handling would require + recreating in the frozen executable the same hierarchy of directories + leading to the library, in addition of keeping track of which the current + working directory is; + +* **only library paths represented by a literal string will be detected and + included in the final executable**: PyInstaller import detection works by + inspecting raw Python bytecode, and since you can pass the library path to + ctypes using a string (that can be represented by a literal in the code, but + also by a variable, by the return value of an arbitrarily complex function, + etc...), it's not reasonably possible to detect **all** ctypes dependencies; + +* **only libraries referenced in the same context of ctypes' invocation will + be handled**. + +We feel that it should be enough to cover most ctypes' usages, with little or +no modification required in your code. + +If |PyInstaller| does not detect a library, you can add it to your +bundle by passing the respective information to ``--add-binary`` option or +:ref:`listing it in the .spec-file `. If your frozen +application will be able to pick up the library at run-time can not be +guaranteed as it depends on the detailed implementation. + + +Gotchas +~~~~~~~~~~~~~~~ + +The ctypes detection system at :ref:`Analysis time ` +is based on ``ctypes.util.find_library()``. +This means that you have to make sure +that while performing ``Analysis`` and running frozen, +all the environment values ``find_library()`` uses to search libraries +are aligned to those when running un-frozen. +Examples include using ``LD_LIBRARY_PATH`` or ``DYLD_LIBRARY_PATH`` to +widen ``find_library()`` scope. + + +SWIG support +========================= + +|PyInstaller| tries to detect binary modules created by SWIG. This detection +requires: + +- The Python wrapper module must be imported somewhere in your application + (or by any of the modules it uses). + +- The wrapper module must be available as source-code and it's first line must + contain the text ``automatically generated by SWIG``. + +- The C-module must have the same name as the wrapper module prefixed with an + underscore (``_``). (This is a SWIG restriction already.) + +- The C-module must sit just beside the wrapper module (thus a relative import + would work). + +Also some restrictions apply, due to the way the SWIG wrapper is +implemented: + +- The C-module will become a `global` module. As a consequence, you can not + use two SWIG modules with the same basename (e.g. ``pkg1._cmod`` and + ``pkg2._cmod``), as one would overwrite the other. + + +Cython support +====================== + +|PyInstaller| can follow import statements that refer to Cython C object +modules and bundle them – like for any other module implemented in C. + +But – again, as for any other module implemented in C – |PyInstaller| can not +determine if the Cython C object module is importing some Python module. +These will typically show up as in a traceback like this +(mind the ``.pyx`` extension):: + + Traceback (most recent call last): + […] + File "myapp\cython_module.pyx", line 3, in init myapp.cython_module + ModuleNotFoundError: No module named 'csv' + +So if you are using a Cython C object module, which imports Python modules, +you will have to list these as ``--hidden-import``. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/help2rst.py b/3rdparty/pyinstaller-4.3/doc/help2rst.py new file mode 100644 index 0000000000000000000000000000000000000000..6306c06b491a4b983d5a9899470280bff10859ae --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/help2rst.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, PyInstaller Development Team. +# Copyright (c) 2015-2020, Hartmut Goebel. +# +# 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 script reformats the output of `myprog --help` to decent rst. + +Currently handles optparse and argparse output. Removes everything +before "Options" resp. "optional arguments" and after "Obsolete +options". +""" +import argparse +import subprocess +import textwrap +import re +import sys +import os + +__copyright__ = "Copyright (c) 2015-2021 PyInstaller Development Team, " \ + "Copyright (c) 2015-2020 Hartmut Goebel" +__author__ = "Hartmut Goebel " + + +def gen_headings(program, text, headings_character): + text = (t.rstrip() for t in text.splitlines()) + new_text = [] + dedent = 0 + for line in text: + heading = line and not line.startswith((' ', '-')) + if heading: + # remove trailing colon + line = line.rstrip(':') + dedent = 0 + elif dedent: + # dedent for this section already known + assert line[:dedent].strip() == '', line + line = line[dedent:] + else: + # dedent for this section not yet known + # check for next optional arguments (starting with a dash) + leading_whitespace = re.findall(r'^[ \t]*(?=-)', line) + if leading_whitespace: + dedent = len(leading_whitespace[0]) + line = line[dedent:] + if heading: + # add a sphinx reference label + new_text.extend(('', + '.. _%s %s:' % (program, line.lower()), + '')) + new_text.append(line) + if heading: + # append underline + new_text.append(headings_character * len(line)) + new_text.append('') # empty line for readability only + return '\n'.join(new_text) + + +def process(program, generate_headings, headings_character): + help = subprocess.check_output([sys.executable, program, '--help'], + universal_newlines=True) + if '\nOptions:' in help: + # optparse style + help = help.split('\nOptions:', 1)[1] + elif '\noptional arguments:' in help: + # argparse style + help = help.split('\noptional arguments:', 1)[1] + else: + raise SystemError('unexpected format of output for --help') + # Remove any obsolete options + help = re.split(r'\n\s*Obsolete options', help)[0] + + help = help.strip('\n') + # escape stars prior to other processing + help = help.replace('*', r'\*') + # Change troublesome argparse text that the optparse-like docutils parser + # doesn't understand. + help = help.replace('{all,imports,bootloader,noarchive}', + '') + if generate_headings: + program = os.path.splitext(os.path.basename(program))[0].lower() + help = textwrap.dedent(help) + help = gen_headings(program, help, headings_character) + return help + + +def to_file(program, generate_headings, headings_character, outfile): + help = process(program, generate_headings, headings_character) + with open(outfile, 'w') as ofh: + print(help, file=ofh) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--generate-headings', + action='store_true', default=True, + help=("Generate section headings from arumgent groups" + " (this is the default)")) + parser.add_argument('--no-generate-headings',action='store_false', + dest='generate_headings') + g = parser.add_argument_group('tst') + g.add_argument('--headings-character', default="-", + help=("Character to use for underlining section headers" + " (default: -)")) + parser.add_argument('program') + args = parser.parse_args() + print(process(**vars(args))) + +if __name__ == '__main__': + main() diff --git a/3rdparty/pyinstaller-4.3/doc/hooks.rst b/3rdparty/pyinstaller-4.3/doc/hooks.rst new file mode 100644 index 0000000000000000000000000000000000000000..f254f312ec03f0f3c382a2fcd27b553fee1cbc67 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/hooks.rst @@ -0,0 +1,709 @@ +.. _understanding pyinstaller hooks: + +Understanding PyInstaller Hooks +================================== + +.. note:: + + We strongly encourage package developers + to provide hooks with their packages. + See section :ref:`provide hooks with package` for how easy this is. + +In summary, a "hook" file extends |PyInstaller| to adapt it to +the special needs and methods used by a Python package. +The word "hook" is used for two kinds of files. +A *runtime* hook helps the bootloader to launch an app. +For more on runtime hooks, see :ref:`Changing Runtime Behavior`. +Other hooks run while an app is being analyzed. +They help the Analysis phase find needed files. + +The majority of Python packages use normal methods of importing +their dependencies, and |PyInstaller| locates all their files without difficulty. +But some packages make unusual uses of the Python import mechanism, +or make clever changes to the import system at runtime. +For this or other reasons, |PyInstaller| cannot reliably find +all the needed files, or may include too many files. +A hook can tell about additional source files or data files to import, +or files not to import. + +A hook file is a Python script, and can use all Python features. +It can also import helper methods from ``PyInstaller.utils.hooks`` +and useful variables from ``PyInstaller.compat``. +These helpers are documented below. + +The name of a hook file is :file:`hook-{full.import.name}.py`, +where *full.import.name* is +the fully-qualified name of an imported script or module. +You can browse through the existing hooks in the +``hooks`` folder of the |PyInstaller| distribution folder +and see the names of the packages for which hooks have been written. +For example ``hook-PyQt5.QtCore.py`` is a hook file telling +about hidden imports needed by the module ``PyQt5.QtCore``. +When your script contains ``import PyQt5.QtCore`` +(or ``from PyQt5 import QtCore``), +Analysis notes that ``hook-PyQt5.QtCore.py`` exists, and will call it. + +Many hooks consist of only one statement, an assignment to ``hiddenimports``. +For example, the hook for the `dnspython`_ package, called +``hook-dns.rdata.py``, has only this statement:: + + hiddenimports = [ + "dns.rdtypes.*", + "dns.rdtypes.ANY.*" + ] + +When Analysis sees ``import dns.rdata`` or ``from dns import rdata`` +it calls ``hook-dns.rdata.py`` and examines its value +of ``hiddenimports``. +As a result, it is as if your source script also contained:: + + import dns.rdtypes.* + import dsn.rdtypes.ANY.* + +A hook can also cause the addition of data files, +and it can cause certain files to *not* be imported. +Examples of these actions are shown below. + +When the module that needs these hidden imports is useful only to your project, +store the hook file(s) somewhere near your source file. +Then specify their location to the ``pyinstaller`` or ``pyi-makespec`` +command with the ``--additional-hooks-dir=`` option. +If the hook file(s) are at the same level as the script, +the command could be simply:: + + pyinstaller --additional-hooks-dir=. myscript.py + +If you write a hook for a module used by others, +please ask the package developer to +:ref:`include the hook with her/his package ` +or send us the hook file so we can make it available. + + +How a Hook Is Loaded +----------------------- + +A hook is a module named :file:`hook-{full.import.name}.py` +in a folder where the Analysis object looks for hooks. +Each time Analysis detects an import, it looks for a hook file with +a matching name. +When one is found, Analysis imports the hook's code into a Python namespace. +This results in the execution of all top-level statements in the hook source, +for example import statements, assignments to global names, and +function definitions. +The names defined by these statements are visible to Analysis +as attributes of the namespace. + +Thus a hook is a normal Python script and can use all normal Python facilities. +For example it could test ``sys.version`` and adjust its +assignment to ``hiddenimports`` based on that. +There are many hooks in the |PyInstaller| installation, +but a much larger collection can be found in the +`community hooks package `_. +Please browse through them for examples. + +.. _provide hooks with package: + +Providing PyInstaller Hooks with your Package +------------------------------------------------ + +As a package developer you can provide hooks for PyInstaller +within your package. +This has the major benefit +that you can easily adopt the hooks +when your package changes. +Thus your package's users don't need to wait until PyInstaller +might catch up with these changes. +If both PyInstaller and your package provide hooks for some module, +your package's hooks take precedence, +but can still be overridden by the command line option +``--additional-hooks-dir``. + + +You can tell PyInstaller about the additional hooks +by defining some simple `setuptools entry-points +`_ +in your package. +Therefore add entries like these to your :file:`setup.cfg`:: + + [options.entry_points] + pyinstaller40 = + hook-dirs = pyi_hooksample.__pyinstaller:get_hook_dirs + tests = pyi_hooksample.__pyinstaller:get_PyInstaller_tests + +This defines two entry-points: + +:``pyinstaller40.hook-dirs`` for hook registration: + + This entry point refers to a function + that will be invoked with no parameters. + It must return a sequence of strings, + each element of which provides an additional absolute path + to search for hooks. + This is equivalent to passing the ``--additional-hooks-dir`` + command-line option to PyInstaller for each string in the sequence. + + In this example, the function is ``get_hook_dirs() -> List[str]``. + +:``pyinstaller40.tests`` for test registration: + + This entry point refers to a function + that will be invoked with no parameters. + It must return a sequence of strings, + each element of which provides an additional absolute path + to a directory tree or to a Python source file. + These paths are then passed to `pytest` for test discovery. + This allows both testing by this package and by PyInstaller. + + In this project, the function is ``get_PyInstaller_tests() -> List[str]``. + +A sample project providing a guide for +integrating PyInstaller hooks and tests into a package +is available at +https://github.com/pyinstaller/hooksample. +This project demonstrates defining a library +which includes PyInstaller hooks along with tests for those hooks +and sample file for integration into CD/CI testing. +Detailed documentation about this sample project +is available at +https://pyinstaller-sample-hook.readthedocs.io/en/latest/. + + +Hook Global Variables +----------------------- + +A majority of the existing hooks consist entirely of assignments of +values to one or more of the following global variables. +If any of these are defined by the hook, Analysis takes their values and +applies them to the bundle being created. + +``hiddenimports`` + A list of module names (relative or absolute) that should + be part of the bundled app. + This has the same effect as the ``--hidden-import`` command line option, + but it can contain a list of names and is applied automatically + only when the hooked module is imported. + Example:: + + hiddenimports = ['_gdbm', 'socket', 'h5py.defs'] + +``excludedimports`` + A list of absolute module names that should + *not* be part of the bundled app. + If an excluded module is imported only by the hooked module or one + of its sub-modules, the excluded name and its sub-modules + will not be part of the bundle. + (If an excluded name is explicitly imported in the + source file or some other module, it will be kept.) + Several hooks use this to prevent automatic inclusion of + the ``tkinter`` module. Example:: + + excludedimports = [modname_tkinter] + +``datas`` + A list of files to bundle with the app as data. + Each entry in the list is a tuple containing two strings. + The first string specifies a file (or file "glob") in this system, + and the second specifies the name(s) the file(s) are to have in + the bundle. + (This is the same format as used for the ``datas=`` argument, + see :ref:`Adding Data Files`.) + Example:: + + datas = [ ('/usr/share/icons/education_*.png', 'icons') ] + + If you need to collect multiple directories or nested directories, + you can use helper functions from the ``PyInstaller.utils.hooks`` module + (see below) to create this list, for example:: + + datas = collect_data_files('submodule1') + datas += collect_data_files('submodule2') + + In rare cases you may need to apply logic to locate + particular files within the file system, + for example because the files are + in different places on different platforms or under different versions. + Then you can write a ``hook()`` function as described + below under :ref:`The hook(hook_api) Function`. + + +``binaries`` + A list of files or directories to bundle as binaries. + The format is the same as ``datas`` (tuples with strings that + specify the source and the destination). + Binaries is a special case of ``datas``, in that PyInstaller will + check each file to see if it depends on other dynamic libraries. + Example:: + + binaries = [ ('C:\\Windows\\System32\\*.dll', 'dlls') ] + + Many hooks use helpers from the ``PyInstaller.utils.hooks`` module + to create this list (see below):: + + binaries = collect_dynamic_libs('zmq') + + +Useful Items in ``PyInstaller.compat`` +---------------------------------------- + +A hook may import the following names from ``PyInstaller.compat``, +for example:: + + from PyInstaller.compat import modname_tkinter, is_win + +``is_py36``, ``is_py37``, ``is_py38``, ``is_py39``: + True when the current version of Python is at least 3.6, 3.7, 3.8 or 3.9 respectively. + +``is_win``: + True in a Windows system. +``is_cygwin``: + True when ``sys.platform=='cygwin'``. +``is_darwin``: + True in Mac OS X. +``is_linux``: + True in any GNU/Linux system (``sys.platform.startswith('linux')``). +``is_solar``: + True in Solaris. +``is_aix``: + True in AIX. +``is_freebsd``: + True in FreeBSD. +``is_openbsd``: + True in OpenBSD. + +``is_venv``: + True in any virtual environment (either virtualenv or venv). +``base_prefix``: + String, the correct path to the base Python installation, + whether the installation is native or a virtual environment. + +``modname_tkinter``: + String ``tkinter`` (this module was named differently in Python 2). + To prevent an unnecessary import of Tkinter, write:: + + from PyInstaller.compat import modname_tkinter + excludedimports = [ modname_tkinter ] + +``EXTENSION_SUFFIXES``: + List of Python C-extension file suffixes. Used for finding all + binary dependencies in a folder; see file:`hook-cryptography.py` + for an example. + +Useful Items in ``PyInstaller.utils.hooks`` +-------------------------------------------- + +A hook may import useful functions from ``PyInstaller.utils.hooks``. +Use a fully-qualified import statement, for example:: + + from PyInstaller.utils.hooks import collect_data_files, eval_statement + +The ``PyInstaller.utils.hooks`` functions listed here are generally useful +and used in a number of existing hooks. +There are several more functions besides these that serve the needs +of specific hooks, such as hooks for PyQt5. +You are welcome to read the ``PyInstaller.utils.hooks`` module +(and read the existing hooks that import from it) to get code and ideas. + +``exec_statement( 'statement' )``: + Execute a single Python statement in an externally-spawned interpreter + and return the standard output that results, as a string. + Examples:: + + tk_version = exec_statement( + "from _tkinter import TK_VERSION; print(TK_VERSION)" + ) + + mpl_data_dir = exec_statement( + "import matplotlib; print(matplotlib._get_data_path())" + ) + datas = [ (mpl_data_dir, "") ] + +``eval_statement( 'statement' )``: + Execute a single Python statement in an externally-spawned interpreter. + If the resulting standard output text is not empty, apply + the ``eval()`` function to it; else return None. Example:: + + databases = eval_statement(''' + import sqlalchemy.databases + print(sqlalchemy.databases.__all__) + ''') + for db in databases: + hiddenimports.append("sqlalchemy.databases." + db) + +``is_module_satisfies( requirements, version=None, version_attr='__version__' )``: + Check that the named module (fully-qualified) exists and satisfies the + given requirement. Example:: + + if is_module_satisfies('sqlalchemy >= 0.6'): + + This function provides robust version checking based on the same low-level + algorithm used by ``easy_install`` and ``pip``, and should always be + used in preference to writing your own comparison code. + In particular, version strings should never be compared lexicographically + (except for exact equality). + For example ``'00.5' > '0.6'`` returns True, which is not the desired result. + + The ``requirements`` argument uses the same syntax as supported by + the `Package resources`_ module of setup tools (follow the link to + see the supported syntax). + + The optional ``version`` argument is is a PEP0440-compliant, + dot-delimited version specifier such as ``'3.14-rc5'``. + + When the package being queried has been installed by ``easy_install`` + or ``pip``, the existing setup tools machinery is used to perform the test + and the ``version`` and ``version_attr`` arguments are ignored. + + When that is not the case, the ``version`` argument is taken as the + installed version of the package + (perhaps obtained by interrogating the package in some other way). + When ``version`` is ``None``, the named package is imported into a + subprocess, and the ``__version__`` value of that import is tested. + If the package uses some other name than ``__version__`` for its version + global, that name can be passed as the ``version_attr`` argument. + + For more details and examples refer to the function's doc-string, found + in ``Pyinstaller/utils/hooks/__init__.py``. + + +``collect_all( 'package-name', include_py_files=False )``: + Given a package name as a string, this function returns a tuple of ``datas, binaries, + hiddenimports`` containing all data files, binaries, and modules in the given + package, including any modules specified in the requirements for the + distribution of this module. The value of ``include_py_files`` is passed + directly to ``collect_data_files``. + + Typical use: ``datas, binaries, hiddenimports = collect_all('my_module_name')``. + For example, ``hook-gevent.py`` invokes ``collect_all``, which gathers: + + * All data files, such as ``__greenlet_primitives.pxd``, ``__hub_local.pxd``, + and many, many more. + * All binaries, such as ``__greenlet_primitives.cp37-win_amd64.pyd`` (on a + Windows 64-bit install) and many, many more. + * All modules in ``gevent``, such as ``gevent.threadpool``, + ``gevent._semaphore``, and many, many more. + * All requirements. ``pip show gevent`` gives ``Requires: cffi, greenlet``. + Therefore, the ``cffi`` and ``greenlet`` modules are included. + +``collect_submodules( 'package-name', pattern=None )``: + Returns a list of strings that specify all the modules in a package, + ready to be assigned to the ``hiddenimports`` global. + Returns an empty list when ``package`` does not name a package + (a package is defined as a module that contains a ``__path__`` attribute). + + The ``pattern``, if given, is function to filter through the submodules + found, selecting which should be included in the returned list. It takes one + argument, a string, which gives the name of a submodule. Only if the + function returns true is the given submodule is added to the list of + returned modules. For example, ``filter=lambda name: 'test' not in + name`` will return modules that don't contain the word ``test``. + +``is_module_or_submodule( name, mod_or_submod )``: + This helper function is designed for use in the ``filter`` argument of + ``collect_submodules``, by returning ``True`` if the given ``name`` is + a module or a submodule of ``mod_or_submod``. For example: + ``collect_submodules('foo', lambda name: not is_module_or_submodule(name, + 'foo.test'))`` excludes ``foo.test`` and ``foo.test.one`` but not + ``foo.testifier``. + +``collect_data_files( package, include_py_files=False, subdir=None, excludes=None, includes=None )``: + This routine produces a list of ``(source, dest)`` non-Python (i.e. data) + files which reside in ``package``. Its results can be directly assigned to + ``datas`` in a hook script; see, for example, ``hook-sphinx.py``. + Parameters: + + - The ``package`` parameter is a string which names the package. + - By default, all Python executable files (those ending in ``.py``, + ``.pyc``, and so on) will NOT be collected; setting the + ``include_py_files`` argument to ``True`` collects these files as well. + This is typically used with Python routines (such as those in + ``pkgutil``) that search a given directory for Python executable files + then load them as extensions or plugins. + - The ``subdir`` argument gives a subdirectory relative to ``package`` to + search, which is helpful when submodules are imported at run-time from a + directory lacking ``__init__.py``. + - The ``excludes`` argument contains a sequence of strings or Paths. These + provide a list of `globs `_ + to exclude from the collected data files; if a directory matches the + provided glob, all files it contains will be excluded as well. All + elements must be relative paths, which are relative to the provided + package's path (/ ``subdir`` if provided). + + Therefore, ``*.txt`` will exclude only ``.txt`` files in ``package``\ 's + path, while ``**/*.txt`` will exclude all ``.txt`` files in + ``package``\ 's path and all its subdirectories. Likewise, + ``**/__pycache__`` will exclude all files contained in any subdirectory + named ``__pycache__``. + - The ``includes`` function like ``excludes``, but only include matching + paths. ``excludes`` override ``includes``: a file or directory in both + lists will be excluded. + + This function does not work on zipped Python eggs. + +``collect_dynamic_libs( 'module-name' )``: + Returns a list of (source, dest) tuples for all the dynamic libs + present in a module directory. + The list is ready to be assigned to the ``binaries`` global variable. + The function uses ``os.walk()`` to examine all files in the + module directory recursively. + The name of each file found is tested against the likely patterns for + a dynamic lib: ``*.dll``, ``*.dylib``, ``lib*.pyd``, and ``lib*.so``. + Example:: + + binaries = collect_dynamic_libs( 'enchant' ) + +``get_module_file_attribute( 'module-name' )``: + Return the absolute path to *module-name*, a fully-qualified module name. + Example:: + + nacl_dir = os.path.dirname(get_module_file_attribute('nacl')) + +``get_package_paths( 'package-name' )``: + Given the name of a package, return a tuple. + The first element is the absolute path to the folder where the package is stored. + The second element is the absolute path to the named package. + For example, if ``pkg.subpkg`` is stored in ``/abs/Python/lib`` + the result of:: + + get_package_paths( 'pkg.subpkg' ) + + is the tuple, ``( '/abs/Python/lib', '/abs/Python/lib/pkg/subpkg' )`` + +``copy_metadata( 'package-name' )``: + Given the name of a package, return the name of its distribution + metadata folder as a list of tuples ready to be assigned + (or appended) to the ``datas`` global variable. + + Some packages rely on metadata files accessed through the + ``pkg_resources`` module. + Normally |PyInstaller| does not include these metadata files. + If a package fails without them, you can use this + function in a hook file to easily add them to the bundle. + The tuples in the returned list have two strings. + The first is the full pathname to a folder in this system. + The second is the folder name only. + When these tuples are added to ``datas``\ , + the folder will be bundled at the top level. + If *package-name* does not have metadata, an + AssertionError exception is raised. + +.. autofunction:: PyInstaller.utils.hooks.collect_entry_point + +``get_homebrew_path( formula='' )``: + Return the homebrew path to the named formula, or to the + global prefix when formula is omitted. Returns None if + not found. + + +``django_find_root_dir()``: + Return the path to the top-level Python package containing + the Django files, or None if nothing can be found. + +``django_dottedstring_imports( 'django-root-dir' )``: + Return a list of all necessary Django modules specified in + the Django settings.py file, such as the + ``Django.settings.INSTALLED_APPS`` list and many others. + + +Support for Conda +................. + +.. automodule:: PyInstaller.utils.hooks.conda + +.. autofunction:: PyInstaller.utils.hooks.conda.distribution + +.. autofunction:: PyInstaller.utils.hooks.conda.package_distribution + +.. autofunction:: PyInstaller.utils.hooks.conda.files + +.. autofunction:: PyInstaller.utils.hooks.conda.requires + +.. autoclass:: PyInstaller.utils.hooks.conda.Distribution + +.. autoclass:: PyInstaller.utils.hooks.conda.PackagePath + :members: + +.. autofunction:: PyInstaller.utils.hooks.conda.walk_dependency_tree + +.. autofunction:: PyInstaller.utils.hooks.conda.collect_dynamic_libs + +.. _the hook(hook_api) function: + +The ``hook(hook_api)`` Function +-------------------------------- + +In addition to, or instead of, setting global values, +a hook may define a function ``hook(hook_api)``. +A ``hook()`` function should only be needed if the hook +needs to apply sophisticated logic or to make a complex +search of the source machine. + +The Analysis object calls the function and passes it a ``hook_api`` object +which has the following immutable properties: + +``__name__``: + The fully-qualified name of the module that caused the + hook to be called, e.g., ``six.moves.tkinter``. + +``__file__``: + The absolute path of the module. If it is: + + * A standard (rather than namespace) package, this is the absolute path + of this package's directory. + + * A namespace (rather than standard) package, this is the abstract + placeholder ``-``. + + * A non-package module or C extension, this is the absolute path of the + corresponding file. + +``__path__``: + A list of the absolute paths of all directories comprising the module + if it is a package, or ``None``. Typically the list contains only the + absolute path of the package's directory. + +The ``hook_api`` object also offers the following methods: + +``add_imports( *names )``: + The ``names`` argument may be a single string or a list of strings + giving the fully-qualified name(s) of modules to be imported. + This has the same effect as adding the names to the ``hiddenimports`` global. + +``del_imports( *names )``: + The ``names`` argument may be a single string or a list of strings, + giving the fully-qualified name(s) of modules that are not + to be included if they are imported only by the hooked module. + This has the same effect as adding names to the ``excludedimports`` global. + +``add_datas( tuple_list )``: + The ``tuple_list`` argument has the format used with the ``datas`` global + variable. This call has the effect of adding items to that list. + +``add_binaries( tuple_list )``: + The ``tuple_list`` argument has the format used with the ``binaries`` + global variable. This call has the effect of adding items to that list. + +The ``hook()`` function can add, remove or change included files using the +above methods of ``hook_api``. +Or, it can simply set values in the four global variables, because +these will be examined after ``hook()`` returns. + +The ``pre_find_module_path( pfmp_api )`` Method +------------------------------------------------ + +You may write a hook with the special function ``pre_find_module_path( pfmp_api )``. +This method is called when the hooked module name is first seen +by Analysis, before it has located the path to that module or package +(hence the name "pre-find-module-path"). + +Hooks of this type are only recognized if they are stored in +a sub-folder named ``pre_find_module_path`` in a hooks folder, +either in the distributed hooks folder or an ``--additional-hooks-dir`` folder. +You may have normal hooks as well as hooks of this type for the same module. +For example |PyInstaller| includes both a ``hooks/hook-distutils.py`` +and also a ``hooks/pre_find_module_path/hook-distutils.py``. + +The ``pfmp_api`` object that is passed has the following immutable attribute: + +``module_name``: + A string, the fully-qualified name of the hooked module. + +The ``pfmp_api`` object has one mutable attribute, ``search_dirs``. +This is a list of strings that specify the absolute path, or paths, +that will be searched for the hooked module. +The paths in the list will be searched in sequence. +The ``pre_find_module_path()`` function may replace or change +the contents of ``pfmp_api.search_dirs``. + +Immediately after return from ``pre_find_module_path()``, the contents +of ``search_dirs`` will be used to find and analyze the module. + +For an example of use, +see the file :file:`hooks/pre_find_module_path/hook-distutils.py`. +It uses this method to redirect a search for distutils when +|PyInstaller| is executing in a virtual environment. + + +The ``pre_safe_import_module( psim_api )`` Method +--------------------------------------------------- + +You may write a hook with the special function ``pre_safe_import_module( psim_api )``. +This method is called after the hooked module has been found, +but *before* it and everything it recursively imports is added +to the "graph" of imported modules. +Use a pre-safe-import hook in the unusual case where: + +* The script imports *package.dynamic-name* +* The *package* exists +* however, no module *dynamic-name* exists at compile time (it will be defined somehow at run time) + +You use this type of hook to make dynamically-generated names known to PyInstaller. +PyInstaller will not try to locate the dynamic names, fail, and report them as missing. +However, if there are normal hooks for these names, they will be called. + +Hooks of this type are only recognized if they are stored in a sub-folder +named ``pre_safe_import_module`` in a hooks folder, +either in the distributed hooks folder or an ``--additional-hooks-dir`` folder. +(See the distributed ``hooks/pre_safe_import_module`` folder for examples.) + +You may have normal hooks as well as hooks of this type for the same module. +For example the distributed system has both a ``hooks/hook-gi.repository.GLib.py`` +and also a ``hooks/pre_safe_import_module/hook-gi.repository.GLib.py``. + +The ``psim_api`` object offers the following attributes, +all of which are immutable (an attempt to change one raises an exception): + +``module_basename``: + String, the unqualified name of the hooked module, for example ``text``. + +``module_name``: + String, the fully-qualified name of the hooked module, for example + ``email.mime.text``. + +``module_graph``: + The module graph representing all imports processed so far. + +``parent_package``: + If this module is a top-level module of its package, ``None``. + Otherwise, the graph node that represents the import of the + top-level module. + +The last two items, ``module_graph`` and ``parent_package``, +are related to the module-graph, the internal data structure used by +|PyInstaller| to document all imports. +Normally you do not need to know about the module-graph. + +The ``psim_api`` object also offers the following methods: + +``add_runtime_module( fully_qualified_name )``: + Use this method to add an imported module whose name may not + appear in the source because it is dynamically defined at run-time. + This is useful to make the module known to |PyInstaller| and avoid misleading warnings. + A typical use applies the name from the ``psim_api``:: + + psim_api.add_runtime_module( psim_api.module_name ) + +``add_alias_module( real_module_name, alias_module_name )``: + ``real_module_name`` is the fully-qualifed name of an existing + module, one that has been or could be imported by name + (it will be added to the graph if it has not already been imported). + ``alias_module_name`` is a name that might be referenced in the + source file but should be treated as if it were ``real_module_name``. + This method ensures that if |PyInstaller| processes an import of + ``alias_module_name`` it will use ``real_module_name``. + +``append_package_path( directory )``: + The hook can use this method to add a package path + to be searched by |PyInstaller|, typically an import + path that the imported module would add dynamically to + the path if the module was executed normally. + ``directory`` is a string, a pathname to add to the + ``__path__`` attribute. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/index.rst b/3rdparty/pyinstaller-4.3/doc/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..bb07a1187982db32c735f413a9e869d23f6d44fe --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/index.rst @@ -0,0 +1,89 @@ +================== +PyInstaller Manual +================== + +:Version: |PyInstallerVersion| +:Homepage: |Homepage| +:Contact: pyinstaller@googlegroups.com +:Authors: David Cortesi, based on structure by Giovanni Bajo & William Caban, based on Gordon McMillan's manual +:Copyright: This document has been placed in the public domain. + + +|PyInstaller| bundles a Python application and all its dependencies into +a single package. +The user can run the packaged app without installing a Python interpreter or any modules. +|PyInstaller| supports Python 3.6 or newer, +and correctly bundles the major Python packages +such as numpy, PyQt, Django, wxPython, and others. + +|PyInstaller| is tested against Windows, Mac OS X, and GNU/Linux. +However, it is not a cross-compiler: +to make a Windows app you run |PyInstaller| in Windows; +to make a GNU/Linux app you run it in GNU/Linux, etc. +|PyInstaller| has been used successfully with +AIX, Solaris, FreeBSD and OpenBSD +but testing against them is not part of our continuous integration tests. + + +What's New This Release +~~~~~~~~~~~~~~~~~~~~~~~~ + +Release 4.0 adds support for 3rd-party packages to provide PyInstaller hooks +along with the package. This allows Maintainers of other Python packages to +deliver up-to-date PyInstaller hooks as part of their package. +See our `sample project`__ for more information. + +__ https://github.com/pyinstaller/hooksample + +PyInstaller uses this option itself to provide updated hooks much faster: +Many hooks are moved into the new package `pyinstaller-hooks-contrib`__, +which is updated monthly. +This package is installed automatically when installing PyInstaller, +but can also be updated independently. + +__ https://github.com/pyinstaller/pyinstaller-hooks-contrib + +Finally, this version drops support for Python 2.7, +which is end-of-life since January 2020.. +The minimum required version is now Python 3.6. +The last version supporting Python 2.7 was PyInstaller 3.6. + + +Contents: + +.. toctree:: + :maxdepth: 2 + + requirements + license + contributing + installation + operating-mode + usage + runtime-information + spec-files + feature-notes + when-things-go-wrong + advanced-topics + hooks + bootloader-building + CHANGES + CREDITS + man-pages + development/index + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/installation.rst b/3rdparty/pyinstaller-4.3/doc/installation.rst new file mode 100644 index 0000000000000000000000000000000000000000..65d0682227a9d3a645539faec6de1d77b589fcee --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/installation.rst @@ -0,0 +1,159 @@ +How to Install |PyInstaller| +=============================== + +|PyInstaller| is a normal Python package. +You can download the archive from PyPi_, +but it is easier to install using pip_ where is is available, +for example:: + + pip install pyinstaller + +or upgrade to a newer version:: + + pip install --upgrade pyinstaller + +To install the current development version use:: + + pip install https://github.com/pyinstaller/pyinstaller/tarball/develop + + +Installing in Windows +~~~~~~~~~~~~~~~~~~~~~~~ + +For Windows, PyWin32_ or the more recent pypiwin32_, is a prerequisite. +The latter is installed automatically when you install |PyInstaller| +using pip_ or `easy_install`_. +If necessary, follow the pypiwin32_ link to install it manually. + +It is particularly easy to use pip-Win_ to install |PyInstaller| +along with the correct version of PyWin32_. +pip-Win_ also provides virtualenv_, which makes it simple +to maintain multiple different Python interpreters and install packages +such as |PyInstaller| in each of them. +(For more on the uses of virtualenv, see :ref:`Supporting Multiple Platforms` below.) + +When pip-Win is working, enter this command in its Command field +and click Run:: + + venv -c -i pyi-env-name + +This creates a new virtual environment rooted at ``C:\Python\pyi-env-name`` +and makes it the current environment. +A new command shell +window opens in which you can run commands within this environment. +Enter the command :: + + pip install PyInstaller + +Once it is installed, to use |PyInstaller|, + +* Start pip-Win +* In the Command field enter ``venv pyi-env-name`` +* Click Run + +Then you have a command shell window in which commands such as +`pyinstaller` execute in that Python environment. + +Installing in Mac OS X +~~~~~~~~~~~~~~~~~~~~~~~~ + +Mac OS X 10.8 comes with Python 2.7 pre-installed by Apple. +However, Python 2.7 is end-of-life and no longer supported by |PyInstaller|, +also +major packages such as +PyQt, Numpy, Matplotlib, Scipy, and the like, +have dropped support for Python 2.7, too. +Thus we strongly +recommend that you install these using either `MacPorts`_ or `Homebrew`_. + +|PyInstaller| users report fewer problems when they use a package manager +than when they attempt to install major packages individually. + +Alternatively you might install Python 3 following the +`official guide `_. + + +Installing from the archive +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If pip is not available, download the compressed archive from PyPI_. +If you are asked to test a problem using the latest development code, +download the compressed archive from the *develop* branch of +`PyInstaller Downloads`_ page. + +Expand the archive. +Inside is a script named ``setup.py``. +Execute ``python setup.py install`` +with administrator privilege to install or upgrade |PyInstaller|. + +For platforms other than Windows, GNU/Linux and Mac OS, you must first +build a |bootloader| program for your platform: see :ref:`Building the Bootloader`. +After the |bootloader| has been created, +use ``python setup.py install`` with administrator privileges +to complete the installation. + + + +Verifying the installation +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +On all platforms, the command ``pyinstaller`` should now exist on the +execution path. To verify this, enter the command:: + + pyinstaller --version + +The result should resemble ``3.n`` for a released version, +and ``3.n.dev0-xxxxxx`` for a development branch. + +If the command is not found, make sure the execution path includes +the proper directory: + +* Windows: ``C:\PythonXY\Scripts`` where *XY* stands for the + major and minor Python version number, + for example ``C:\Python34\Scripts`` for Python 3.4) +* GNU/Linux: ``/usr/bin/`` +* OS X (using the default Apple-supplied Python) ``/usr/bin`` +* OS X (using Python installed by homebrew) ``/usr/local/bin`` +* OS X (using Python installed by macports) ``/opt/local/bin`` + +To display the current path in Windows the command is ``echo %path%`` +and in other systems, ``echo $PATH``. + + +Installed commands +~~~~~~~~~~~~~~~~~~~~ + +The complete installation places these commands on the execution path: + +* ``pyinstaller`` is the main command to build a bundled application. + See :ref:`Using PyInstaller`. + +* ``pyi-makespec`` is used to create a spec file. See :ref:`Using Spec Files`. + +* ``pyi-archive_viewer`` is used to inspect a bundled application. + See :ref:`Inspecting Archives`. + +* ``pyi-bindepend`` is used to display dependencies of an executable. + See :ref:`Inspecting Executables`. + +* ``pyi-grab_version`` is used to extract a version resource from a Windows + executable. See :ref:`Capturing Windows Version Data`. + +If you do not perform a complete installation +(installing via ``pip`` or executing ``setup.py``), +these commands will not be installed as commands. +However, you can still execute all the functions documented below +by running Python scripts found in the distribution folder. +The equivalent of the ``pyinstaller`` command is +:file:`{pyinstaller-folder}/pyinstaller.py`. +The other commands are found in :file:`{pyinstaller-folder}/cliutils/` +with meaningful names (``makespec.py``, etc.) + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/license.rst b/3rdparty/pyinstaller-4.3/doc/license.rst new file mode 100644 index 0000000000000000000000000000000000000000..fb45722a5f1974d86c036f3b3209d5dc73857db5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/license.rst @@ -0,0 +1,28 @@ +License +======= + +|PyInstaller| is distributed under the `GPL License`_ but with +an exception that allows you to use it to build commercial products: + + #. You may use PyInstaller to bundle commercial applications out of your + source code. + + #. The executable bundles generated by PyInstaller from your source code + can be shipped with whatever license you want. + + #. You may modify PyInstaller for your own needs but changes to the + PyInstaller source code fall under the terms of the GPL license. + That is, if you distribute your modifications you must distribute + them under GPL terms. + +For updated information or clarification see our +`FAQ`_ at the `PyInstaller`_ home page. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/make.bat b/3rdparty/pyinstaller-4.3/doc/make.bat new file mode 100755 index 0000000000000000000000000000000000000000..b839c2785f658cf76b3b18533b4705f8e3d2e7fb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/make.bat @@ -0,0 +1,263 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + echo. coverage to run coverage check of the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +REM Check if sphinx-build is available and fallback to Python version if any +%SPHINXBUILD% 2> nul +if errorlevel 9009 goto sphinx_python +goto sphinx_ok + +:sphinx_python + +set SPHINXBUILD=python -m sphinx.__init__ +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +:sphinx_ok + + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyInstaller.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyInstaller.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %~dp0 + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "coverage" ( + %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage + if errorlevel 1 exit /b 1 + echo. + echo.Testing of coverage in the sources finished, look at the ^ +results in %BUILDDIR%/coverage/python.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/3rdparty/pyinstaller-4.3/doc/man-pages.rst b/3rdparty/pyinstaller-4.3/doc/man-pages.rst new file mode 100644 index 0000000000000000000000000000000000000000..4650b35751122ecfe18241e8995932f764ad0efa --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/man-pages.rst @@ -0,0 +1,9 @@ +Man Pages +============ + +.. toctree:: + :titlesonly: + :glob: + + man/pyinstaller + man/pyi-* diff --git a/3rdparty/pyinstaller-4.3/doc/man/pyi-makespec.rst b/3rdparty/pyinstaller-4.3/doc/man/pyi-makespec.rst new file mode 100644 index 0000000000000000000000000000000000000000..6e7cf46f0e20397c189fdd2e2acfd9be353d107a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/man/pyi-makespec.rst @@ -0,0 +1,65 @@ +========================== +pyi-makespec +========================== + +.. raw:: manpage + + .\" disable justification (adjust text to left margin only) + .ad l + \ + + +SYNOPSIS +========== + +``pyi-makespec`` SCRIPT [SCRIPT ...] + +DESCRIPTION +============ + +The spec file is the description of what you want |PyInstaller| to do +with your program. ``pyi-makespec`` is a simple wizard to create spec +files that cover basic usages:: + + pyi-makespec [--onefile] yourprogram.py + +By default, ``pyi-makespec`` generates a spec file that tells +|PyInstaller| to create a distribution directory contains the main +executable and the dynamic libraries. The option ``--onefile`` +specifies that you want PyInstaller to build a single file with +everything inside. + +In most cases the specfile generated by ``pyi-makespec`` is all you +need. If not, see `When things go wrong` in the manual and be sure to +read the introduction to `Spec Files`. + + +OPTIONS +======== + +.. include:: _pyi-makespec-options.tmp + +ENVIRONMENT VARIABLES +===================== + +:PYINSTALLER_CONFIG_DIR: + This changes the directory where PyInstaller caches some files. + The default location for this is operating system dependent, + but is typically a subdirectory of the home directory. + + +SEE ALSO +============= + +``pyinstaller``\(1), +The PyInstaller Manual |Manual|, +Project Homepage |Homepage| + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/man/pyinstaller.rst b/3rdparty/pyinstaller-4.3/doc/man/pyinstaller.rst new file mode 100644 index 0000000000000000000000000000000000000000..72e8fc4f21139720a7fad6933c727008a64acb4f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/man/pyinstaller.rst @@ -0,0 +1,67 @@ +========================== +pyinstaller +========================== + +.. raw:: manpage + + .\" disable justification (adjust text to left margin only) + .ad l + \ + +SYNOPSIS +========== + +``pyinstaller`` SCRIPT... + +``pyinstaller`` SPECFILE + + +DESCRIPTION +============ + +PyInstaller is a program that freezes (packages) Python programs into +stand-alone executables, under Windows, GNU/Linux, Mac OS X, +FreeBSD, OpenBSD, Solaris and AIX. +Its main advantages over similar tools are that PyInstaller works with +Python 3.6-3.9, it builds smaller executables thanks to transparent +compression, it is fully multi-platform, and use the OS support to load the +dynamic libraries, thus ensuring full compatibility. + +You may either pass one or more file-names of Python scripts or a single +`.spec`-file-name. In the first case, ``pyinstaller`` will generate a +`.spec`-file (as ``pyi-makespec`` would do) and immediately process it. + +If you pass a `.spec`-file, this will be processed and most options given on +the command-line will have no effect. +Please see the PyInstaller Manual for more information. + + +OPTIONS +======== + +.. include:: _pyinstaller-options.tmp + +ENVIRONMENT VARIABLES +===================== + +:PYINSTALLER_CONFIG_DIR: + This changes the directory where PyInstaller caches some files. + The default location for this is operating system dependent, + but is typically a subdirectory of the home directory. + + +SEE ALSO +============= + +``pyi-makespec``\(1), +The PyInstaller Manual |Manual|, +Project Homepage |Homepage| + + +.. include:: ../_common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/operating-mode.rst b/3rdparty/pyinstaller-4.3/doc/operating-mode.rst new file mode 100644 index 0000000000000000000000000000000000000000..c63aa0476e325404279daa889f5cbc601250835c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/operating-mode.rst @@ -0,0 +1,312 @@ +What |PyInstaller| Does and How It Does It +============================================================ + +This section covers the basic ideas of |PyInstaller|. +These ideas apply to all platforms. +Options and special cases are covered below, under :ref:`Using PyInstaller`. + +|PyInstaller| reads a Python script written by you. +It analyzes your code to discover every other module and library +your script needs in order to execute. +Then it collects copies of all those files -- including +the active Python interpreter! -- and puts them with +your script in a single folder, +or optionally in a single executable file. + +For the great majority of programs, this can be done with one short command, :: + + pyinstaller myscript.py + +or with a few added options, for example a windowed application +as a single-file executable, :: + + pyinstaller --onefile --windowed myscript.py + +You distribute the bundle as a folder or file to other people, +and they can execute +your program. +To your users, the app is self-contained. +They do not need to install any particular version of Python or any modules. +They do not need to have Python installed at all. + +.. Note:: + + The output of |PyInstaller| is specific to the active operating system + and the active version of Python. + This means that to prepare a distribution for: + + * a different OS + * a different version of Python + * a 32-bit or 64-bit OS + + you run |PyInstaller| on that OS, under that version of Python. + The Python interpreter that executes |PyInstaller| is part of + the bundle, and it is specific to the OS and the word size. + + +Analysis: Finding the Files Your Program Needs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +What other modules and libraries does your script need in order to run? +(These are sometimes called its "dependencies".) + +To find out, |PyInstaller| finds all the ``import`` statements +in your script. +It finds the imported modules and looks in them for ``import`` +statements, and so on recursively, until it has a complete list of +modules your script may use. + +|PyInstaller| understands the "egg" distribution format often used +for Python packages. +If your script imports a module from an "egg", |PyInstaller| adds +the egg and its dependencies to the set of needed files. + +|PyInstaller| also knows about many major Python packages, +including the GUI packages +Qt_ (imported via PyQt_ or PySide_), WxPython_, TkInter_, Django_, +and other major packages. +For a complete list, see `Supported Packages`_. + +Some Python scripts import modules in ways that |PyInstaller| cannot detect: +for example, by using the ``__import__()`` function with variable data, +using ``imp.find_module()``, +or manipulating the ``sys.path`` value at run time. +If your script requires files that |PyInstaller| does not know about, +you must help it: + +* You can give additional files on the ``pyinstaller`` command line. +* You can give additional import paths on the command line. +* You can edit the :file:`{myscript}.spec` file + that |PyInstaller| writes the first time you run it for your script. + In the spec file you can tell |Pyinstaller| about code modules + that are unique to your script. +* You can write "hook" files that inform |Pyinstaller| of hidden imports. + If you create a "hook" for a package that other users might also use, + you can contribute your hook file to |PyInstaller|. + +If your program depends on access to certain data files, +you can tell |PyInstaller| to include them in the bundle as well. +You do this by modifying the spec file, an advanced topic that is +covered under :ref:`Using Spec Files`. + +In order to locate included files at run time, +your program needs to be able to learn its path at run time +in a way that works regardless of +whether or not it is running from a bundle. +This is covered under :ref:`Run-time Information`. + +|PyInstaller| does *not* include libraries that should exist in +any installation of this OS. +For example in GNU/Linux, it does not bundle any file +from :file:`/lib` or :file:`/usr/lib`, assuming +these will be found in every system. + + +.. _Bundling to One Folder: + +Bundling to One Folder +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you apply |PyInstaller| to :file:`myscript.py` the default +result is a single folder named :file:`myscript`. +This folder contains all your script's dependencies, +and an executable file also named :file:`myscript` +(:file:`myscript.exe` in Windows). + +You compress the folder +to :file:`myscript.zip` and transmit it to your users. +They install the program simply by unzipping it. +A user runs your app by +opening the folder and launching the :file:`myscript` executable inside it. + +It is easy to debug problems that occur when building the app +when you use one-folder mode. +You can see exactly what files |PyInstaller| collected into the folder. + +Another advantage of a one-folder bundle +is that when you change your code, as long +as it imports `exactly the same set of dependencies`, you could send out +only the updated :file:`myscript` executable. +That is typically much smaller +than the entire folder. +(If you change the script so that it imports more +or different dependencies, or if the dependencies +are upgraded, you must redistribute the whole bundle.) + +A small disadvantage of the one-folder format is that the one folder contains +a large number of files. +Your user must find the :file:`myscript` executable +in a long list of names or among a big array of icons. +Also your user can create +a problem by accidentally dragging files out of the folder. + +.. _how the one-folder program works: + +How the One-Folder Program Works +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A bundled program always starts execution in the |PyInstaller| |bootloader|. +This is the heart of the ``myscript`` executable in the folder. + +The |PyInstaller| |bootloader| is a binary +executable program for the active platform +(Windows, GNU/Linux, Mac OS X, etc.). +When the user launches your program, it is the |bootloader| that runs. +The |bootloader| creates a temporary Python environment +such that the Python interpreter will find all imported modules and +libraries in the ``myscript`` folder. + +The |bootloader| starts a copy of the Python interpreter +to execute your script. +Everything follows normally from there, provided +that all the necessary support files were included. + +(This is an overview. +For more detail, see :ref:`The Bootstrap Process in Detail` below.) + + +.. _Bundling to One File: + +Bundling to One File +~~~~~~~~~~~~~~~~~~~~~ + +|PyInstaller| can bundle your script and all its dependencies into a single +executable named :file:`myscript` (:file:`myscript.exe` in Windows). + +The advantage is that your users get something they understand, +a single executable to launch. +A disadvantage is that any related files +such as a README must be distributed separately. +Also, the single executable is a little slower to start up than +the one-folder bundle. + +Before you attempt to bundle to one file, make sure your app +works correctly when bundled to one folder. +It is is *much* easier to diagnose problems in one-folder mode. + +.. _how the one-file program works: + +How the One-File Program Works +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The |bootloader| is the heart of the one-file bundle also. +When started it creates a temporary folder +in the appropriate temp-folder location for this OS. +The folder is named :file:`_MEI{xxxxxx}`, where *xxxxxx* is a random number. + +The one executable file contains an embedded archive of all the Python +modules used by your script, as well as +compressed copies of any non-Python support files (e.g. ``.so`` files). +The |bootloader| uncompresses the support files and writes copies +into the the temporary folder. +This can take a little time. +That is why a one-file app is a little slower to start +than a one-folder app. + +.. note:: + + |PyInstaller| currently does not preserve file attributes. + see :issue:`3926`. + + +After creating the temporary folder, the |bootloader| +proceeds exactly as for the one-folder bundle, +in the context of the temporary folder. +When the bundled code terminates, +the |bootloader| deletes the temporary folder. + +(In GNU/Linux and related systems, it is possible +to mount the ``/tmp`` folder with a "no-execution" option. +That option is not compatible with a |PyInstaller| +one-file bundle. It needs to execute code out of :file:`/tmp`. +If you know the target environment, +``--runtime-tmpdir`` might be a workaround.) + +Because the program makes a temporary folder with a unique name, +you can run multiple copies of the app; +they won't interfere with each other. +However, running multiple copies is expensive in disk space because +nothing is shared. + +The :file:`_MEI{xxxxxx}` folder is not removed if the program crashes +or is killed (kill -9 on Unix, killed by the Task Manager on Windows, +"Force Quit" on Mac OS). +Thus if your app crashes frequently, your users will lose disk space to +multiple :file:`_MEI{xxxxxx}` temporary folders. + +It is possible to control the location of the :file:`_MEI{xxxxxx}` folder by +using the ``--runtime-tmpdir`` command line option. The specified path is +stored in the executable, and the bootloader will create the +:file:`_MEI{xxxxxx}` folder inside of the specified folder. Please see +:ref:`defining the extraction location` for details. + +.. Note:: + + Do *not* give administrator privileges to a one-file executable + (setuid root in Unix/Linux, or the "Run this program as an administrator" + property in Windows 7). + There is an unlikely but not impossible way in which a malicious attacker could + corrupt one of the shared libraries in the temp folder + while the |bootloader| is preparing it. + Distribute a privileged program in one-folder mode instead. + +.. Note:: + Applications that use `os.setuid()` may encounter permissions errors. + The temporary folder where the bundled app runs may not being readable + after `setuid` is called. If your script needs to + call `setuid`, it may be better to use one-folder mode + so as to have more control over the permissions on its files. + + +Using a Console Window +~~~~~~~~~~~~~~~~~~~~~~~ + +By default the |bootloader| creates a command-line console +(a terminal window in GNU/Linux and Mac OS, a command window in Windows). +It gives this window to the Python interpreter for its standard input and output. +Your script's use of ``print`` and ``input()`` are directed here. +Error messages from Python and default logging output +also appear in the console window. + +An option for Windows and Mac OS is to tell |PyInstaller| to not provide a console window. +The |bootloader| starts Python with no target for standard output or input. +Do this when your script has a graphical interface for user input and can properly +report its own diagnostics. + +As noted in the `CPython tutorial Appendix +`__, +for Windows a file extention of `.pyw` suppresses the console window +that normally appears. +Likewise, a console window will not be provided when using +a :file:`myscript.pyw` script with |PyInstaller|. + + +Hiding the Source Code +~~~~~~~~~~~~~~~~~~~~~~~~ + +The bundled app does not include any source code. +However, |PyInstaller| bundles compiled Python scripts (``.pyc`` files). +These could in principle be decompiled to reveal the logic of +your code. + +If you want to hide your source code more thoroughly, one possible option +is to compile some of your modules with Cython_. +Using Cython you can convert Python modules into C and compile +the C to machine language. +|PyInstaller| can follow import statements that refer to +Cython C object modules and bundle them. + +Additionally, Python bytecode can be obfuscated with AES256 by specifying +an encryption key on PyInstaller's command line. Please note that it is still +very easy to extract the key and get back the original bytecode, but it +should prevent most forms of "casual" tampering. +See :ref:`encrypting python bytecode` for details. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/pyi-makespec.1 b/3rdparty/pyinstaller-4.3/doc/pyi-makespec.1 new file mode 100644 index 0000000000000000000000000000000000000000..414ce8cad5911e087da4ca10b7fc3c7dea178ec1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/pyi-makespec.1 @@ -0,0 +1,308 @@ +.\" Man page generated from reStructuredText. +. +.TH "PYI-MAKESPEC" "1" "2021-04-17" "4.3" "PyInstaller" +.SH NAME +pyi-makespec \- Create a spec file for your PyInstaller project +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.\" disable justification (adjust text to left margin only) +.ad l +\.SH SYNOPSIS +.sp +\fBpyi\-makespec\fP SCRIPT [SCRIPT ...] +.SH DESCRIPTION +.sp +The spec file is the description of what you want \fIPyInstaller\fP to do +with your program. \fBpyi\-makespec\fP is a simple wizard to create spec +files that cover basic usages: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +pyi\-makespec [\-\-onefile] yourprogram.py +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +By default, \fBpyi\-makespec\fP generates a spec file that tells +\fIPyInstaller\fP to create a distribution directory contains the main +executable and the dynamic libraries. The option \fB\-\-onefile\fP +specifies that you want PyInstaller to build a single file with +everything inside. +.sp +In most cases the specfile generated by \fBpyi\-makespec\fP is all you +need. If not, see \fIWhen things go wrong\fP in the manual and be sure to +read the introduction to \fISpec Files\fP\&. +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-h\fP,\fB \-\-help +show this help message and exit +.TP +.BI \-\-log\-level \ LEVEL +Amount of detail in build\-time console messages. LEVEL +may be one of TRACE, DEBUG, INFO, WARN, ERROR, +CRITICAL (default: INFO). +.UNINDENT +.SS What to generate +.INDENT 0.0 +.TP +.B \-D\fP,\fB \-\-onedir +Create a one\-folder bundle containing an executable +(default) +.TP +.B \-F\fP,\fB \-\-onefile +Create a one\-file bundled executable. +.TP +.BI \-\-specpath \ DIR +Folder to store the generated spec file (default: +current directory) +.TP +.BI \-n \ NAME\fR,\fB \ \-\-name \ NAME +Name to assign to the bundled app and spec file +(default: first script\(aqs basename) +.UNINDENT +.SS What to bundle, where to search +.INDENT 0.0 +.TP +.BI \-\-add\-data \ +Additional non\-binary files or folders to be added to +the executable. The path separator is platform +specific, \fBos.pathsep\fP (which is \fB;\fP on Windows +and \fB:\fP on most unix systems) is used. This option +can be used multiple times. +.TP +.BI \-\-add\-binary \ +Additional binary files to be added to the executable. +See the \fB\-\-add\-data\fP option for more details. This +option can be used multiple times. +.TP +.BI \-p \ DIR\fR,\fB \ \-\-paths \ DIR +A path to search for imports (like using PYTHONPATH). +Multiple paths are allowed, separated by \(aq;\(aq, or use +this option multiple times +.TP +.BI \-\-hidden\-import \ MODULENAME\fR,\fB \ \-\-hiddenimport \ MODULENAME +Name an import not visible in the code of the +script(s). This option can be used multiple times. +.TP +.BI \-\-collect\-submodules \ MODULENAME +Collect all submodules from the specified package or +module. This option can be used multiple times. +.TP +.BI \-\-collect\-data \ MODULENAME\fR,\fB \ \-\-collect\-datas \ MODULENAME +Collect all data from the specified package or module. +This option can be used multiple times. +.TP +.BI \-\-collect\-binaries \ MODULENAME +Collect all binaries from the specified package or +module. This option can be used multiple times. +.TP +.BI \-\-collect\-all \ MODULENAME +Collect all submodules, data files, and binaries from +the specified package or module. This option can be +used multiple times. +.TP +.BI \-\-copy\-metadata \ PACKAGENAME +Copy metadata for the specified package. This option +can be used multiple times. +.TP +.BI \-\-additional\-hooks\-dir \ HOOKSPATH +An additional path to search for hooks. This option +can be used multiple times. +.TP +.BI \-\-runtime\-hook \ RUNTIME_HOOKS +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. +.TP +.BI \-\-exclude\-module \ EXCLUDES +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. +.TP +.BI \-\-key \ KEY +The key used to encrypt Python bytecode. +.UNINDENT +.SS How to generate +.INDENT 0.0 +.TP +.BI \-d \ \fR,\fB \ \-\-debug \ +R|Provide assistance with debugging a frozen +application. This argument may be provided multiple +times to select several of the following options. \- +all: All three of the following options. \- imports: +specify the \-v option to the underlying Python +interpreter, causing it to print a message each time a +module is initialized, showing the place (filename or +built\-in module) from which it is loaded. See +\fI\%https://docs.python.org/3/using/cmdline.html#id4\fP\&. \- +bootloader: tell the bootloader to issue progress +messages while initializing and starting the bundled +app. Used to diagnose problems with missing imports. \- +noarchive: instead of storing all frozen Python source +files as an archive inside the resulting executable, +store them as files in the resulting output directory. +.TP +.B \-s\fP,\fB \-\-strip +Apply a symbol\-table strip to the executable and +shared libs (not recommended for Windows) +.TP +.B \-\-noupx +Do not use UPX even if it is available (works +differently between Windows and *nix) +.TP +.BI \-\-upx\-exclude \ FILE +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. +.UNINDENT +.SS Windows and Mac OS X specific options +.INDENT 0.0 +.TP +.B \-c\fP,\fB \-\-console\fP,\fB \-\-nowindowed +Open a console window for standard i/o (default). On +Windows this option will have no effect if the first +script is a \(aq.pyw\(aq file. +.TP +.B \-w\fP,\fB \-\-windowed\fP,\fB \-\-noconsole +Windows and Mac OS X: do not provide a console window +for standard i/o. On Mac OS X this also triggers +building an OS X .app bundle. On Windows this option +will be set if the first script is a \(aq.pyw\(aq file. This +option is ignored in *NIX systems. +.TP +.BI \-i \ \fR,\fB \ \-\-icon \ +FILE.ico: apply that 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 Mac OS +X. Use "NONE" to not apply any icon, thereby making +the OS to show some default (default: apply +PyInstaller\(aqs icon) +.UNINDENT +.SS Windows specific options +.INDENT 0.0 +.TP +.BI \-\-version\-file \ FILE +add a version resource from FILE to the exe +.TP +.BI \-m \ \fR,\fB \ \-\-manifest \ +add manifest FILE or XML to the exe +.TP +.BI \-r \ RESOURCE\fR,\fB \ \-\-resource \ RESOURCE +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. +.TP +.B \-\-uac\-admin +Using this option creates a Manifest which will +request elevation upon application restart. +.TP +.B \-\-uac\-uiaccess +Using this option allows an elevated application to +work with Remote Desktop. +.UNINDENT +.SS Windows Side\-by\-side Assembly searching options (advanced) +.INDENT 0.0 +.TP +.B \-\-win\-private\-assemblies +Any Shared Assemblies bundled into the application +will be changed into Private Assemblies. This means +the exact versions of these assemblies will always be +used, and any newer versions installed on user +machines at the system level will be ignored. +.TP +.B \-\-win\-no\-prefer\-redirects +While searching for Shared or Private Assemblies to +bundle into the application, PyInstaller will prefer +not to follow policies that redirect to newer +versions, and will try to bundle the exact versions of +the assembly. +.UNINDENT +.SS Mac OS X specific options +.INDENT 0.0 +.TP +.BI \-\-osx\-bundle\-identifier \ BUNDLE_IDENTIFIER +Mac OS X .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\(aqs basename) +.UNINDENT +.SS Rarely used special options +.INDENT 0.0 +.TP +.BI \-\-runtime\-tmpdir \ PATH +Where to extract libraries and support files in +\fIonefile\fP\-mode. If this option is given, the +bootloader will ignore any temp\-folder location +defined by the run\-time OS. The \fB_MEIxxxxxx\fP\-folder +will be created here. Please use this option only if +you know what you are doing. +.TP +.B \-\-bootloader\-ignore\-signals +Tell the bootloader to ignore signals rather than +forwarding them to the child process. Useful in +situations where e.g. a supervisor process signals +both the bootloader and child (e.g. via a process +group) to avoid signalling the child twice. +.UNINDENT +.SH ENVIRONMENT VARIABLES +.INDENT 0.0 +.TP +.B PYINSTALLER_CONFIG_DIR +This changes the directory where PyInstaller caches some files. +The default location for this is operating system dependent, +but is typically a subdirectory of the home directory. +.UNINDENT +.SH SEE ALSO +.sp +\fBpyinstaller\fP(1), +The PyInstaller Manual \fI\%https://pyinstaller.readthedocs.io/\fP, +Project Homepage \fI\%http://www.pyinstaller.org\fP +.SH AUTHOR +Hartmut Goebel +.SH COPYRIGHT +This document has been placed in the public domain. +.\" Generated by docutils manpage writer. +. diff --git a/3rdparty/pyinstaller-4.3/doc/pyinstaller.1 b/3rdparty/pyinstaller-4.3/doc/pyinstaller.1 new file mode 100644 index 0000000000000000000000000000000000000000..f451924e6095596d0e3582dad35b21e1c8127d8b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/pyinstaller.1 @@ -0,0 +1,338 @@ +.\" Man page generated from reStructuredText. +. +.TH "PYINSTALLER" "1" "2021-04-17" "4.3" "PyInstaller" +.SH NAME +pyinstaller \- Configure and build a PyInstaller project in one run +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.\" disable justification (adjust text to left margin only) +.ad l +\.SH SYNOPSIS +.sp +\fBpyinstaller\fP SCRIPT... +.sp +\fBpyinstaller\fP SPECFILE +.SH DESCRIPTION +.sp +PyInstaller is a program that freezes (packages) Python programs into +stand\-alone executables, under Windows, GNU/Linux, Mac OS X, +FreeBSD, OpenBSD, Solaris and AIX. +Its main advantages over similar tools are that PyInstaller works with +Python 3.6\-3.9, it builds smaller executables thanks to transparent +compression, it is fully multi\-platform, and use the OS support to load the +dynamic libraries, thus ensuring full compatibility. +.sp +You may either pass one or more file\-names of Python scripts or a single +\fI\&.spec\fP\-file\-name. In the first case, \fBpyinstaller\fP will generate a +\fI\&.spec\fP\-file (as \fBpyi\-makespec\fP would do) and immediately process it. +.sp +If you pass a \fI\&.spec\fP\-file, this will be processed and most options given on +the command\-line will have no effect. +Please see the PyInstaller Manual for more information. +.SH OPTIONS +.INDENT 0.0 +.TP +.B \-h\fP,\fB \-\-help +show this help message and exit +.TP +.B \-v\fP,\fB \-\-version +Show program version info and exit. +.TP +.BI \-\-distpath \ DIR +Where to put the bundled app (default: .dist) +.TP +.BI \-\-workpath \ WORKPATH +Where to put all the temporary work files, .log, .pyz +and etc. (default: .build) +.TP +.B \-y\fP,\fB \-\-noconfirm +Replace output directory (default: +SPECPATHdistSPECNAME) without asking for +confirmation +.TP +.BI \-\-upx\-dir \ UPX_DIR +Path to UPX utility (default: search the execution +path) +.TP +.B \-a\fP,\fB \-\-ascii +Do not include unicode encoding support (default: +included if available) +.TP +.B \-\-clean +Clean PyInstaller cache and remove temporary files +before building. +.TP +.BI \-\-log\-level \ LEVEL +Amount of detail in build\-time console messages. LEVEL +may be one of TRACE, DEBUG, INFO, WARN, ERROR, +CRITICAL (default: INFO). +.UNINDENT +.SS What to generate +.INDENT 0.0 +.TP +.B \-D\fP,\fB \-\-onedir +Create a one\-folder bundle containing an executable +(default) +.TP +.B \-F\fP,\fB \-\-onefile +Create a one\-file bundled executable. +.TP +.BI \-\-specpath \ DIR +Folder to store the generated spec file (default: +current directory) +.TP +.BI \-n \ NAME\fR,\fB \ \-\-name \ NAME +Name to assign to the bundled app and spec file +(default: first script\(aqs basename) +.UNINDENT +.SS What to bundle, where to search +.INDENT 0.0 +.TP +.BI \-\-add\-data \ +Additional non\-binary files or folders to be added to +the executable. The path separator is platform +specific, \fBos.pathsep\fP (which is \fB;\fP on Windows +and \fB:\fP on most unix systems) is used. This option +can be used multiple times. +.TP +.BI \-\-add\-binary \ +Additional binary files to be added to the executable. +See the \fB\-\-add\-data\fP option for more details. This +option can be used multiple times. +.TP +.BI \-p \ DIR\fR,\fB \ \-\-paths \ DIR +A path to search for imports (like using PYTHONPATH). +Multiple paths are allowed, separated by \(aq;\(aq, or use +this option multiple times +.TP +.BI \-\-hidden\-import \ MODULENAME\fR,\fB \ \-\-hiddenimport \ MODULENAME +Name an import not visible in the code of the +script(s). This option can be used multiple times. +.TP +.BI \-\-collect\-submodules \ MODULENAME +Collect all submodules from the specified package or +module. This option can be used multiple times. +.TP +.BI \-\-collect\-data \ MODULENAME\fR,\fB \ \-\-collect\-datas \ MODULENAME +Collect all data from the specified package or module. +This option can be used multiple times. +.TP +.BI \-\-collect\-binaries \ MODULENAME +Collect all binaries from the specified package or +module. This option can be used multiple times. +.TP +.BI \-\-collect\-all \ MODULENAME +Collect all submodules, data files, and binaries from +the specified package or module. This option can be +used multiple times. +.TP +.BI \-\-copy\-metadata \ PACKAGENAME +Copy metadata for the specified package. This option +can be used multiple times. +.TP +.BI \-\-additional\-hooks\-dir \ HOOKSPATH +An additional path to search for hooks. This option +can be used multiple times. +.TP +.BI \-\-runtime\-hook \ RUNTIME_HOOKS +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. +.TP +.BI \-\-exclude\-module \ EXCLUDES +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. +.TP +.BI \-\-key \ KEY +The key used to encrypt Python bytecode. +.UNINDENT +.SS How to generate +.INDENT 0.0 +.TP +.BI \-d \ \fR,\fB \ \-\-debug \ +Provide assistance with debugging a frozen +application. This argument may be provided multiple +times to select several of the following options. +.INDENT 7.0 +.IP \(bu 2 +all: All three of the following options. +.IP \(bu 2 +imports: specify the \-v option to the underlying +Python interpreter, causing it to print a message +each time a module is initialized, showing the +place (filename or built\-in module) from which it +is loaded. See +\fI\%https://docs.python.org/3/using/cmdline.html#id4\fP\&. +.IP \(bu 2 +bootloader: tell the bootloader to issue progress +messages while initializing and starting the +bundled app. Used to diagnose problems with +missing imports. +.IP \(bu 2 +noarchive: instead of storing all frozen Python +source files as an archive inside the resulting +executable, store them as files in the resulting +output directory. +.UNINDENT +.TP +.B \-s\fP,\fB \-\-strip +Apply a symbol\-table strip to the executable and +shared libs (not recommended for Windows) +.TP +.B \-\-noupx +Do not use UPX even if it is available (works +differently between Windows and *nix) +.TP +.BI \-\-upx\-exclude \ FILE +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. +.UNINDENT +.SS Windows and Mac OS X specific options +.INDENT 0.0 +.TP +.B \-c\fP,\fB \-\-console\fP,\fB \-\-nowindowed +Open a console window for standard i/o (default). On +Windows this option will have no effect if the first +script is a \(aq.pyw\(aq file. +.TP +.B \-w\fP,\fB \-\-windowed\fP,\fB \-\-noconsole +Windows and Mac OS X: do not provide a console window +for standard i/o. On Mac OS X this also triggers +building an OS X .app bundle. On Windows this option +will be set if the first script is a \(aq.pyw\(aq file. This +option is ignored in *NIX systems. +.TP +.BI \-i \ \fR,\fB \ \-\-icon \ +FILE.ico: apply that 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 Mac OS +X. Use "NONE" to not apply any icon, thereby making +the OS to show some default (default: apply +PyInstaller\(aqs icon) +.UNINDENT +.SS Windows specific options +.INDENT 0.0 +.TP +.BI \-\-version\-file \ FILE +add a version resource from FILE to the exe +.TP +.BI \-m \ \fR,\fB \ \-\-manifest \ +add manifest FILE or XML to the exe +.TP +.BI \-r \ RESOURCE\fR,\fB \ \-\-resource \ RESOURCE +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. +.TP +.B \-\-uac\-admin +Using this option creates a Manifest which will +request elevation upon application restart. +.TP +.B \-\-uac\-uiaccess +Using this option allows an elevated application to +work with Remote Desktop. +.UNINDENT +.SS Windows Side\-by\-side Assembly searching options (advanced) +.INDENT 0.0 +.TP +.B \-\-win\-private\-assemblies +Any Shared Assemblies bundled into the application +will be changed into Private Assemblies. This means +the exact versions of these assemblies will always be +used, and any newer versions installed on user +machines at the system level will be ignored. +.TP +.B \-\-win\-no\-prefer\-redirects +While searching for Shared or Private Assemblies to +bundle into the application, PyInstaller will prefer +not to follow policies that redirect to newer +versions, and will try to bundle the exact versions of +the assembly. +.UNINDENT +.SS Mac OS X specific options +.INDENT 0.0 +.TP +.BI \-\-osx\-bundle\-identifier \ BUNDLE_IDENTIFIER +Mac OS X .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\(aqs basename) +.UNINDENT +.SS Rarely used special options +.INDENT 0.0 +.TP +.BI \-\-runtime\-tmpdir \ PATH +Where to extract libraries and support files in +\fIonefile\fP\-mode. If this option is given, the +bootloader will ignore any temp\-folder location +defined by the run\-time OS. The \fB_MEIxxxxxx\fP\-folder +will be created here. Please use this option only if +you know what you are doing. +.TP +.B \-\-bootloader\-ignore\-signals +Tell the bootloader to ignore signals rather than +forwarding them to the child process. Useful in +situations where e.g. a supervisor process signals +both the bootloader and child (e.g. via a process +group) to avoid signalling the child twice. +.UNINDENT +.SH ENVIRONMENT VARIABLES +.INDENT 0.0 +.TP +.B PYINSTALLER_CONFIG_DIR +This changes the directory where PyInstaller caches some files. +The default location for this is operating system dependent, +but is typically a subdirectory of the home directory. +.UNINDENT +.SH SEE ALSO +.sp +\fBpyi\-makespec\fP(1), +The PyInstaller Manual \fI\%https://pyinstaller.readthedocs.io/\fP, +Project Homepage \fI\%http://www.pyinstaller.org\fP +.SH AUTHOR +Hartmut Goebel +.SH COPYRIGHT +This document has been placed in the public domain. +.\" Generated by docutils manpage writer. +. diff --git a/3rdparty/pyinstaller-4.3/doc/requirements.rst b/3rdparty/pyinstaller-4.3/doc/requirements.rst new file mode 100644 index 0000000000000000000000000000000000000000..6b0f13a14585b084b6edfdaedd7bb65e34f1f785 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/requirements.rst @@ -0,0 +1,70 @@ +Requirements +============ + +.. Keep this list in sync with the README.txt + +Windows +~~~~~~~~ + +|PyInstaller| runs in Windows 8 or newer +(Windows 7 should work too, but is not supported). +It can create graphical windowed apps (apps that do not need a command window). + +|PyInstaller| requires two Python modules in a Windows system. +It requires either the PyWin32_ or pypiwin32_ Python extension for Windows. +If you install |PyInstaller| using pip, and PyWin32 is not already installed, +pypiwin32 is automatically installed. +|PyInstaller| also requires the pefile_ package. + +The pip-Win_ package is recommended, but not required. + +Mac OS X +~~~~~~~~~ + +|PyInstaller| runs in Mac OS X 10.7 (Lion) or newer. +It can build graphical windowed apps (apps that do not use a terminal window). +PyInstaller builds apps that are compatible with the Mac OS X release in +which you run it, and following releases. +It can build 32-bit binaries in Mac OS X releases that support them. + +GNU/Linux +~~~~~~~~~~ + +|PyInstaller| requires the ``ldd`` terminal application to discover +the shared libraries required by each program or shared library. +It is typically found in the distribution-package ``glibc`` or ``libc-bin``. + +It also requires the ``objdump`` terminal application to extract +information from object files +and the ``objcopy`` terminal application to append data to the +bootloader. +These are typically found in the distribution-package ``binutils``. + +AIX, Solaris, FreeBSD and OpenBSD +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Users have reported success running |PyInstaller| on these platforms, +but it is not tested on them. +The ``ldd`` and ``objdump`` commands are needed. + +Each bundled app contains a copy of a *bootloader*, +a program that sets up the application and starts it +(see :ref:`The Bootstrap Process in Detail`). + +When you install |PyInstaller| using pip_, the setup will attempt +to build a bootloader for this platform. +If that succeeds, the installation continues and |PyInstaller| is ready to use. + +If the pip_ setup fails to build a bootloader, +or if you do not use pip_ to install, +you must compile a bootloader manually. +The process is described under :ref:`Building the Bootloader`. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/runtime-information.rst b/3rdparty/pyinstaller-4.3/doc/runtime-information.rst new file mode 100644 index 0000000000000000000000000000000000000000..7be5fc7f329492251939852bdd9fb9ca22c914c9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/runtime-information.rst @@ -0,0 +1,228 @@ +.. _run-time information: + +Run-time Information +===================== + +Your app should run in a bundle exactly as it does when run from source. +However, you may want to learn at run-time whether the app is running from +source or whether it is bundled ("frozen"). You can use the following code to +check "are we bundled?":: + + import sys + if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'): + print('running in a PyInstaller bundle') + else: + print('running in a normal Python process') + +When a bundled app starts up, the |bootloader| sets the ``sys.frozen`` +attribute and stores the absolute path to the bundle folder in +``sys._MEIPASS``. For a one-folder bundle, this is the path to that folder. For +a one-file bundle, this is the path to the temporary folder created by the +|bootloader| (see :ref:`How the One-File Program Works`). + +When your app is running, it may need to access data files in one of the +following locations: + +* Files that were bundled with it (see :ref:`Adding Data Files`). +* Files the user has placed with the app bundle, say in the same folder. +* Files in the user's current working directory. + +The program has access to several variables for these uses. + + +Using ``__file__`` +~~~~~~~~~~~~~~~~~~ + +When your program is not bundled, the Python variable ``__file__`` refers to +the current path of the module it is contained in. When importing a module +from a bundled script, the |PyInstaller| |bootloader| will set the module's +``__file__`` attribute to the correct path relative to the bundle folder. + +For example, if you import ``mypackage.mymodule`` from a bundled script, then +the ``__file__`` attribute of that module will be ``sys._MEIPASS + +'mypackage/mymodule.pyc'``. So if you have a data file at +``mypackage/file.dat`` that you added to the bundle at ``mypackage/file.dat``, +the following code will get its path (in both the non-bundled and the bundled +case):: + + from os import path + path_to_dat = path.abspath(path.join(path.dirname(__file__), 'file.dat')) + +In the main script (the ``__main__`` module) itself, the ``__file__`` +variable contains path to the script file. In Python 3.8 and earlier, +this path is either absolute or relative (depending on how the script +was passed to the ``python`` interpreter), while in Python 3.9 and later, +it is always an absolute path. In the bundled script, the |PyInstaller| +|bootloader| always sets the ``__file__`` variable inside the ``__main__`` +module to the absolute path inside the bundle directory, as if the +byte-compiled entry-point script existed there. + +For example, if your entry-point script is called ``program.py``, then +the ``__file__`` attribute inside the bundled script will point to +``sys._MEIPASS + 'program.py'``. Therefore, locating a data file relative +to the main script can be either done directly using ``sys._MEIPASS`` or +via the parent path of the ``__file__`` inside the main script. + +The following example will get the path to a file ``other-file.dat`` +located next to the main script if not bundled and inside the bundle folder +if it is bundled:: + + from os import path + bundle_dir = path.abspath(path.dirname(__file__)) + path_to_dat = path.join(bundle_dir, 'other-file.dat') + +Or, if you'd rather use pathlib_:: + + from pathlib import Path + bundle_dir = Path(__file__).parent + path_to_dat = Path.cwd() / bundle_dir / "other-file.dat" + +.. versionchanged:: 5.0 + + Formerly, the ``__file__`` attribute of the entry-point script + (the ``__main__`` module) was set to only its basename rather than + its full (absolute or relative) path within the bundle directory. + Therefore, |PyInstaller| documentation used to suggest ``sys._MEIPASS`` + as means for locating resources relative to the bundled entry-point + script. Now, ``__file__`` is always set to the absolute full path, + and is the preferred way of locating such resources. + + +Placing data files at expected locations inside the bundle +---------------------------------------------------------- + +To place the data-files where your code expects them to be (i.e., relative +to the main script or bundle directory), you can use the **dest** parameter +of the ``--add-data=source:dest`` command-line switches. Assuming you normally +use the following code in a file named ``my_script.py`` to locate a file +``file.dat`` in the same folder:: + + from os import path + path_to_dat = path.abspath(path.join(path.dirname(__file__), 'file.dat')) + +Or the pathlib_ equivalent:: + + from pathlib import Path + path_to_dat = (Path.cwd() / __file__).with_name("file.dat") + +And ``my_script.py`` is **not** part of a package (not in a folder containing +an ``__init_.py``), then ``__file__`` will be ``[app root]/my_script.pyc`` +meaning that if you put ``file.dat`` in the root of your package, using:: + + PyInstaller --add-data=/path/to/file.dat:. + +It will be found correctly at runtime without changing ``my_script.py``. + +.. note:: Windows users should use ``;`` instead of ``:`` in the above line. + +If ``__file__`` is checked from inside a package or library (say +``my_library.data``) then ``__file__`` will be +``[app root]/my_library/data.pyc`` and ``--add-data`` should mirror that:: + + PyInstaller --add-data=/path/to/my_library/file.dat:./my_library + +However, in this case it is much easier to switch to :ref:`the spec file +` and use the :meth:`collect_data_files` helper function:: + + from PyInstaller.utils.hooks import collect_data_files + + a = Analysis(..., + datas=collect_data_files("my_library"), + ...) + +Using ``sys.executable`` and ``sys.argv[0]`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a normal Python script runs, ``sys.executable`` is the path to the +program that was executed, namely, the Python interpreter. +In a frozen app, ``sys.executable`` is also the path to the +program that was executed, but that is not Python; +it is the bootloader in either the one-file app +or the executable in the one-folder app. +This gives you a reliable way to locate the frozen executable the user +actually launched. + +The value of ``sys.argv[0]`` is the name or relative path that was +used in the user's command. +It may be a relative path or an absolute path depending +on the platform and how the app was launched. + +If the user launches the app by way of a symbolic link, +``sys.argv[0]`` uses that symbolic name, +while ``sys.executable`` is the actual path to the executable. +Sometimes the same app is linked under different names +and is expected to behave differently depending on the name that is +used to launch it. +For this case, you would test ``os.path.basename(sys.argv[0])`` + +On the other hand, sometimes the user is told to store the executable +in the same folder as the files it will operate on, +for example a music player that should be stored in the same folder +as the audio files it will play. +For this case, you would use ``os.path.dirname(sys.executable)``. + +The following small program explores some of these possibilities. +Save it as ``directories.py``. +Execute it as a Python script, +then bundled as a one-folder app. +Then bundle it as a one-file app and launch it directly and also via a +symbolic link:: + + #!/usr/bin/python3 + import sys, os + frozen = 'not' + if getattr(sys, 'frozen', False): + # we are running in a bundle + frozen = 'ever so' + bundle_dir = sys._MEIPASS + else: + # we are running in a normal Python environment + bundle_dir = os.path.dirname(os.path.abspath(__file__)) + print( 'we are',frozen,'frozen') + print( 'bundle dir is', bundle_dir ) + print( 'sys.argv[0] is', sys.argv[0] ) + print( 'sys.executable is', sys.executable ) + print( 'os.getcwd is', os.getcwd() ) + + + +LD_LIBRARY_PATH / LIBPATH considerations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This environment variable is used to discover libraries, it is the library +search path - on GNU/Linux and \*BSD `LD_LIBRARY_PATH` is used, on AIX it is +`LIBPATH`. + +If it exists, +PyInstaller saves the original value to `*_ORIG`, then modifies the search +path so that the bundled libraries are found first by the bundled code. + +But if your code executes a system program, you often do not want that this +system program loads your bundled libraries (that are maybe not compatible +with your system program) - it rather should load the correct libraries from +the system locations like it usually does. + +Thus you need to restore the original path before creating the subprocess +with the system program. + +:: + + env = dict(os.environ) # make a copy of the environment + lp_key = 'LD_LIBRARY_PATH' # for GNU/Linux and *BSD. + lp_orig = env.get(lp_key + '_ORIG') + if lp_orig is not None: + env[lp_key] = lp_orig # restore the original, unmodified value + else: + # This happens when LD_LIBRARY_PATH was not set. + # Remove the env var as a last resort: + env.pop(lp_key, None) + p = Popen(system_cmd, ..., env=env) # create the process + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/spec-files.rst b/3rdparty/pyinstaller-4.3/doc/spec-files.rst new file mode 100644 index 0000000000000000000000000000000000000000..b0867702c00e8d3fc8411ebf3205e0f47cb766bf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/spec-files.rst @@ -0,0 +1,615 @@ +.. _using spec files: + +Using Spec Files +================= + +When you execute + + ``pyinstaller`` *options*.. ``myscript.py`` + +the first thing |PyInstaller| does is to build a spec (specification) file +:file:`myscript.spec`. +That file is stored in the ``--specpath=`` directory, +by default the current directory. + +The spec file tells |PyInstaller| how to process your script. +It encodes the script names and most of the options +you give to the ``pyinstaller`` command. +The spec file is actually executable Python code. +|PyInstaller| builds the app by executing the contents of the spec file. + +For many uses of |PyInstaller| you do not need to examine or modify the spec file. +It is usually enough to +give all the needed information (such as hidden imports) +as options to the ``pyinstaller`` command and let it run. + +There are four cases where it is useful to modify the spec file: + +* When you want to bundle data files with the app. +* When you want to include run-time libraries (``.dll`` or ``.so`` files) that + |PyInstaller| does not know about from any other source. +* When you want to add Python run-time options to the executable. +* When you want to create a multiprogram bundle with merged common modules. + +These uses are covered in topics below. + +You create a spec file using this command: + + ``pyi-makespec`` *options* :file:`{name}.py` [*other scripts* ...] + +The *options* are the same options documented above +for the ``pyinstaller`` command. +This command creates the :file:`{name}.spec` file but does not +go on to build the executable. + +After you have created a spec file and modified it as necessary, +you build the application by passing the spec file to the ``pyinstaller`` command: + + ``pyinstaller`` *options* :file:`{name}.spec` + +When you create a spec file, most command options are encoded in the spec file. +When you build from a spec file, those options cannot be changed. +If they are given on the command line they are ignored and +replaced by the options in the spec file. + +Only the following command-line options have an effect when building from a spec file: + +* ``--upx-dir=`` +* ``--distpath=`` +* ``--workpath=`` +* ``--noconfirm`` +* ``--ascii`` +* ``--clean`` + +.. _spec-file operations: + +Spec File Operation +~~~~~~~~~~~~~~~~~~~~ + +After |PyInstaller| creates a spec file, +or opens a spec file when one is given instead of a script, +the ``pyinstaller`` command executes the spec file as code. +Your bundled application is created by the execution of the spec file. +The following is a shortened example of a spec file for a minimal, one-folder app:: + + block_cipher = None + a = Analysis(['minimal.py'], + pathex=['/Developer/PItests/minimal'], + binaries=None, + datas=None, + hiddenimports=[], + hookspath=None, + runtime_hooks=None, + excludes=None, + cipher=block_cipher) + pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) + exe = EXE(pyz,... ) + coll = COLLECT(...) + +The statements in a spec file create instances of four classes, +``Analysis``, ``PYZ``, ``EXE`` and ``COLLECT``. + +* A new instance of class ``Analysis`` takes a list of script names as input. + It analyzes all imports and other dependencies. + The resulting object (assigned to ``a``) contains lists of dependencies + in class members named: + + - ``scripts``: the python scripts named on the command line; + - ``pure``: pure python modules needed by the scripts; + - ``binaries``: non-python modules needed by the scripts, including names + given by the ``--add-binary`` option; + - ``datas``: non-binary files included in the app, including names given + by the ``--add-data`` option. + +* An instance of class ``PYZ`` is a ``.pyz`` archive (described + under :ref:`Inspecting Archives` below), which contains all the + Python modules from ``a.pure``. + +* An instance of ``EXE`` is built from the analyzed scripts and the ``PYZ`` + archive. This object creates the executable file. + +* An instance of ``COLLECT`` creates the output folder from all the other parts. + +In one-file mode, there is no call to ``COLLECT``, and the +``EXE`` instance receives all of the scripts, modules and binaries. + +You modify the spec file to pass additional values to ``Analysis`` and +to ``EXE``. + + +.. _adding files to the bundle: + +Adding Files to the Bundle +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To add files to the bundle, you create a list that describes the files +and supply it to the ``Analysis`` call. +When you bundle to a single folder (see :ref:`Bundling to One Folder`), +the added data files are copied into the folder with the executable. +When you bundle to a single executable (see :ref:`Bundling to One File`), +copies of added files are compressed into the executable, and expanded to the +:file:`_MEI{xxxxxx}` temporary folder before execution. +This means that any changes a one-file executable makes to an added file +will be lost when the application ends. + +In either case, to find the data files at run-time, see :ref:`Run-time Information`. + + +.. _adding data files: + +Adding Data Files +------------------ + +You can add data files to the bundle by using the ``--add-data`` command option, or by +adding them as a list to the spec file. + +When using the spec file, provide a list that +describes the files as the value of the ``datas=`` argument to ``Analysis``. +The list of data files is a list of tuples. +Each tuple has two values, both of which must be strings: + + * The first string specifies the file or files as they are in this system now. + + * The second specifies the name of the *folder* to contain + the files at run-time. + +For example, to add a single README file to the top level of a one-folder app, +you could modify the spec file as follows:: + + a = Analysis(... + datas=[ ('src/README.txt', '.') ], + ... + ) + +And the command line equivalent (see +:ref:`options-group What to bundle, where to search` +for platform-specific details):: + + pyinstaller --add-data 'src/README.txt:.' myscript.py + +You have made the ``datas=`` argument a one-item list. +The item is a tuple in which the first string says the existing file +is :file:`src/README.txt`. +That file will be looked up (relative to the location of the spec file) +and copied into the top level of the bundled app. + +The strings may use either ``/`` or ``\`` as the path separator character. +You can specify input files using "glob" abbreviations. +For example to include all the ``.mp3`` files from a certain folder:: + + a = Analysis(... + datas= [ ('/mygame/sfx/*.mp3', 'sfx' ) ], + ... + ) + +All the ``.mp3`` files in the folder :file:`/mygame/sfx` will be copied +into a folder named ``sfx`` in the bundled app. + +The spec file is more readable if you create the list of added files +in a separate statement:: + + added_files = [ + ( 'src/README.txt', '.' ), + ( '/mygame/sfx/*.mp3', 'sfx' ) + ] + a = Analysis(... + datas = added_files, + ... + ) + +You can also include the entire contents of a folder:: + + added_files = [ + ( 'src/README.txt', '.' ), + ( '/mygame/data', 'data' ), + ( '/mygame/sfx/*.mp3', 'sfx' ) + ] + +The folder :file:`/mygame/data` will be reproduced under the name +:file:`data` in the bundle. + + +.. _using data files from a module: + +Using Data Files from a Module +-------------------------------- + +If the data files you are adding are contained within a Python module, +you can retrieve them using ``pkgutil.get_data()``. + +For example, suppose that part of your application is a module named ``helpmod``. +In the same folder as your script and its spec file you have this folder +arrangement:: + + helpmod + __init__.py + helpmod.py + help_data.txt + +Because your script includes the statement ``import helpmod``, +|PyInstaller| will create this folder arrangement in your bundled app. +However, it will only include the ``.py`` files. +The data file :file:`help_data.txt` will not be automatically included. +To cause it to be included also, you would add a ``datas`` tuple +to the spec file:: + + a = Analysis(... + datas= [ ('helpmod/help_data.txt', 'helpmod' ) ], + ... + ) + +When your script executes, you could find ``help_data.txt`` by +using its base folder path, as described in the previous section. +However, this data file is part of a module, so you can also retrieve +its contents using the standard library function ``pkgutil.get_data()``:: + + import pkgutil + help_bin = pkgutil.get_data( 'helpmod', 'help_data.txt' ) + +This returns the contents of the :file:`help_data.txt` +file as a binary string. +If it is actually characters, you must decode it:: + + help_utf = help_bin.decode('UTF-8', 'ignore') + + +.. _adding binary files: + +Adding Binary Files +-------------------- + +.. Note:: `Binary` files refers to DLLs, dynamic libraries, shared + object-files, and such, which |PyInstaller| is going to search for further + `binary` dependencies. Files like images and PDFs should go into the + ``datas``. + +You can add binary files to the bundle by using the ``--add-binary`` command option, +or by adding them as a list to the spec file. +In the spec file, make a list of tuples that describe the files needed. +Assign the list of tuples to the ``binaries=`` argument of Analysis. + +Adding binary files works in a similar way as adding data files. As described in +:ref:`Adding Binary Files`, each tuple should have two values: + + * The first string specifies the file or files as they are in this system now. + + * The second specifies the name of the *folder* to contain + the files at run-time. + +Normally |PyInstaller| learns about ``.so`` and ``.dll`` libraries by +analyzing the imported modules. +Sometimes it is not clear that a module is imported; +in that case you use a ``--hidden-import=`` command option. +But even that might not find all dependencies. + +Suppose you have a module ``special_ops.so`` that is written in C +and uses the Python C-API. +Your program imports ``special_ops``, and |PyInstaller| finds and +includes ``special_ops.so``. +But perhaps ``special_ops.so`` links to ``libiodbc.2.dylib``. +|PyInstaller| does not find this dependency. +You could add it to the bundle this way:: + + a = Analysis(... + binaries=[ ( '/usr/lib/libiodbc.2.dylib', '.' ) ], + ... + +Or via the command line (again, see +:ref:`options-group What to bundle, where to search` +for platform-specific details):: + + pyinstaller --add-binary '/usr/lib/libiodbc.2.dylib:.' myscript.py + +If you wish to store ``libiodbc.2.dylib`` on a specific folder inside the bundle, +for example ``vendor``, then you could specify it, using the second element of the tuple:: + + a = Analysis(... + binaries=[ ( '/usr/lib/libiodbc.2.dylib', 'vendor' ) ], + ... + +As with data files, if you have multiple binary files to add, +to improve readability, +create the list in a separate statement and pass the list by name. + +Advanced Methods of Adding Files +--------------------------------- + +|PyInstaller| supports a more advanced (and complex) way of adding +files to the bundle that may be useful for special cases. +See :ref:`The TOC and Tree Classes` below. + + +.. _giving run-time python options: + +Giving Run-time Python Options +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can pass command-line options to the Python interpreter. +The interpreter takes a number of command-line options but only the +following are supported for a bundled app: + +* ``v`` to write a message to stdout each time a module is initialized. + +* ``u`` for unbuffered stdio. + +* ``W`` and an option to change warning behavior: ``W ignore`` or + ``W once`` or ``W error``. + +To pass one or more of these options, +create a list of tuples, one for each option, and pass the list as +an additional argument to the EXE call. +Each tuple has three elements: + +* The option as a string, for example ``v`` or ``W ignore``. + +* None + +* The string ``OPTION`` + +For example modify the spec file this way:: + + options = [ ('v', None, 'OPTION'), ('W ignore', None, 'OPTION') ] + a = Analysis( ... + ) + ... + exe = EXE(pyz, + a.scripts, + options, <--- added line + exclude_binaries=... + ) + +.. Note:: The unbuffered stdio mode (the ``u`` option) enables unbuffered + binary layer of ``stdout`` and ``stderr`` streams on all supported Python + versions. The unbuffered text layer requires Python 3.7 or later. + + +.. _spec file options for a mac os x bundle: + +Spec File Options for a Mac OS X Bundle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When you build a windowed Mac OS X app +(that is, running in Mac OS X, you specify the ``--onefile --windowed`` options), +the spec file contains an additional statement to +create the Mac OS X application bundle, or app folder:: + + app = BUNDLE(exe, + name='myscript.app', + icon=None, + bundle_identifier=None) + +The ``icon=`` argument to ``BUNDLE`` will have the path to an icon file +that you specify using the ``--icon=`` option. +The ``bundle_identifier`` will have the value you specify with the +``--osx-bundle-identifier=`` option. + +An :file:`Info.plist` file is an important part of a Mac OS X app bundle. +(See the `Apple bundle overview`_ for a discussion of the contents +of ``Info.plist``.) + +|PyInstaller| creates a minimal :file:`Info.plist`. +The ``version`` option can be used to set the application version +using the CFBundleShortVersionString Core Foundation Key. + +You can add or overwrite entries in the plist by passing an +``info_plist=`` parameter to the BUNDLE call. Its argument should be a +Python dict with keys and values to be included in the :file:`Info.plist` +file. +|PyInstaller| creates :file:`Info.plist` from the info_plist dict +using the Python Standard Library module plistlib_. +plistlib can handle nested Python objects (which are translated to nested +XML), and translates Python data types to the proper :file:`Info.plist` +XML types. Here's an example:: + + app = BUNDLE(exe, + name='myscript.app', + icon=None, + bundle_identifier=None, + version='0.0.1', + info_plist={ + 'NSPrincipalClass': 'NSApplication', + 'NSAppleScriptEnabled': False, + 'CFBundleDocumentTypes': [ + { + 'CFBundleTypeName': 'My File Format', + 'CFBundleTypeIconFile': 'MyFileIcon.icns', + 'LSItemContentTypes': ['com.example.myformat'], + 'LSHandlerRank': 'Owner' + } + ] + }, + ) + +In the above example, the key/value ``'NSPrincipalClass': 'NSApplication'`` is +necessary to allow Mac OS X to render applications using retina resolution. +The key ``'NSAppleScriptEnabled'`` is assigned the Python boolean +``False``, which will be output to :file:`Info.plist` properly as ````. +Finally the key ``CFBundleDocumentTypes`` tells Mac OS X what filetypes your +application supports (see `Apple document types`_). + +Multipackage Bundles +~~~~~~~~~~~~~~~~~~~~~ + +Some products are made of several different apps, +each of which might +depend on a common set of third-party libraries, or share code in other ways. +When packaging such a product it +would be a pity to treat each app in isolation, bundling it with +all its dependencies, because that means storing duplicate copies +of code and libraries. + +You can use the multipackage feature to bundle a set of executable apps +so that they share single copies of libraries. +You can do this with either one-file or one-folder apps. +Each dependency (a DLL, for example) is packaged only once, in one of the apps. +Any other apps in the set that depend on that DLL +have an "external reference" to it, telling them +to extract that dependency from the executable file of the app that contains it. + +This saves disk space because each dependency is stored only once. +However, to follow an external reference takes extra time when an app is starting up. +All but one of the apps in the set will have slightly slower launch times. + +The external references between binaries include hard-coded +paths to the output directory, and cannot be rearranged. +If you use one-folder mode, you must +install all the application folders within a single parent directory. +If you use one-file mode, you must place all +the related applications in the same directory +when you install the application. + +To build such a set of apps you must code a custom +spec file that contains a call to the ``MERGE`` function. +This function takes a list of analyzed scripts, +finds their common dependencies, and modifies the analyses +to minimize the storage cost. + +The order of the analysis objects in the argument list matters. +The MERGE function packages each dependency into the +first script from left to right that needs that dependency. +A script that comes later in the list and needs the same file +will have an external reference to the prior script in the list. +You might sequence the scripts to place the most-used scripts first in the list. + +A custom spec file for a multipackage bundle contains one call to the MERGE function:: + + MERGE(*args) + +MERGE is used after the analysis phase and before ``EXE`` and ``COLLECT``. +Its variable-length list of arguments consists of +a list of tuples, each tuple having three elements: + +* The first element is an Analysis object, an instance of class Analysis, + as applied to one of the apps. + +* The second element is the script name of the analyzed app (without the ``.py`` extension). + +* The third element is the name for the executable (usually the same as the script). + +MERGE examines the Analysis objects to learn the dependencies of each script. +It modifies these objects to avoid duplication of libraries and modules. +As a result the packages generated will be connected. + + +Example MERGE spec file +------------------------ + +One way to construct a spec file for a multipackage bundle is to +first build a spec file for each app in the package. +Suppose you have a product that comprises three apps named +(because we have no imagination) ``foo``, ``bar`` and ``zap``: + + ``pyi-makespec`` *options as appropriate...* ``foo.py`` + + ``pyi-makespec`` *options as appropriate...* ``bar.py`` + + ``pyi-makespec`` *options as appropriate...* ``zap.py`` + +Check for warnings and test each of the apps individually. +Deal with any hidden imports and other problems. +When all three work correctly, +combine the statements from the three files :file:`foo.spec`, +:file:`bar.spec` and :file:`zap.spec` +as follows. + +First copy the Analysis statements from each, +changing them to give each Analysis object a unique name:: + + foo_a = Analysis(['foo.py'], + pathex=['/the/path/to/foo'], + hiddenimports=[], + hookspath=None) + + bar_a = Analysis(['bar.py'], etc., etc... + + zap_a = Analysis(['zap.py'], etc., etc... + +Now call the MERGE method to process the three Analysis objects:: + + MERGE( (foo_a, 'foo', 'foo'), (bar_a, 'bar', 'bar'), (zap_a, 'zap', 'zap') ) + +The Analysis objects ``foo_a``, ``bar_a``, and ``zap_a`` are modified +so that the latter two refer to the first for common dependencies. + +Following this you can copy the ``PYZ``, ``EXE`` and ``COLLECT`` statements from +the original three spec files, +substituting the unique names of the Analysis objects +where the original spec files have ``a.``, for example:: + + foo_pyz = PYZ(foo_a.pure) + foo_exe = EXE(foo_pyz, foo_a.scripts, ... etc. + foo_coll = COLLECT( foo_exe, foo_a.binaries, foo_a.datas... etc. + + bar_pyz = PYZ(bar_a.pure) + bar_exe = EXE(bar_pyz, bar_a.scripts, ... etc. + bar_coll = COLLECT( bar_exe, bar_a.binaries, bar_a.datas... etc. + +(If you are building one-file apps, there is no ``COLLECT`` step.) +Save the combined spec file as ``foobarzap.spec`` and then build it:: + + pyi-build foobarzap.spec + +The output in the :file:`dist` folder will be all three apps, but +the apps :file:`dist/bar/bar` and :file:`dist/zap/zap` will refer to +the contents of :file:`dist/foo/` for shared dependencies. + +There are several multipackage examples in the +|PyInstaller| distribution folder under :file:`tests/functional/specs`. + +Remember that a spec file is executable Python. +You can use all the Python facilities (``for`` and ``with`` +and the members of ``sys`` and ``io``) +in creating the Analysis +objects and performing the ``PYZ``, ``EXE`` and ``COLLECT`` statements. +You may also need to know and use :ref:`The TOC and Tree Classes` described below. + +Globals Available to the Spec File +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While a spec file is executing it has access to a limited set of global names. +These names include the classes defined by |PyInstaller|: +``Analysis``, ``BUNDLE``, ``COLLECT``, ``EXE``, ``MERGE``, +``PYZ``, ``TOC`` and ``Tree``, +which are discussed in the preceding sections. + +Other globals contain information about the build environment: + +``DISTPATH`` + The relative path to the :file:`dist` folder where + the application will be stored. + The default path is relative to the current directory. + If the ``--distpath=`` option is used, ``DISTPATH`` contains that value. + +``HOMEPATH`` + The absolute path to the |PyInstaller| + distribution, typically in the current Python site-packages folder. + +``SPEC`` + The complete spec file argument given to the + ``pyinstaller`` command, for example :file:`myscript.spec` + or :file:`source/myscript.spec`. + +``SPECPATH`` + The path prefix to the ``SPEC`` value as returned by ``os.path.split()``. + +``specnm`` + The name of the spec file, for example :file:`myscript`. + +``workpath`` + The path to the :file:`build` directory. The default is relative to + the current directory. If the ``workpath=`` option is used, + ``workpath`` contains that value. + +``WARNFILE`` + The full path to the warnings file in the build directory, + for example :file:`build/warn-myscript.txt`. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/usage.rst b/3rdparty/pyinstaller-4.3/doc/usage.rst new file mode 100644 index 0000000000000000000000000000000000000000..1c9ebaa95f78af09a747e0942766537718eac02d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/usage.rst @@ -0,0 +1,629 @@ +.. _using pyinstaller: + +==================== +Using PyInstaller +==================== + + +The syntax of the ``pyinstaller`` command is: + + ``pyinstaller`` [*options*] *script* [*script* ...] | *specfile* + +In the most simple case, +set the current directory to the location of your program ``myscript.py`` +and execute:: + + pyinstaller myscript.py + +|PyInstaller| analyzes :file:`myscript.py` and: + +* Writes :file:`myscript.spec` in the same folder as the script. +* Creates a folder :file:`build` in the same folder as the script if it does not exist. +* Writes some log files and working files in the ``build`` folder. +* Creates a folder :file:`dist` in the same folder as the script if it does not exist. +* Writes the :file:`myscript` executable folder in the :file:`dist` folder. + +In the :file:`dist` folder you find the bundled app you distribute to your users. + +Normally you name one script on the command line. +If you name more, all are analyzed and included in the output. +However, the first script named supplies the name for the +spec file and for the executable folder or file. +Its code is the first to execute at run-time. + +For certain uses you may edit the contents of ``myscript.spec`` +(described under :ref:`Using Spec Files`). +After you do this, you name the spec file to |PyInstaller| instead of the script: + + ``pyinstaller myscript.spec`` + +The :file:`myscript.spec` file contains most of the information +provided by the options that were specified when +:command:`pyinstaller` (or :command:`pyi-makespec`) +was run with the script file as the argument. +You typically do not need to specify any options when running +:command:`pyinstaller` with the spec file. +Only :ref:`a few command-line options ` +have an effect when building from a spec file. + +You may give a path to the script or spec file, for example + + ``pyinstaller`` `options...` ``~/myproject/source/myscript.py`` + +or, on Windows, + + ``pyinstaller "C:\Documents and Settings\project\myscript.spec"`` + + +Options +~~~~~~~~~~~~~~~ + +General Options +------------------ + +.. include:: _pyinstaller-options.tmp + + + +Shortening the Command +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Because of its numerous options, a full ``pyinstaller`` command +can become very long. +You will run the same command again and again as you develop +your script. +You can put the command in a shell script or batch file, +using line continuations to make it readable. +For example, in GNU/Linux:: + + pyinstaller --noconfirm --log-level=WARN \ + --onefile --nowindow \ + --add-data="README:." \ + --add-data="image1.png:img" \ + --add-binary="libfoo.so:lib" \ + --hidden-import=secret1 \ + --hidden-import=secret2 \ + --upx-dir=/usr/local/share/ \ + myscript.spec + +Or in Windows, use the little-known BAT file line continuation:: + + pyinstaller --noconfirm --log-level=WARN ^ + --onefile --nowindow ^ + --add-data="README;." ^ + --add-data="image1.png;img" ^ + --add-binary="libfoo.so;lib" ^ + --hidden-import=secret1 ^ + --hidden-import=secret2 ^ + --icon=..\MLNMFLCN.ICO ^ + myscript.spec + + +Running |PyInstaller| from Python code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to run |PyInstaller| from Python code, you can use the ``run`` function +defined in ``PyInstaller.__main__``. For instance, the following code: + +.. code-block:: python + + import PyInstaller.__main__ + + PyInstaller.__main__.run([ + 'my_script.py', + '--onefile', + '--windowed' + ]) + +Is equivalent to: + +.. code-block:: shell + + pyinstaller my_script.py --onefile --windowed + + +Running |PyInstaller| with Python optimizations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. Note:: + + When using this feature, you should be aware of how the Python bytecode + optimization mechanism works. When using ``-O``, ``__debug__`` is set + to ``False`` and ``assert`` statements are removed from the bytecode. + The ``-OO`` flag additionally removes docstrings. + + Using this feature affects not only your main script, but *all* modules + included by |PyInstaller|. If your code (or any module imported by your + script) relies on these features, your program may break or have + unexpected behavior. + +|PyInstaller| can be run with Python optimization flags (``-O`` or ``-OO``) +by executing it as a Python module, rather than using the ``pyinstaller`` +command:: + + # run with basic optimizations + python -O -m PyInstaller myscript.py + + # also discard docstrings + python -OO -m PyInstaller myscript.py + +Or, by explicitly setting the ``PYTHONOPTIMIZE`` environment variable +to a non-zero value:: + + # Unix + PYTHONOPTIMIZE=1 pyinstaller myscript.py + + # Windows + set PYTHONOPTIMIZE=1 && pyinstaller myscript.py + +You can use any |PyInstaller| options that are otherwise available with +the ``pyinstaller`` command. For example:: + + python -O -m PyInstaller --onefile myscript.py + +Alternatively, you can also use the path to pyinstaller:: + + python -O /path/to/pyinstaller myscript.py + +Using UPX +~~~~~~~~~~~~~~~~~~~ + +UPX_ is a free utility available for most operating systems. +UPX compresses executable files and libraries, making them smaller, +sometimes much smaller. +UPX is available for most operating systems and can compress +a large number of executable file formats. +See the UPX_ home page for downloads, and for the list of +supported executable formats. + +A compressed executable program is wrapped in UPX +startup code that dynamically decompresses the program +when the program is launched. +After it has been decompressed, the program runs normally. +In the case of a |PyInstaller| one-file executable that has +been UPX-compressed, the full execution sequence is: + +* The compressed program start up in the UPX decompressor code. +* After decompression, the program executes the |PyInstaller| |bootloader|, + which creates a temporary environment for Python. +* The Python interpreter executes your script. + +|PyInstaller| looks for UPX on the execution path +or the path specified with the ``--upx-dir`` option. +If UPX exists, |PyInstaller| applies it to the final executable, +unless the ``--noupx`` option was given. +UPX has been used with |PyInstaller| output often, usually with no problems. + + +.. _encrypting python bytecode: + +Encrypting Python Bytecode +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To encrypt the Python bytecode modules stored in the bundle, +pass the ``--key=``\ *key-string* argument on +the command line. + +For this to work, you need to run:: + + pip install pyinstaller[encryption] + +The *key-string* is a string of 16 characters which is used to +encrypt each file of Python byte-code before it is stored in +the archive inside the executable file. + +This feature uses the tinyaes_ module internally for the encryption. + + +.. _defining the extraction location: + +Defining the Extraction Location +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In rare cases, when you bundle to a single executable +(see :ref:`Bundling to One File` and :ref:`how the one-file program works`), +you may want to control the location of the temporary directory at compile +time. This can be done using the ``--runtime-tmpdir`` option. If this option is +given, the bootloader will ignore any temp-folder location defined by the +run-time OS. Please use this option only if you know what you are doing. + + +.. _supporting multiple platforms: + +Supporting Multiple Platforms +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you distribute your application for only one combination of OS and Python, +just install |PyInstaller| like any other package and use it in your +normal development setup. + + +Supporting Multiple Python Environments +----------------------------------------- + +When you need to bundle your application within one OS +but for different versions of Python and support libraries -- for example, +a Python 3.6 version and a Python 3.7 version; +or a supported version that uses Qt4 and a development version that uses Qt5 -- +we recommend you use venv_. +With `venv` you can maintain different combinations of Python +and installed packages, and switch from one combination to another easily. +These are called `virtual environments` or `venvs` in short. + +* Use `venv` to create as many different development environments as you need, + each with its unique combination of Python and installed packages. +* Install |PyInstaller| in each virtual environment. +* Use |PyInstaller| to build your application in each virtual environment. + +Note that when using `venv`, the path to the |PyInstaller| commands is: + +* Windows: ENV_ROOT\\Scripts +* Others: ENV_ROOT/bin + +Under Windows, the pip-Win_ package makes it +especially easy to set up different environments and switch between them. +Under GNU/Linux and Mac OS, you switch environments at the command line. + +See :pep:`405` +and the official `Python Tutorial on Virtual Environments and Packages +`_ +for more information about Python virtual environments. + + +Supporting Multiple Operating Systems +--------------------------------------- + +If you need to distribute your application for more than one OS, +for example both Windows and Mac OS X, you must install |PyInstaller| +on each platform and bundle your app separately on each. + +You can do this from a single machine using virtualization. +The free virtualBox_ or the paid VMWare_ and Parallels_ +allow you to run another complete operating system as a "guest". +You set up a virtual machine for each "guest" OS. +In it you install +Python, the support packages your application needs, and PyInstaller. + +A `File Sync & Share`__ system like NextCloud_ is useful with virtual machines. +Install the synchronization client in each virtual machine, +all linked to your synchronization account. +Keep a single copy of your script(s) in a synchronized folder. +Then on any virtual machine you can run |PyInstaller| thus:: + + cd ~/NextCloud/project_folder/src # GNU/Linux, Mac -- Windows similar + rm *.pyc # get rid of modules compiled by another Python + pyinstaller --workpath=path-to-local-temp-folder \ + --distpath=path-to-local-dist-folder \ + ...other options as required... \ + ./myscript.py + +__ https://en.wikipedia.org/wiki/Enterprise_file_synchronization_and_sharing + +|PyInstaller| reads scripts from the common synchronized folder, +but writes its work files and the bundled app in folders that +are local to the virtual machine. + +If you share the same home directory on multiple platforms, for +example GNU/Linux and OS X, you will need to set the PYINSTALLER_CONFIG_DIR +environment variable to different values on each platform otherwise +PyInstaller may cache files for one platform and use them on the other +platform, as by default it uses a subdirectory of your home directory +as its cache location. + +It is said to be possible to cross-develop for Windows under GNU/Linux +using the free Wine_ environment. +Further details are needed, see `How to Contribute`_. + + +.. _capturing windows version data: + +Capturing Windows Version Data +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A Windows app may require a Version resource file. +A Version resource contains a group of data structures, +some containing binary integers and some containing strings, +that describe the properties of the executable. +For details see the Microsoft `Version Information Structures`_ page. + +Version resources are complex and +some elements are optional, others required. +When you view the version tab of a Properties dialog, +there's no simple relationship between +the data displayed and the structure of the resource. +For this reason |PyInstaller| includes the ``pyi-grab_version`` command. +It is invoked with the full path name of any Windows executable +that has a Version resource: + + ``pyi-grab_version`` *executable_with_version_resource* + +The command writes text that represents +a Version resource in readable form to standard output. +You can copy it from the console window or redirect it to a file. +Then you can edit the version information to adapt it to your program. +Using ``pyi-grab_version`` you can find an executable that displays the kind of +information you want, copy its resource data, and modify it to suit your package. + +The version text file is encoded UTF-8 and may contain non-ASCII characters. +(Unicode characters are allowed in Version resource string fields.) +Be sure to edit and save the text file in UTF-8 unless you are +certain it contains only ASCII string values. + +Your edited version text file can be given with the ``--version-file=`` +option to ``pyinstaller`` or ``pyi-makespec``. +The text data is converted to a Version resource and +installed in the bundled app. + +In a Version resource there are two 64-bit binary values, +``FileVersion`` and ``ProductVersion``. +In the version text file these are given as four-element tuples, +for example:: + + filevers=(2, 0, 4, 0), + prodvers=(2, 0, 4, 0), + +The elements of each tuple represent 16-bit values +from most-significant to least-significant. +For example the value ``(2, 0, 4, 0)`` resolves to +``0002000000040000`` in hex. + +You can also install a Version resource from a text file after +the bundled app has been created, using the ``pyi-set_version`` command: + + ``pyi-set_version`` *version_text_file* *executable_file* + +The ``pyi-set_version`` utility reads a version text file as written +by ``pyi-grab_version``, converts it to a Version resource, +and installs that resource in the *executable_file* specified. + +For advanced uses, examine a version text file as written by ``pyi-grab_version``. +You find it is Python code that creates a ``VSVersionInfo`` object. +The class definition for ``VSVersionInfo`` is found in +``utils/win32/versioninfo.py`` in the |PyInstaller| distribution folder. +You can write a program that imports ``versioninfo``. +In that program you can ``eval`` +the contents of a version info text file to produce a +``VSVersionInfo`` object. +You can use the ``.toRaw()`` method of that object to +produce a Version resource in binary form. +Or you can apply the ``unicode()`` function to the object +to reproduce the version text file. + + +Building Mac OS X App Bundles +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Under Mac OS X, |PyInstaller| always builds a UNIX executable in +:file:`dist`. +If you specify ``--onedir``, the output is a folder named :file:`myscript` +containing supporting files and an executable named :file:`myscript`. +If you specify ``--onefile``, the output is a single UNIX executable +named :file:`myscript`. +Either executable can be started from a Terminal command line. +Standard input and output work as normal through that Terminal window. + +If you specify ``--windowed`` with either option, the ``dist`` folder +also contains an OS X application named :file:`myscript.app`. + +As you probably know, an application is a special type of folder. +The one built by |PyInstaller| contains a folder always named +:file:`Contents` which contains: + + + A folder :file:`Frameworks` which is empty. + + A folder :file:`Resources` that contains an icon file. + + A file :file:`Info.plist` that describes the app. + + A folder :file:`MacOS` that contains the the executable and + supporting files, just as in the ``--onedir`` folder. + +Use the ``icon=`` argument to specify a custom icon for the application. +It will be copied into the :file:`Resources` folder. +(If you do not specify an icon file, |PyInstaller| supplies a +file :file:`icon-windowed.icns` with the |PyInstaller| logo.) + +Use the ``osx-bundle-identifier=`` argument to add a bundle identifier. +This becomes the ``CFBundleIdentifier`` used in code-signing +(see the `PyInstaller code signing recipe`_ +and for more detail, the `Apple code signing overview`_ technical note). + +You can add other items to the :file:`Info.plist` by editing the spec file; +see :ref:`Spec File Options for a Mac OS X Bundle` below. + + +Platform-specific Notes +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +GNU/Linux +------------------- + +Making GNU/Linux Apps Forward-Compatible +========================================== + +Under GNU/Linux, |PyInstaller| does not bundle ``libc`` +(the C standard library, usually ``glibc``, the Gnu version) with the app. +Instead, the app expects to link dynamically to the ``libc`` from the +local OS where it runs. +The interface between any app and ``libc`` is forward compatible to +newer releases, but it is not backward compatible to older releases. + +For this reason, if you bundle your app on the current version of GNU/Linux, +it may fail to execute (typically with a runtime dynamic link error) if +it is executed on an older version of GNU/Linux. + +The solution is to always build your app on the *oldest* version of +GNU/Linux you mean to support. +It should continue to work with the ``libc`` found on newer versions. + +The GNU/Linux standard libraries such as ``glibc`` are distributed in 64-bit +and 32-bit versions, and these are not compatible. +As a result you cannot bundle your app on a 32-bit system and run it +on a 64-bit installation, nor vice-versa. +You must make a unique version of the app for each word-length supported. + +.. _Platform-specific Notes - Windows: + +Windows +--------------- + +The developer needs to take +special care to include the Visual C++ run-time .dlls: +Python 3.5+ uses Visual Studio 2015 run-time, which has been renamed into +`“Universal CRT“ +`_ +and has become part of Windows 10. +For Windows Vista through Windows 8.1 there are Windows Update packages, +which may or may not be installed in the target-system. +So you have the following options: + +1. Build on *Windows 7* which has been reported to work. + +2. Include one of the VCRedist packages (the redistributable package files) + into your application's installer. This is Microsoft's recommended way, see + “Distributing Software that uses the Universal CRT“ in the above-mentioned + link, numbers 2 and 3. + +3. Install the `Windows Software Development Kit (SDK) for Windows 10 + `_ and expand the + `.spec`-file to include the required DLLs, see “Distributing Software that + uses the Universal CRT“ in the above-mentioned link, number 6. + + If you think, |PyInstaller| should do this by itself, please :ref:`help + improving ` |PyInstaller|. + + + +Mac OS X +------------------- + +Making Mac OS X apps Forward-Compatible +======================================== + +In Mac OS X, components from one version of the OS are usually compatible +with later versions, but they may not work with earlier versions. + +The only way to be certain your app supports an older version of Mac OS X +is to run PyInstaller in the oldest version of the OS you need to support. + +For example, to be sure of compatibility with "Snow Leopard" (10.6) +and later versions, you should execute PyInstaller in that environment. +You would create a copy of Mac OS X 10.6, typically in a virtual machine. +In it, install the desired level of Python +(the default Python in Snow Leopard was 2.6, which PyInstaller no longer supports), +and install |PyInstaller|, your source, and all its dependencies. +Then build your app in that environment. +It should be compatible with later versions of Mac OS X. + + +Building 32-bit Apps in Mac OS X +==================================== + +.. note:: This section still refers to Python 2.7 provided by Apple. + It might not be valid for Python 3 installed + from `MacPorts`_ or `Homebrew`_. + + Please contribute to keep this section up-to-date. + +Older versions of Mac OS X supported both 32-bit and 64-bit executables. +PyInstaller builds an app using the the word-length of the Python used to execute it. +That will typically be a 64-bit version of Python, +resulting in a 64-bit executable. +To create a 32-bit executable, run PyInstaller under a 32-bit Python. + +Python as installed in OS X will usually be executable in either 64- or 32-bit mode. +To verify this, apply the ``file`` command to the Python executable:: + + $ file /usr/local/bin/python3 + /usr/local/bin/python3: Mach-O universal binary with 2 architectures + /usr/local/bin/python3 (for architecture i386): Mach-O executable i386 + /usr/local/bin/python3 (for architecture x86_64): Mach-O 64-bit executable x86_64 + +The OS chooses which architecture to run, and typically defaults to 64-bit. +You can force the use of either architecture by name using the ``arch`` command:: + + $ /usr/local/bin/python3 + Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 5 2014, 20:42:22) + [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin + Type "help", "copyright", "credits" or "license" for more information. + >>> import sys; sys.maxsize + 9223372036854775807 + + $ arch -i386 /usr/local/bin/python3 + Python 3.4.2 (v3.4.2:ab2c023a9432, Oct 5 2014, 20:42:22) + [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin + Type "help", "copyright", "credits" or "license" for more information. + >>> import sys; sys.maxsize + 2147483647 + +Apple's default ``/usr/bin/python`` may circumvent the ``arch`` +specification and run 64-bit regardless. +(That is not the case if you apply ``arch`` to a specific version +such as ``/usr/bin/python2.7``.) +To make sure of running 32-bit in all cases, set the following environment variable:: + + VERSIONER_PYTHON_PREFER_32_BIT=yes + arch -i386 /usr/bin/python pyinstaller --clean -F -w myscript.py + + +Getting the Opened Document Names +==================================== + +.. Note:: + + Support for OpenDocument events is broken in |PyInstaller| 3.0 + owing to code changes needed in the bootloader to support current + versions of Mac OS X. + Do not attempt to use this feature until it has been fixed. + If this feature is important to you, follow and comment on + the status of `PyInstaller Issue #1309`_. + +When a user double-clicks a document of a type your application +supports, or when a user drags a document icon and drops it +on your application's icon, Mac OS X launches your application +and provides the name(s) of the opened document(s) in the +form of an OpenDocument AppleEvent. +This AppleEvent is received by the |bootloader| +before your code has started executing. + +The |bootloader| gets the names of opened documents from +the OpenDocument event and encodes them into the ``argv`` +string before starting your code. +Thus your code can query ``sys.argv`` to get the names +of documents that should be opened at startup. + +OpenDocument is the only AppleEvent the |bootloader| handles. +If you want to handle other events, or events that +are delivered after the program has launched, you must +set up the appropriate handlers. + + +AIX +---------------------- + +Depending on whether Python was build as a 32-bit or a 64-bit executable +you may need to set or unset +the environment variable :envvar:`OBJECT_MODE`. +To determine the size the following command can be used:: + + $ python -c "import sys; print(sys.maxsize) <= 2**32" + True + +When the answer is ``True`` (as above) Python was build as a 32-bit +executable. + +When working with a 32-bit Python executable proceed as follows:: + + $ unset OBJECT_MODE + $ pyinstaller + +When working with a 64-bit Python executable proceed as follows:: + + $ export OBJECT_MODE=64 + $ pyinstaller + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/doc/when-things-go-wrong.rst b/3rdparty/pyinstaller-4.3/doc/when-things-go-wrong.rst new file mode 100644 index 0000000000000000000000000000000000000000..52b1b350279115ef684e6ab78e3e7679d29970b5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/doc/when-things-go-wrong.rst @@ -0,0 +1,382 @@ +.. _when things go wrong: + +When Things Go Wrong +==================== + +The information above covers most normal uses of |PyInstaller|. +However, the variations of Python and third-party libraries are +endless and unpredictable. +It may happen that when you attempt to bundle your app either +|PyInstaller| itself, or your bundled app, terminates with a Python traceback. +Then please consider the following actions in sequence, before +asking for technical help. + + +.. _recipes and examples for specific problems: + +Recipes and Examples for Specific Problems +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The |PyInstaller| `FAQ`_ page has work-arounds for some common problems. +Code examples for some advanced uses and some common +problems are available on our `PyInstaller Recipes`_ page. +Some of the recipes there include: + +* A more sophisticated way of collecting data files + than the one shown above (:ref:`Adding Files to the Bundle`). + +* Bundling a typical Django app. + +* A use of a run-time hook to set the PyQt5 API level. + +* A workaround for a multiprocessing constraint under Windows. + +and others. +Many of these Recipes were contributed by users. +Please feel free to contribute more recipes! + + +.. _finding out what went wrong: + +Finding out What Went Wrong +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Build-time Messages +-------------------- + +When the ``Analysis`` step runs, it produces error and warning messages. +These display after the command line if the ``--log-level`` option allows it. +Analysis also puts messages in a warnings file +named :file:`build/{name}/warn-{name}.txt` in the +``work-path=`` directory. + +Analysis creates a message when it detects an import +and the module it names cannot be found. +A message may also be produced when a class or function is declared in +a package (an ``__init__.py`` module), and the import specifies +``package.name``. In this case, the analysis can't tell if name is supposed to +refer to a submodule or package. + +The "module not found" messages are not classed as errors because +typically there are many of them. +For example, many standard modules +conditionally import modules for different platforms that may or may +not be present. + +All "module not found" messages are written to the +:file:`build/{name}/warn-{name}.txt` file. +They are not displayed to standard output because there are many of them. +Examine the warning file; often there will be dozens of modules not found, +but their absence has no effect. + +When you run the bundled app and it terminates with an ImportError, +that is the time to examine the warning file. +Then see :ref:`Helping PyInstaller Find Modules` below for how to proceed. + + +Build-Time Dependency Graph +---------------------------- + +On each run |PyInstaller| writes a cross-referencing file about dependencies +into the build folder: +:file:`build/{name}/xref-{name}.html` in the +``work-path=`` directory is an HTML file that lists the full +contents of the import graph, showing which modules are imported +by which ones. +You can open it in any web browser. +Find a module name, then keep clicking the "imported by" links +until you find the top-level import that causes that module to be included. + +If you specify ``--log-level=DEBUG`` to the ``pyinstaller`` command, +|PyInstaller| additionally generates a GraphViz_ input file representing the +dependency graph. +The file is :file:`build/{name}/graph-{name}.dot` in the +``work-path=`` directory. +You can process it with any GraphViz_ command, e.g. :program:`dot`, +to produce +a graphical display of the import dependencies. + +These files are very large because even the simplest "hello world" +Python program ends up including a large number of standard modules. +For this reason the graph file is not very useful in this release. + + + +Build-Time Python Errors +------------------------- + +|PyInstaller| sometimes terminates by raising a Python exception. +In most cases the reason is clear from the exception message, +for example "Your system is not supported", or "Pyinstaller +requires at least Python 3.6". +Others clearly indicate a bug that should be reported. + +One of these errors can be puzzling, however: +``IOError("Python library not found!")`` +|PyInstaller| needs to bundle the Python library, which is the +main part of the Python interpreter, linked as a dynamic load library. +The name and location of this file varies depending on the platform in use. +Some Python installations do not include a dynamic Python library +by default (a static-linked one may be present but cannot be used). +You may need to install a development package of some kind. +Or, the library may exist but is not in a folder where |PyInstaller| +is searching. + +The places where |PyInstaller| looks for the python library are +different in different operating systems, but ``/lib`` and ``/usr/lib`` +are checked in most systems. +If you cannot put the python library there, +try setting the correct path in the environment variable +``LD_LIBRARY_PATH`` in GNU/Linux or +``DYLD_LIBRARY_PATH`` in OS X. + + +Getting Debug Messages +---------------------- + +The ``--debug=all`` option (and its :ref:`choices `) provides a +signficiant amount of diagnostic information. +This can be useful during development of a complex package, +or when your app doesn't seem to be starting, +or just to learn how the runtime works. + +Normally the debug progress messages go to standard output. +If the ``--windowed`` option is used when bundling a Windows app, +they are sent to any attached debugger. If you are not using a debugger +(or don't have one), the DebugView_ the free (beer) tool can be used to +display such messages. It has to be started before running the bundled +application. + +.. _DebugView: https://docs.microsoft.com/en-us/sysinternals/downloads/debugview + +For a ``--windowed`` Mac OS app they are not displayed. + +Consider bundling without ``--debug`` for your production version. +Debugging messages require system calls and have an impact on performance. + + +.. _getting python's verbose imports: + +Getting Python's Verbose Imports +-------------------------------- + +You can build the app with the ``--debug=imports`` option +(see `Getting Debug Messages`_ above), +which will pass the ``-v`` (verbose imports) flag +to the embedded Python interpreter. +This can be extremely useful. +It can be informative even with apps that are apparently working, +to make sure that they are getting all imports from the bundle, +and not leaking out to the local installed Python. + +Python verbose and warning messages always go to standard output +and are not visible when the ``--windowed`` option is used. +Remember to not use this for your production version. + + +Figuring Out Why Your GUI Application Won't Start +--------------------------------------------------- + +If you are using the ``--windowed`` option, +your bundled application may fail to start with an error message like +``Failed to execute script my_gui``. +In this case, you will want to get more verbose output to find out +what is going on. + +* For Mac OS, you can run your application on the command line, + i.e.``./dist/my_gui`` + in `Terminal` instead of clicking on ``my_gui.app``. + +* For Windows, you will need to re-bundle your application without the + ``--windowed`` option. + Then you can run the resulting executable from the command line, + i.e.: ``my_gui.exe``. + +* For Unix and GNU/Linux there in no ``--windowed`` option. + Anyway, if a your GUI application fails, + you can run your application on the command line, + i.e. ``./dist/my_gui``. + +This should give you the relevant error that is preventing your +application from initializing, and you can then move on to other +debugging steps. + + +Operation not permitted error +----------------------------- + +If you use the --onefile and it fails to run you program with error like:: + + ./hello: error while loading shared libraries: libz.so.1: + failed to map segment from shared object: Operation not permitted + +This can be caused by wrong permissions for the /tmp directory +(e.g. the filesystem is mounted with ``noexec`` flags). + +A simple way to solve this issue is to set, +in the environment variable TMPDIR, +a path to a directory in a filesystem mounted without ``noexec`` flags, e.g.:: + + export TMPDIR=/var/tmp/ + +.. _helping pyinstaller find modules: + +Helping PyInstaller Find Modules +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Extending the Path +------------------ + +If Analysis recognizes that a module is needed, but cannot find that module, +it is often because the script is manipulating ``sys.path``. +The easiest thing to do in this case is to use the ``--paths=`` option +to list all the other places that the script might be searching for imports:: + + pyi-makespec --paths=/path/to/thisdir \ + --paths=/path/to/otherdir myscript.py + +These paths will be noted in the spec file. +They will be added to the current ``sys.path`` during analysis. + + +Listing Hidden Imports +---------------------- + +If Analysis thinks it has found all the imports, +but the app fails with an import error, +the problem is a hidden import; that is, an import that is not +visible to the analysis phase. + +Hidden imports can occur when the code is using ``__import__``, +``imp.find_module()`` +or perhaps ``exec`` or ``eval``. +Hidden imports can also occur when an extension module uses the +Python/C API to do an import. +When this occurs, Analysis can detect nothing. +There will be no warnings, only an ImportError at run-time. + +To find these hidden imports, +build the app with the ``--debug=imports`` flag +(see :ref:`Getting Python's Verbose Imports` above) +and run it. + +Once you know what modules are needed, you add the needed modules +to the bundle using the ``--hidden-import=`` command option, +or by editing the spec file, +or with a hook file (see :ref:`Understanding PyInstaller Hooks` below). + + +Extending a Package's ``__path__`` +---------------------------------- + +Python allows a script to extend the search path used for imports +through the ``__path__`` mechanism. +Normally, the ``__path__`` of an imported module has only one entry, +the directory in which the ``__init__.py`` was found. +But ``__init__.py`` is free to extend its ``__path__`` to include other directories. +For example, the ``win32com.shell.shell`` module actually resolves to +``win32com/win32comext/shell/shell.pyd``. +This is because ``win32com/__init__.py`` appends ``../win32comext`` to its ``__path__``. + +Because the ``__init__.py`` of an imported module +is not actually executed during analysis, +changes it makes to ``__path__`` are not seen by |PyInstaller|. +We fix the problem with the same hook mechanism we use for hidden imports, +with some additional logic; see :ref:`Understanding PyInstaller Hooks` below. + +Note that manipulations of ``__path__`` hooked in this way apply only +to the Analysis. +At runtime all imports are intercepted and satisfied from within the +bundle. ``win32com.shell`` is resolved the same +way as ``win32com.anythingelse``, and ``win32com.__path__`` +knows nothing of ``../win32comext``. + +Once in a while, that's not enough. + + +.. _changing runtime behavior: + +Changing Runtime Behavior +------------------------- + +More bizarre situations can be accomodated with runtime hooks. +These are small scripts that manipulate the environment before your main script runs, +effectively providing additional top-level code to your script. + +There are two ways of providing runtime hooks. +You can name them with the option ``--runtime-hook=``\ *path-to-script*. + +Second, some runtime hooks are provided. +At the end of an analysis, +the names in the module list produced by the Analysis phase are looked up in +:file:`loader/rthooks.dat` in the |PyInstaller| install folder. +This text file is the string representation of a +Python dictionary. The key is the module name, and the value is a list +of hook-script pathnames. +If there is a match, those scripts are included in the bundled app +and will be called before your main script starts. + +Hooks you name with the option are executed +in the order given, and before any installed runtime hooks. +If you specify ``--runtime-hook=file1.py --runtime-hook=file2.py`` +then the execution order at runtime will be: + +1. Code of :file:`file1.py`. +2. Code of :file:`file2.py`. +3. Any hook specified for an included module that is found + in :file:`rthooks/rthooks.dat`. +4. Your main script. + +Hooks called in this way, while they need to be careful of what they import, +are free to do almost anything. +One reason to write a run-time hook is to +override some functions or variables from some modules. +A good example of this is the Django runtime +hook (see ``loader/rthooks/pyi_rth_django.py`` in the +|PyInstaller| folder). +Django imports some modules dynamically and it is looking +for some ``.py`` files. +However ``.py`` files are not available in the one-file bundle. +We need to override the function +``django.core.management.find_commands`` +in a way that will just return a list of values. +The runtime hook does this as follows:: + + import django.core.management + def _find_commands(_): + return """cleanup shell runfcgi runserver""".split() + django.core.management.find_commands = _find_commands + + + +Getting the Latest Version +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you have some reason to think you have found a bug in |PyInstaller| +you can try downloading the latest development version. +This version might have fixes or features that are not yet at `PyPI`_. +You can download the latest stable version and the latest development +version from the `PyInstaller Downloads`_ page. + +You can also install the latest version of |PyInstaller| directly +using pip_:: + + pip install https://github.com/pyinstaller/pyinstaller/archive/develop.zip + +Asking for Help +~~~~~~~~~~~~~~~~~~ + +When none of the above suggestions help, +do ask for assistance on the `PyInstaller Email List`_. + +Then, if you think it likely that you see a bug in |PyInstaller|, +refer to the `How to Report Bugs`_ page. + + +.. include:: _common_definitions.txt + +.. Emacs config: + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/pyinstaller.py b/3rdparty/pyinstaller-4.3/pyinstaller.py new file mode 100644 index 0000000000000000000000000000000000000000..94c841506dc4f5226554ca273ce2beb5c29add0e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/pyinstaller.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 utility is primary meant to be used when PyInstaller is not +# installed, eg. when be run by a git checkout. + +from PyInstaller.__main__ import run +run() diff --git a/3rdparty/pyinstaller-4.3/pyproject.toml b/3rdparty/pyinstaller-4.3/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..2c362504599fd3832c34ecd9710e04c2e9e7667c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/pyproject.toml @@ -0,0 +1,76 @@ +[tool.towncrier] + filename = "doc/CHANGES.rst" + directory = "news" + template = "news/_template.rst" + underlines = "-~^" + wrap = true + package = "PyInstaller" + title_format = "{version} ({project_date})" + + [[tool.towncrier.section]] + path = "" + + [[tool.towncrier.type]] + directory = "feature" + name = "Features" + showcontent = true + + [[tool.towncrier.type]] + directory = "bugfix" + name = "Bugfix" + showcontent = true + + [[tool.towncrier.type]] + directory = "breaking" + name = "Incompatible Changes" + showcontent = true + + [[tool.towncrier.type]] + directory = "hooks" + name = "Hooks" + showcontent = true + + [[tool.towncrier.type]] + directory = "bootloader" + name = "Bootloader" + showcontent = true + + [[tool.towncrier.type]] + directory = "moduleloader" + name = "Module Loader" + showcontent = true + + [[tool.towncrier.type]] + directory = "doc" + name = "Documentation" + showcontent = true + + [[tool.towncrier.type]] + directory = "process" + name = "Project & Process" + showcontent = true + + [[tool.towncrier.type]] + directory = "core" + name = "PyInstaller Core" + showcontent = true + + [[tool.towncrier.type]] + directory = "tests" + name = "Test-suite and Continuous Integration" + showcontent = true + + [[tool.towncrier.type]] + directory = "build" + name = "Bootloader build" + showcontent = true + +[build-system] +# Tells pip to install wheel before trying to install PyInstaller +# from an sdist or from Github. +# Installing without wheel uses legacy `python setup.py install` +# which has issues with unicode paths. +requires = [ + "wheel", + "setuptools", +] diff --git a/3rdparty/pyinstaller-4.3/requirements.txt b/3rdparty/pyinstaller-4.3/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..c6a88f6b5b4a3d10724e8e9076d40ac98ed36337 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/requirements.txt @@ -0,0 +1,16 @@ +# This is the pip requirements file for running PyInstaller. +# If you want to run the test you will also need to install what is +# defined in tests/requirements-tools.txt and for extensive testing +# also tests/requirements-libraries.txt. + +## IMPORTANT: Keep aligned with setup.cfg + +setuptools<50.0.0 # 50.0.0 breaks a modulegraph regession test +altgraph +pyinstaller-hooks-contrib >= 2020.11 +pefile; sys_platform == 'win32' +pywin32-ctypes; sys_platform == 'win32' +macholib; sys_platform == 'darwin' +importlib-metadata ; python_version < '3.8' + +## IMPORTANT: Keep aligned with setup.cfg diff --git a/3rdparty/pyinstaller-4.3/setup.cfg b/3rdparty/pyinstaller-4.3/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..e48ff593891d0b42d162ff03be49cf0fbbb5c780 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/setup.cfg @@ -0,0 +1,132 @@ +[metadata] +name = pyinstaller +version = attr: PyInstaller.__version__ +url = http://www.pyinstaller.org/ +description = PyInstaller bundles a Python application and all its dependencies into a single package. +long_description = file: README.rst +long_description_content_type = text/x-rst +author = Hartmut Goebel, Giovanni Bajo, David Vierra, David Cortesi, Martin Zibricky +keywords = + packaging, app, apps, bundle, convert, standalone, executable + pyinstaller, cxfreeze, freeze, py2exe, py2app, bbfreeze +license = GPLv2-or-later with a special exception which allows to use PyInstaller to build and distribute non-free programs (including commercial ones) +license_file = COPYING.txt +classifiers = + Development Status :: 6 - Mature + Environment :: Console + Intended Audience :: Developers + Intended Audience :: Other Audience + Intended Audience :: System Administrators + License :: OSI Approved :: GNU General Public License v2 (GPLv2) + Natural Language :: English + Operating System :: MacOS :: MacOS X + Operating System :: Microsoft :: Windows + Operating System :: POSIX + Operating System :: POSIX :: AIX + Operating System :: POSIX :: BSD + Operating System :: POSIX :: Linux + Operating System :: POSIX :: SunOS/Solaris + Programming Language :: C + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation :: CPython + Topic :: Software Development + Topic :: Software Development :: Build Tools + Topic :: Software Development :: Interpreters + Topic :: Software Development :: Libraries :: Python Modules + Topic :: System :: Installation/Setup + Topic :: System :: Software Distribution + Topic :: Utilities + +[options] +zip_safe = False +packages = PyInstaller +include_package_data = True +python_requires = >=3.6 +install_requires = + setuptools + altgraph + pefile >= 2017.8.1 ; sys_platform == 'win32' + pywin32-ctypes >= 0.2.0 ; sys_platform == 'win32' + macholib >= 1.8 ; sys_platform == 'darwin' + pyinstaller-hooks-contrib >= 2020.6 + importlib-metadata ; python_version < '3.8' + +[options.extras_require] +hook_testing = + pytest >= 2.7.3 + execnet >= 1.5.0 + psutil +encryption = + tinyaes>=1.0.0 + +[options.package_data] +pyinstaller = bootloader/*/* +pyinstaller.hooks = rthooks.dat +pyinstaller.utils = pytest.ini + +[options.entry_points] +console_scripts = + pyinstaller = PyInstaller.__main__:run + pyi-archive_viewer = PyInstaller.utils.cliutils.archive_viewer:run + pyi-bindepend = PyInstaller.utils.cliutils.bindepend:run + pyi-grab_version = PyInstaller.utils.cliutils.grab_version:run + pyi-makespec = PyInstaller.utils.cliutils.makespec:run + pyi-set_version = PyInstaller.utils.cliutils.set_version:run + +[sdist] +formats = gztar + +[zest.releaser] +python-file-with-version = PyInstaller/__init__.py +releaser.before_upload = PyInstaller.utils.release.sign_source_distribution +push-changes = no +tag-format = v{version} +tag-message = PyInstaller {version} +tag-signing = yes + +[catchlog] +log_level = DEBUG + +[tool:pytest] +timeout = 300 +python_files = "tests/functional/test_*.py" "tests/unit/test_*.py" +norecursedirs = + tests/functional/data + tests/functional/logs + tests/functional/modules + tests/functional/scripts + tests/functional/specs + tests/scripts + tests/unit/Tree_files + tests/unit/hookutils_files + tests/unit/test_modulegraph/testdata + tests/unit/test_modulegraph/testpkg-* +filterwarnings = + ignore:Please use assertEqual instead.:DeprecationWarning + ignore:Use zipio.listdir instead of os_listdir:DeprecationWarning: +addopts = "-v" "-rsxXfE" "--doctest-glob=" +markers = + darwin: only run on macOS + linux: only runs on GNU/Linux + win32: only runs on Windows + +[flake8] +exclude = + .git, + doc/_build, + build, + dist, + bootloader +show-source = True +extend-ignore = E265 + +[egg_info] +tag_build = +tag_date = 0 + diff --git a/3rdparty/pyinstaller-4.3/setup.py b/3rdparty/pyinstaller-4.3/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..37f6fb7c3922e93e6e6334aff23c06d5588afc1c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/setup.py @@ -0,0 +1,80 @@ +#! /usr/bin/env python +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import os +from setuptools import setup + +# Hack required to allow compat to not fail when pypiwin32 isn't found +os.environ["PYINSTALLER_NO_PYWIN32_FAILURE"] = "1" + + +#-- plug-in building the bootloader + +from distutils.core import Command +from distutils.command.build import build + + +class build_bootloader(Command): + """ + Wrapper for distutil command `build`. + """ + + user_options =[] + def initialize_options(self): pass + def finalize_options(self): pass + + def bootloader_exists(self): + # Checks is the console, non-debug bootloader exists + from PyInstaller import HOMEPATH, PLATFORM + from PyInstaller.compat import is_win, is_cygwin + exe = 'run' + if is_win or is_cygwin: + exe = 'run.exe' + exe = os.path.join(HOMEPATH, 'PyInstaller', 'bootloader', PLATFORM, exe) + return os.path.isfile(exe) + + def compile_bootloader(self): + import subprocess + from PyInstaller import HOMEPATH + + src_dir = os.path.join(HOMEPATH, 'bootloader') + cmd = [sys.executable, './waf', 'configure', 'all'] + rc = subprocess.call(cmd, cwd=src_dir) + if rc: + raise SystemExit('ERROR: Failed compiling the bootloader. ' + 'Please compile manually and rerun setup.py') + + def run(self): + if getattr(self, 'dry_run', False): + return + if self.bootloader_exists(): + return + print('No precompiled bootloader found. Trying to compile it for you ...', + file=sys.stderr) + self.compile_bootloader() + + +class MyBuild(build): + # plug `build_bootloader` into the `build` command + def run(self): + self.run_command('build_bootloader') + build.run(self) + +#-- + +setup( + setup_requires = ["setuptools >= 39.2.0"], + cmdclass = {'build_bootloader': build_bootloader, + 'build': MyBuild, + }, +) diff --git a/3rdparty/pyinstaller-4.3/tests/README.md b/3rdparty/pyinstaller-4.3/tests/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b3ad1ecbcc5e89513a6e947f65d0f5eee3ddc944 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/README.md @@ -0,0 +1,129 @@ +Tests for PyInstaller +===================== + +This directory contains tests for PyInstaller: + +- `functional` directory contains tests where executables are created from + Python scripts. +- `unit` directory contains simple unit tests. +- `old_suite` directory contains old structure of tests (TODO migrate all tests + to a new structure). + +Prerequisites +------------- + +In order to run the tests, you will need the following Python packages/libraries +installed: + +- pytest +- psutil +- execnet + +The easiest way to install these (and some useful pytest add-ons) is running +``` +pip install -U -r tests/requirements-tools.txt +``` + +Running the Tests +----------------- + +To run the tests, navigate to the root directory of the PyInstaller project and +run the following command: + + py.test + +Or, to speed up test runs by sending tests to multiple CPUs: + + py.test -n NUM + +Or, to run only the unit or functional tests, run one the following command: + + py.test tests/unit + py.test tests/functional + py.test tests/functional -k "not tests/functional/test_libraries.py" + +Or, to run only the unit and functional tests, but not the huge library +test-suite: + + py.test tests/unit tests/functional -k "not tests/functional/test_libraries.py" + + +Or, to run only a particular test suite within a file, run the following +command: + + py.test tests/functional/test_basic.py -k test_pyz_as_external_file + +Run all tests matching `test_ctypes_CDLL` resp. `ctypes_CDLL`: + + py.test -k test_ctypes_CDLL + py.test -k ctypes_CDLL + +Run both the onefile and ondir tests for +`test_ctypes_CDLL_find_library__nss_files`: + + py.test -k test_ctypes_CDLL_find_library__nss_files + +Finally, to only run a particular test, run one of the following commands: + + py.test -k test_ctypes_CDLL_find_library__nss_files[onedir] + py.test -k test_ctypes_CDLL_find_library__nss_files[onefile] + +## Continuous Integration (CI) + +Continuous integration (CI) automatically exercises all tests for all platforms +officially supported by PyInstaller. + +### Python Packages + +Regardless of platform or CI service, all Python packages to be tested should +be listed in `test/requirements-library.txt`. Python packages required for +exercising tests (e.g., `pytest`) should instead be listed in +`test/requirements-tools.txt`. + +Both files are usual pip [requirements +files](https://pip.pypa.io/en/stable/reference/pip_install/#requirements-file-format) +following the respective syntax (e.g., +[`{package_name}>={minimum_version}`](https://pip.pypa.io/en/stable/reference/pip_install/#requirement-specifiers). +These packages will be installed with `pip` into remote testing environments +managed by third-party CI services. + +Packages only available for specific version or platforms should get an appropriate +[environment +marker](https://www.python.org/dev/peps/pep-0426/#environment-markers) like +so: +``` +SomeProject ==5.4 ; python_version != '3.6' +SomeProject ; sys_platform == 'win32' +``` + +### GNU/Linux + +The top-level `.travis.yml` file configures the Travis-CI service to remotely +test PyInstaller in an Ubuntu 12.04 (LTS) container, the most recent GNU/Linux +distribution supported by Travis-CI. + +Non-Python dependencies installable through `apt-get` on Ubuntu 12.04 should be +listed as `- `-prefixed items in the `addons:` → `apt:` → `packages:` subsection +of `.travis.yml`. Since Ubuntu 12.04 provides _no_ Python 3 packages prefixed by +`python3-`, only Python 2.7 packages prefixed by `python-` are installable by +`apt-get`. Since installing only Python 2.7 packages would be useless, Python +packages should _always_ be installed by `pip` rather than `apt-get`. See +**"Python Packages"** above. + +### OS X + +The top-level `.travis.yml` file of a +[separate repository](https://github.com/pyinstaller/pyinstaller-osx-tests) +configures the Travis-CI service to remotely test PyInstaller in an OS X 10.9.5 +virtual machine, the most recent OS X version supported by Travis-CI. + +### Windows + +The top-level `appveyor.yml` file configures the Appveyor service to remotely +test PyInstaller in a Windows virtual machine. + +Non-Python dependencies installable through either Chocolatey (`cinst`), +PowerShell (`ps`), or WebPI (`WebpiCmd`) should be listed as `- `-prefixed items +in the `install:` section of `appveyor.yml`. See the +[official documentation](http://www.appveyor.com/docs/build-configuration#installing-additional-software) +for voluminous details. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/README b/3rdparty/pyinstaller-4.3/tests/functional/README new file mode 100644 index 0000000000000000000000000000000000000000..2c59a1080c96b7460632de99f24d3a1aba72cbc8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/README @@ -0,0 +1,6 @@ +Directories + +scripts Python scripts that are used in tests. +modules Python modules used in some tests. +data Directory with data for some tests. +logs Directory with .toc log files. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/conftest.py b/3rdparty/pyinstaller-4.3/tests/functional/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..7ab389c09c85e7779f9fbc46916aa297e5cffc15 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/conftest.py @@ -0,0 +1,2 @@ +# Bring all fixtures into this file. +from PyInstaller.utils.conftest import * diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/PIL_images/tinysample.tiff b/3rdparty/pyinstaller-4.3/tests/functional/data/PIL_images/tinysample.tiff new file mode 100644 index 0000000000000000000000000000000000000000..a7bd93799d0a0d664717ca53cc3fe61075752481 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/data/PIL_images/tinysample.tiff differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/PyQt5_QWebEngine/test_web_page.html b/3rdparty/pyinstaller-4.3/tests/functional/data/PyQt5_QWebEngine/test_web_page.html new file mode 100644 index 0000000000000000000000000000000000000000..874c2fa78b507d83048115059bf24c6b83360418 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/PyQt5_QWebEngine/test_web_page.html @@ -0,0 +1,10 @@ + + + + + Test web page + + +

This is a test web page.

+ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/PyQt5_uic/PyQt5-uic.ui b/3rdparty/pyinstaller-4.3/tests/functional/data/PyQt5_uic/PyQt5-uic.ui new file mode 100644 index 0000000000000000000000000000000000000000..ab682147b5c69767c2c6134e15aaba096ebd30a1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/PyQt5_uic/PyQt5-uic.ui @@ -0,0 +1,39 @@ + + + Form + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + 60 + 40 + 300 + 200 + + + + QQuickWidget::SizeRootObjectToView + + + + + + QQuickWidget + QWidget +
QtQuickWidgets/QQuickWidget
+
+
+ + +
diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/PySide2_QWebEngine/test_web_page.html b/3rdparty/pyinstaller-4.3/tests/functional/data/PySide2_QWebEngine/test_web_page.html new file mode 100644 index 0000000000000000000000000000000000000000..67a3e24e72dcb3862055a3b951bab32bd2120411 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/PySide2_QWebEngine/test_web_page.html @@ -0,0 +1,11 @@ + + + + + Test web page + + +

This is a test web page with internationalised characters.

+

HЯ⾀ÄÉÖÜ

+ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/app_with_plugin/static_plugin.py b/3rdparty/pyinstaller-4.3/tests/functional/data/app_with_plugin/static_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..9db1a8f4bff3ab410319f4d7ab181758b2c23838 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/app_with_plugin/static_plugin.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 is a static plugin module that goes +# with the test_app_with_plugins sample. + + +print('Static plugin imported.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/AppIcon.svg b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/AppIcon.svg new file mode 100644 index 0000000000000000000000000000000000000000..c16e21d389d4d5e9cb54620a4c76f7cd40704c31 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/AppIcon.svg @@ -0,0 +1,4423 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/DirIcon.png b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/DirIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..778589e03ad9d984f2f974db3c7e46317a307eb1 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/DirIcon.png differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/create.sh b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/create.sh new file mode 100644 index 0000000000000000000000000000000000000000..75990103b50f1b0184972cbaed3b748c88e3b719 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/create.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +set -e + +main() { + local tools_dir="$1" + local tmp_dir="$2" + local app_name="$3" + local app_id="org.pyinstaller.appimage.test" + local app_dir="${tmp_dir}/dist/AppRun" + + echo ">>> Adjusting file names to fit in the AppImage" + [ -d "${app_dir}" ] && rm -rf "${app_dir}" + mv -v "${tmp_dir}/dist/${app_name}" "${app_dir}" + mv -v "${app_dir}/${app_name}" "${app_dir}/AppRun" + + echo ">>> Copying icons" + cp -v "${tools_dir}/DirIcon.png" "${app_dir}/.DirIcon" + cp -v "${tools_dir}/AppIcon.svg" "${app_dir}/${app_name}.svg" + + echo ">>> Copying metadata files" + mkdir -pv "${app_dir}/usr/share/metainfo" + cp -v "${tools_dir}/${app_id}.appdata.xml" "${app_dir}/usr/share/metainfo" + mkdir -pv "${app_dir}/usr/share/applications" + cp -v "${tools_dir}/${app_id}.desktop" "${app_dir}/usr/share/applications" + ln -srv "${app_dir}/usr/share/applications/${app_id}.desktop" "${app_dir}/${app_id}.desktop" + + return 0 # <-- Needed, do not remove! +} + +main "$@" diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/org.pyinstaller.appimage.test.appdata.xml b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/org.pyinstaller.appimage.test.appdata.xml new file mode 100644 index 0000000000000000000000000000000000000000..c98c9c7b0fc811e0a18c267f496c73088cce820e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/org.pyinstaller.appimage.test.appdata.xml @@ -0,0 +1,19 @@ + + + org.pyinstaller.appimage.test + MIT + LGPL-2.1-only + PyInstaller Appimage Test + Some summary + +

Simple AppImage test for code frozen with PyInstaller.

+
+ org.pyinstaller.appimage.test + https://github.com/pyinstaller/pyinstaller + https://github.com/pyinstaller/pyinstaller/issues + PyInstaller + contact -- example dot org + + apptest + +
diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/org.pyinstaller.appimage.test.desktop b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/org.pyinstaller.appimage.test.desktop new file mode 100644 index 0000000000000000000000000000000000000000..c27cda29c15cbe8acce03bf354727a847403982a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/appimage/org.pyinstaller.appimage.test.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=PyInstaller Appimage Test +GenericName=PyInstaller Appimage Test +Comment=Simple AppImage test for code frozen with PyInstaller. +Exec=false +Icon=apptest +Type=Application +StartupNotify=false +Terminal=false +Categories=Office; diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/ctypes_dylib/ctypes_dylib.c b/3rdparty/pyinstaller-4.3/tests/functional/data/ctypes_dylib/ctypes_dylib.c new file mode 100644 index 0000000000000000000000000000000000000000..084bf8d35444a4e565f8ad19136317624af6ceb7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/ctypes_dylib/ctypes_dylib.c @@ -0,0 +1,30 @@ +/* + * **************************************************************************** + * Copyright (c) 2005-2021, 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) + * **************************************************************************** + */ + +#ifdef _WIN32 + +// Windows code +int __declspec(dllexport) dummy(int arg) +{ + return arg + 12; +} + +#else + +// Unix code +int dummy(int arg) +{ + return arg + 12; +} + +#endif diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/django/db.sqlite3 b/3rdparty/pyinstaller-4.3/tests/functional/data/django/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..949885df18cd121af9c14b65786c31b3ddaa5c76 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/data/django/db.sqlite3 differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/settings.py b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..6a732cbe9fc47ea430b8efebd754c04653c80629 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/settings.py @@ -0,0 +1,102 @@ +""" +Django settings for django_site project. + +Generated by 'django-admin startproject' using Django 1.8.3. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.8/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'm)2$jf=m1%hxqo0ne71@x17&xfky7^%xtw#=$ht%y4#=p9#8_t' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', +) + +ROOT_URLCONF = 'django_site.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'django_site.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Internationalization +# https://docs.djangoproject.com/en/1.8/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.8/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/urls.py b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..9782e29957c4311a8211e72692bcf4b3a06a67ae --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/urls.py @@ -0,0 +1,21 @@ +"""django_site URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/1.8/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') +Including another URLconf + 1. Add an import: from blog import urls as blog_urls + 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) +""" +from django.conf.urls import include, url +from django.contrib import admin + +urlpatterns = [ + url(r'^admin/', admin.site.urls), +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/wsgi.py b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..a2e4f9c7920350ce0c61702880748d8f528dbae4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/django/django_site/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for django_site project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_site.settings") + +application = get_wsgi_application() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/django/manage.py b/3rdparty/pyinstaller-4.3/tests/functional/data/django/manage.py new file mode 100644 index 0000000000000000000000000000000000000000..d7adb99c9a9cc0b3c65909255b5bbeace939eb2f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/django/manage.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +import os +import sys + +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_site.settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/requests/openssl.conf b/3rdparty/pyinstaller-4.3/tests/functional/data/requests/openssl.conf new file mode 100644 index 0000000000000000000000000000000000000000..f84e62570e92ae2fd5528f2e85148dbebf152e0d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/requests/openssl.conf @@ -0,0 +1,10 @@ +[ req ] +distinguished_name = req_distinguished_name +x509_extensions = v3_ca +[ alternate_names ] +DNS.1 = localhost +[ req_distinguished_name ] + commonName = Common Name + commonName_default = localhost +[ v3_ca ] + subjectAltName = @alternate_names diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/requests/server.pem b/3rdparty/pyinstaller-4.3/tests/functional/data/requests/server.pem new file mode 100644 index 0000000000000000000000000000000000000000..82e12f2d21c160edd0dbb85efed338fa02336df9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/requests/server.pem @@ -0,0 +1,99 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDWa9w08ag/g2+N +MoR5MzWWTbI7kdWifFZjumGYcUVzkn928IX1iYnPgBLmS/VxZMDO8NtkhCQWr0oh +JaKuvmZJQAse3AF4iBRfg7nejWb/ck9VFNnm4c3BljbsaLqkBJj3G7GJG/seGcaI +W52ws/scGKa2cBN9wN40YPeV27f4KMu7XyGHf2hwWUap2akhrKR5cYi+Mh9oc+ES +aOMA0H5GWyBFQjXtbE9U4qVosbfc6HxqOS09fmpoyTeUDA4yLL0B7myQIMII0+Ap +kHeNNr4RcEke08jQ/eoybuQ38hyRvbBBxEQB9WVZDZIXxNJv1vr4FClHZSLV2pYl +ozz8L0ejAgMBAAECggEAZ6VloVX6zRC8mFUGAgwF6CyQbgkVamCN5dEPIgAG4VG8 +OYMUTdb4/YtcF2Q6NWDNbnqwokrZovmCbLljhPJWQSwq8/TG5TtqFa136CMT2YCo +5miY1+joa54v2GDbbzMlubTyQWN8JFWzSPB4LhUh2bf0xhUw5sWW41zH8PFvYQ18 +tW7NEoKtd9GN7zj6bK0vQxkgaCXqFGaTdQiL45McA2ogymNSKLrtkn1T9KwpkX1T +WBxVBaXUSas2Qi7ZvdRtrqtpiSNPAtD7TJbeDcIBOBA04FzGJGfrtnmRmqOkPkq/ +JIu/KZEQLWSuWBLzZDcAChJTTqzzkWWVQR+6PyjMsQKBgQDvtGfLFnT4XBburbEZ +arZzoPFOwYbPAvGubrWg03MVVzLE+rSIOSrN5SKMLn6lab0s64m5ceTUakbRtVHD +0ZIGM1IXAg8enKUbrTIiNAap6yYFHxDRPVELCmK0A08VsigTfkJlhJCUBFIXHAkd +33quVuvcTby6O2r3cHZLyEM+iQKBgQDk/3JX8S8oiJHY4TeFtH32E/M7bFa8XXRd +OtUN3ANI/l10oylQvTmj6kCE6mPUzWECsm5XlaCyoSMAZqiLjcB7GGXqJzbh/kWv +Ol59ZKFPjYvmh2WgcgRjfy714gWqTipwErPedooMmhXN+EGINKMm7DYdwe/TuynA +/39LFuLpywKBgA/WUY0/fJb0LqeyXiMZw9g75/WWH7wJq8Ikmmd30QC19CrDja36 +aPOVkgTFBaUUKWWQYlcYSVAnfWpZmn9fl1BFj8TpY+pX2yjK9qb/3PjZCngIJ2pL +rNEX7JGMLmt2i+NN0kE20v6Ukn/oYEnS/B4MrCSwFioPxu92RLqZDqhRAoGBAK7e +mvSpCvgLAkT6ByolIKNPrMhN/RYuz3N6P8QrpJ6TD87H4f7z4RZZBhf53W5dv50N +oNFlQ6flAROHUWHwR3I4uWrLs090msYq7okW1VAoqRLLhkG1j8BjGPtPNEBPHH0k +6xIQt27UI557258lgwlwDAtBU+D95e6prQ0sVu9tAoGAKQLW27WAjpRlhfpfrFFR +IPBcwhRkpZ3ACa2HbNnBzjGW6LTmMUIgl6SpmbWyci/WGCuf1I54lEYzSEIf1PTy +wtIHGToS0Pv4xyogWLe4reo2LferXy9/pfbVFlmkgsboFT3tXEorDI/ij+SizMlW +1iXgN4XoagAzWnJ7v0U7MnA= +-----END PRIVATE KEY----- +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 3e:92:1c:fc:f2:9c:66:10:b0:a7:b2:47:84:4c:5e:d4:73:49:9f:82 + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN = localhost + Validity + Not Before: Mar 6 09:08:35 2019 GMT + Not After : Feb 10 09:08:35 2119 GMT + Subject: CN = localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (2048 bit) + Modulus: + 00:d6:6b:dc:34:f1:a8:3f:83:6f:8d:32:84:79:33: + 35:96:4d:b2:3b:91:d5:a2:7c:56:63:ba:61:98:71: + 45:73:92:7f:76:f0:85:f5:89:89:cf:80:12:e6:4b: + f5:71:64:c0:ce:f0:db:64:84:24:16:af:4a:21:25: + a2:ae:be:66:49:40:0b:1e:dc:01:78:88:14:5f:83: + b9:de:8d:66:ff:72:4f:55:14:d9:e6:e1:cd:c1:96: + 36:ec:68:ba:a4:04:98:f7:1b:b1:89:1b:fb:1e:19: + c6:88:5b:9d:b0:b3:fb:1c:18:a6:b6:70:13:7d:c0: + de:34:60:f7:95:db:b7:f8:28:cb:bb:5f:21:87:7f: + 68:70:59:46:a9:d9:a9:21:ac:a4:79:71:88:be:32: + 1f:68:73:e1:12:68:e3:00:d0:7e:46:5b:20:45:42: + 35:ed:6c:4f:54:e2:a5:68:b1:b7:dc:e8:7c:6a:39: + 2d:3d:7e:6a:68:c9:37:94:0c:0e:32:2c:bd:01:ee: + 6c:90:20:c2:08:d3:e0:29:90:77:8d:36:be:11:70: + 49:1e:d3:c8:d0:fd:ea:32:6e:e4:37:f2:1c:91:bd: + b0:41:c4:44:01:f5:65:59:0d:92:17:c4:d2:6f:d6: + fa:f8:14:29:47:65:22:d5:da:96:25:a3:3c:fc:2f: + 47:a3 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:localhost + Signature Algorithm: sha256WithRSAEncryption + 3b:01:28:88:4b:2d:28:99:78:7f:44:a5:76:5c:15:8a:77:bb: + 61:4d:e0:37:c9:fe:6f:1e:65:c1:8d:7c:0a:74:b6:89:7a:79: + f2:c3:cd:51:53:43:e1:9a:c3:f5:46:da:84:34:64:60:af:25: + 20:60:46:02:27:e3:d3:ba:45:95:18:4b:d9:c7:62:b7:eb:11: + 1d:c9:bb:30:9a:8f:88:41:95:02:e5:04:dc:24:15:c8:43:61: + af:75:d5:e6:0d:51:53:1d:f2:bc:b1:68:59:19:30:93:4b:66: + 01:49:13:3b:fb:a7:3c:de:6a:b1:e1:c4:60:c9:6d:6d:bf:0c: + 59:99:ac:ba:47:0b:0b:1c:05:bc:a9:f2:7c:62:5f:18:9d:85: + 48:e1:f1:1b:a6:3e:a1:1d:49:43:07:0c:10:dc:b3:c9:ad:5c: + ea:f3:ba:dd:65:41:8e:2f:18:a7:8e:67:5c:17:27:7c:68:6d: + 83:cf:6d:bd:44:3f:ff:ea:00:0f:18:8c:df:3f:ab:74:73:dd: + 06:51:04:19:07:7d:d4:d7:64:0e:4d:46:3c:a4:6a:5e:68:fb: + 17:41:4f:b6:61:4d:07:61:1c:31:a8:e2:a0:0f:49:98:87:8b: + 68:31:25:9b:3a:05:15:45:0a:85:c8:6d:68:fa:38:12:b9:49: + 08:27:28:4f +-----BEGIN CERTIFICATE----- +MIIC0DCCAbigAwIBAgIUPpIc/PKcZhCwp7JHhExe1HNJn4IwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE5MDMwNjA5MDgzNVoYDzIxMTkw +MjEwMDkwODM1WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDWa9w08ag/g2+NMoR5MzWWTbI7kdWifFZjumGYcUVz +kn928IX1iYnPgBLmS/VxZMDO8NtkhCQWr0ohJaKuvmZJQAse3AF4iBRfg7nejWb/ +ck9VFNnm4c3BljbsaLqkBJj3G7GJG/seGcaIW52ws/scGKa2cBN9wN40YPeV27f4 +KMu7XyGHf2hwWUap2akhrKR5cYi+Mh9oc+ESaOMA0H5GWyBFQjXtbE9U4qVosbfc +6HxqOS09fmpoyTeUDA4yLL0B7myQIMII0+ApkHeNNr4RcEke08jQ/eoybuQ38hyR +vbBBxEQB9WVZDZIXxNJv1vr4FClHZSLV2pYlozz8L0ejAgMBAAGjGDAWMBQGA1Ud +EQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAOwEoiEstKJl4f0Sl +dlwVine7YU3gN8n+bx5lwY18CnS2iXp58sPNUVND4ZrD9UbahDRkYK8lIGBGAifj +07pFlRhL2cdit+sRHcm7MJqPiEGVAuUE3CQVyENhr3XV5g1RUx3yvLFoWRkwk0tm +AUkTO/unPN5qseHEYMltbb8MWZmsukcLCxwFvKnyfGJfGJ2FSOHxG6Y+oR1JQwcM +ENyzya1c6vO63WVBji8Yp45nXBcnfGhtg89tvUQ//+oADxiM3z+rdHPdBlEEGQd9 +1NdkDk1GPKRqXmj7F0FPtmFNB2EcMajioA9JmIeLaDElmzoFFUUKhchtaPo4ErlJ +CCcoTw== +-----END CERTIFICATE----- diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/set_icon/pyi_icon.icns b/3rdparty/pyinstaller-4.3/tests/functional/data/set_icon/pyi_icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..d263d8ab49ae2b35c2cff83c967af6d7497a4930 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/data/set_icon/pyi_icon.icns differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/set_icon/pyi_icon.ico b/3rdparty/pyinstaller-4.3/tests/functional/data/set_icon/pyi_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3bd6cee4bd6ab98e8b9cd18d4285f2285a0241b9 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/data/set_icon/pyi_icon.ico differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/sphinx/conf.py b/3rdparty/pyinstaller-4.3/tests/functional/data/sphinx/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..c0aa117b3f1ebd81391838c2e2bf1e31e16e20d1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/sphinx/conf.py @@ -0,0 +1,289 @@ +# -*- coding: utf-8 -*- +# +# Pyinstaller Sphinx hook test documentation build configuration file, created by +# sphinx-quickstart on Tue Jun 30 07:56:01 2015. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os +import shlex + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Pyinstaller Sphinx hook test' +copyright = u'2012-2015, Bryan A. Jones' +author = u'Bryan A. Jones' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.0' +# The full version, including alpha/beta/rc tags. +release = '0.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = 'fr' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +try: + # try to use optional theme + import alabaster + html_theme = 'alabaster' +except ImportError: + html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'PyinstallerSphinxhooktestdoc' + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'PyinstallerSphinxhooktest.tex', u'Pyinstaller Sphinx hook test Documentation', + u'Bryan A. Jones', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'pyinstallersphinxhooktest', u'Pyinstaller Sphinx hook test Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'PyinstallerSphinxhooktest', u'Pyinstaller Sphinx hook test Documentation', + author, 'PyinstallerSphinxhooktest', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/3rdparty/pyinstaller-4.3/tests/functional/data/sphinx/index.rst b/3rdparty/pyinstaller-4.3/tests/functional/data/sphinx/index.rst new file mode 100644 index 0000000000000000000000000000000000000000..f64616ad6fb782e5ee5aa87b6dfaf11b18450d52 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/data/sphinx/index.rst @@ -0,0 +1,22 @@ +.. Pyinstaller Sphinx hook test documentation master file, created by + sphinx-quickstart on Tue Jun 30 07:56:01 2015. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Pyinstaller Sphinx hook test's documentation! +======================================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage1_B.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage1_B.toc new file mode 100644 index 0000000000000000000000000000000000000000..88bbba10c3e94928d8a69628c415c7502daef70b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage1_B.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'multipackage1_B', + '.*ssl.*', + '(?i).*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage2_B.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage2_B.toc new file mode 100644 index 0000000000000000000000000000000000000000..ad3182c19543f297d4288c61bc7ffab434ef2eb4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage2_B.toc @@ -0,0 +1,4 @@ +[ + 'getopt', + 'multipackage2_B', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage3_B.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage3_B.toc new file mode 100644 index 0000000000000000000000000000000000000000..2c15ceafb9c5f14257d94c4fef87a82b05ca3e0b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage3_B.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'multipackage3_B', + '.*ssl.*', + '(?i).*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage4_B.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage4_B.toc new file mode 100644 index 0000000000000000000000000000000000000000..b6a3ef65f7e356a791935919138aa26349a5bab1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage4_B.toc @@ -0,0 +1,4 @@ +[ + 'getopt', + 'multipackage4_B', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage5_B.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage5_B.toc new file mode 100644 index 0000000000000000000000000000000000000000..127c843ace13d555a3542706b7ca64f27c51c91d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage5_B.toc @@ -0,0 +1,4 @@ +[ + 'getopt', + 'multipackage5_B', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage5_C.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage5_C.toc new file mode 100644 index 0000000000000000000000000000000000000000..1092300c604bfd9856c70b1dd478427c3953ccc7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/multipackage5_C.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'multipackage5_C', + '.*multipackage5_B.multipackage5_B:.*ssl.*', + '(?i).*multipackage5_B.multipackage5_B:.*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/pyi_import_relative.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/pyi_import_relative.toc new file mode 100644 index 0000000000000000000000000000000000000000..0ecf926c9b0f60a07d1597aa652a18c8962a5c88 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/pyi_import_relative.toc @@ -0,0 +1,14 @@ +[ +'pyi_testmod_relimp', +'pyi_testmod_relimp.B', +'pyi_testmod_relimp.B.C', +'pyi_testmod_relimp.B.D', +'pyi_testmod_relimp.E', +'pyi_testmod_relimp.F', +'pyi_testmod_relimp.F.G', +'pyi_testmod_relimp.pyi_testmod_relimp', +'pyi_testmod_relimp.pyi_testmod_relimp.relimp2', +'pyi_testmod_relimp.pyi_testmod_relimp.relimp3', +'pyi_testmod_relimp.relimp1', +'pyi_testmod_relimp.relimp2', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_hiddenimport.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_hiddenimport.toc new file mode 100644 index 0000000000000000000000000000000000000000..10d794d4153a356ed6d42848c143741db6641769 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_hiddenimport.toc @@ -0,0 +1,4 @@ +[ + 'a_hidden_import', + 'a_hidden_import.submodule', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_keyring.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_keyring.toc new file mode 100644 index 0000000000000000000000000000000000000000..80f65d381875b739a66d70c7e95f8d7cf1969999 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_keyring.toc @@ -0,0 +1,4 @@ +[ + # check is we have any of the backend-modules + 'keyring.backends.' +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage1.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage1.toc new file mode 100644 index 0000000000000000000000000000000000000000..f26706fb2cad91696263b872c381ecea5bd35554 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage1.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'test_multipackage1', + '.*multipackage1_B:.*ssl.*', + '(?i).*multipackage1_B:.*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage2.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage2.toc new file mode 100644 index 0000000000000000000000000000000000000000..053f734f0eedc3a5be7c6c1f9d0a8e1095a09ec0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage2.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'test_multipackage2', + '.*multipackage2_B.multipackage2_B:.*ssl.*', + '(?i).*multipackage2_B.multipackage2_B:.*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage3.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage3.toc new file mode 100644 index 0000000000000000000000000000000000000000..b63373dd9a977d830b9562cee19375db3507c1ed --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage3.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'test_multipackage3', + '.*multipackage3_B:.*ssl.*', + '(?i).*multipackage3_B:.*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage4.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage4.toc new file mode 100644 index 0000000000000000000000000000000000000000..807bf87e241b4c889c5060ac3ebd4af413abd618 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage4.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'test_multipackage4', + '.*multipackage4_B.multipackage4_B:.*ssl.*', + '(?i).*multipackage4_B.multipackage4_B:.*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage5.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage5.toc new file mode 100644 index 0000000000000000000000000000000000000000..c8f6d7ce99761c37e98ee32e987e7a64b624bae7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_multipackage5.toc @@ -0,0 +1,6 @@ +[ + 'getopt', + 'test_multipackage5', + '.*multipackage5_B.multipackage5_B:.*ssl.*', + '(?i).*multipackage5_B.multipackage5_B:.*python.*', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1.toc new file mode 100644 index 0000000000000000000000000000000000000000..87bf3f19569a7c17aad3d502e35b2b47d304c144 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1.toc @@ -0,0 +1,7 @@ +[ + 'nspkg1', + 'nspkg1.aaa', + 'nspkg1.bbb', + 'nspkg1.bbb.zzz', + 'nspkg1.ccc', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1_bbb_zzz.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1_bbb_zzz.toc new file mode 100644 index 0000000000000000000000000000000000000000..e9fed4c59757711ab5d5f7fa3a6339cda9f93137 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1_bbb_zzz.toc @@ -0,0 +1,5 @@ +[ + 'nspkg1', + 'nspkg1.bbb', + 'nspkg1.bbb.zzz', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1_empty.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1_empty.toc new file mode 100644 index 0000000000000000000000000000000000000000..2415c61872f406e6fdb2c990fc42d3e88ad70414 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg1_empty.toc @@ -0,0 +1,3 @@ +[ + 'nspkg1', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg2.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg2.toc new file mode 100644 index 0000000000000000000000000000000000000000..2d324fe0397510657d4547c511d8e5f9b9fcc8ab --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg2.toc @@ -0,0 +1,7 @@ +[ + 'nspkg2', + 'nspkg2.aaa', + 'nspkg2.bbb', + 'nspkg2.bbb.zzz', + 'nspkg2.ccc', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3.toc new file mode 100644 index 0000000000000000000000000000000000000000..fc29400681248af2761aca1508bc93e5d29ce38e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3.toc @@ -0,0 +1,6 @@ +[ + 'nspkg3', + 'nspkg3.aaa', + # nspkg3.bbb is not included + 'nspkg3.ccc', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_aaa.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_aaa.toc new file mode 100644 index 0000000000000000000000000000000000000000..48ac1c3c61392a78154bb27310f4513c11679af6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_aaa.toc @@ -0,0 +1,4 @@ +[ + 'nspkg3', + 'nspkg3.aaa', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_bbb_zzz.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_bbb_zzz.toc new file mode 100644 index 0000000000000000000000000000000000000000..e31e6f811905ea20616a0cedaddda1d244d45a64 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_bbb_zzz.toc @@ -0,0 +1,5 @@ +[ + 'nspkg3', + 'nspkg3.bbb', + 'nspkg3.bbb.zzz', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_empty.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_empty.toc new file mode 100644 index 0000000000000000000000000000000000000000..356dd39a77b2d0147d02f21f0995a34c7915ccc7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg3_empty.toc @@ -0,0 +1,3 @@ +[ + 'nspkg3', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg_pep420.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg_pep420.toc new file mode 100644 index 0000000000000000000000000000000000000000..be059046a34cb9387166fef9ccc7f3f6385f698f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_nspkg_pep420.toc @@ -0,0 +1,9 @@ +[ + 'package', + 'package.sub1', + 'package.sub2', + 'package.subpackage', + 'package.subpackage.sub', + 'package.nspkg', + 'package.nspkg.mod', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_option_verbose.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_option_verbose.toc new file mode 100644 index 0000000000000000000000000000000000000000..a689ff27638bc0837bce51678334731915ff01d2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_option_verbose.toc @@ -0,0 +1,8 @@ +# -*- mode: python -*- + +# archive_viewer.get_archive_content() currently only lists the names +# of each entry, not it's type. So we can only test if `v` is in the +# list. Gad there is no module named `v` :-) +[ + 'v', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pil_FixTk.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pil_FixTk.toc new file mode 100644 index 0000000000000000000000000000000000000000..ccc646c1a2337bfcb0a19f037636d3148abeda1c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pil_FixTk.toc @@ -0,0 +1,3 @@ +[ + 'FixTk' +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pil_PyQt5.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pil_PyQt5.toc new file mode 100644 index 0000000000000000000000000000000000000000..a2ba1fbac76f5ac771f3ece97338bd4bc43c051d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pil_PyQt5.toc @@ -0,0 +1,4 @@ +[ + 'PIL.ImageQt', + 'PyQt5', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pkg_without_hook_for_pkg.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pkg_without_hook_for_pkg.toc new file mode 100644 index 0000000000000000000000000000000000000000..5267e909bf4eaf2f5d72b3ab8331c58aa85c7909 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_pkg_without_hook_for_pkg.toc @@ -0,0 +1,3 @@ +[ + 'pkg_without_hook_for_pkg.sub1.sub11', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_tkinter_FixTk.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_tkinter_FixTk.toc new file mode 100644 index 0000000000000000000000000000000000000000..ccc646c1a2337bfcb0a19f037636d3148abeda1c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_tkinter_FixTk.toc @@ -0,0 +1,3 @@ +[ + 'FixTk' +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/logs/test_zope_interface.toc b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_zope_interface.toc new file mode 100644 index 0000000000000000000000000000000000000000..d1c93f88bf5e1d0b19ca718ae02912c65d9e04af --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/logs/test_zope_interface.toc @@ -0,0 +1,4 @@ +[ + 'zope$', + 'zope\.interface$', +] diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/a_hidden_import/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/a_hidden_import/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..02a9405a3860c3dee79bb92bb272c73dfed908e6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/a_hidden_import/__init__.py @@ -0,0 +1,2 @@ + +from . import submodule diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/a_hidden_import/submodule.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/a_hidden_import/submodule.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/a_hidden_import/submodule.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/error_during_import2.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/error_during_import2.py new file mode 100644 index 0000000000000000000000000000000000000000..8d54d0025e185a99a5b914725e699f31b387b324 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/error_during_import2.py @@ -0,0 +1,12 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +raise KeyError diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/module_with_coding_utf8.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/module_with_coding_utf8.py new file mode 100644 index 0000000000000000000000000000000000000000..307703b74b4fc13a2670f3160487169d168c746e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/module_with_coding_utf8.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +x = 'äöüß' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path1/package/sub2.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path1/package/sub2.py new file mode 100644 index 0000000000000000000000000000000000000000..0bfeef52906935d6ee48fd3217df87b43a7c6f29 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path1/package/sub2.py @@ -0,0 +1 @@ +""" package.sub2 """ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/nspkg/mod.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/nspkg/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..34bbb7142c09e19e3aca96998034ed19592675c9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/nspkg/mod.py @@ -0,0 +1 @@ +""" package.nspkg.mod """ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/sub1.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/sub1.py new file mode 100644 index 0000000000000000000000000000000000000000..e786a687544b2d68ed0889628b482d8b8282316b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/sub1.py @@ -0,0 +1 @@ +""" package.sub1 """ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/subpackage/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/subpackage/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f49aa42e776e1c8732b5af608f1306d5a89dfbbd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/subpackage/__init__.py @@ -0,0 +1 @@ +""" package.subpackage """ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/subpackage/sub.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/subpackage/sub.py new file mode 100644 index 0000000000000000000000000000000000000000..719a8e690ff456ec6f91aa72a60b9e25dde8e8b2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg-pep420/path2/package/subpackage/sub.py @@ -0,0 +1 @@ +""" package.subpackage.sub """ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/PKG-INFO b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..9596c8f23c33fc1f2804e0e29ec2514f95550fea --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: nspkg1-aaa +Version: 0.1 +Summary: A test package for name-spaces +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/SOURCES.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..e7d787a4db95c7ce27e20e7455c60338557a358f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,9 @@ +setup.py +nspkg1/__init__.py +nspkg1/aaa/__init__.py +nspkg1_aaa.egg-info/PKG-INFO +nspkg1_aaa.egg-info/SOURCES.txt +nspkg1_aaa.egg-info/dependency_links.txt +nspkg1_aaa.egg-info/namespace_packages.txt +nspkg1_aaa.egg-info/not-zip-safe +nspkg1_aaa.egg-info/top_level.txt \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/dependency_links.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/namespace_packages.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/namespace_packages.txt new file mode 100644 index 0000000000000000000000000000000000000000..43f1ab2eff95a6c9ae8b862aed1b707ee2a9c345 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/namespace_packages.txt @@ -0,0 +1 @@ +nspkg1 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/not-zip-safe b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/not-zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/top_level.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..43f1ab2eff95a6c9ae8b862aed1b707ee2a9c345 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +nspkg1 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/nspkg1/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/nspkg1/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d7e2f9874746ec497695c87c2979347862fed095 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/nspkg1/__init__.py @@ -0,0 +1,3 @@ + +import pkg_resources +pkg_resources.declare_namespace(__name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/nspkg1/aaa/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/nspkg1/aaa/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..51bd1b194b7aa6b6b8c8bc618b7c589a3af58506 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_aaa.egg/nspkg1/aaa/__init__.py @@ -0,0 +1,2 @@ + +print(('this is module %s' % __name__)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_bbb.egg b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_bbb.egg new file mode 100644 index 0000000000000000000000000000000000000000..97a59204b0df271bd892bd836562a3e60656572d Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_bbb.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/PKG-INFO b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..5b6a22fcc8ddccbbe3443adf26f8879721e742e7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: nspkg1-ccc +Version: 0.1 +Summary: A test package for name-spaces +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/SOURCES.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..8d7e630040cad75de05a4d8538ab0ee1798c3859 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,9 @@ +setup.py +nspkg1/__init__.py +nspkg1/ccc.py +nspkg1_ccc.egg-info/PKG-INFO +nspkg1_ccc.egg-info/SOURCES.txt +nspkg1_ccc.egg-info/dependency_links.txt +nspkg1_ccc.egg-info/namespace_packages.txt +nspkg1_ccc.egg-info/not-zip-safe +nspkg1_ccc.egg-info/top_level.txt \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/dependency_links.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/namespace_packages.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/namespace_packages.txt new file mode 100644 index 0000000000000000000000000000000000000000..43f1ab2eff95a6c9ae8b862aed1b707ee2a9c345 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/namespace_packages.txt @@ -0,0 +1 @@ +nspkg1 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/not-zip-safe b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/not-zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/top_level.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..43f1ab2eff95a6c9ae8b862aed1b707ee2a9c345 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +nspkg1 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/nspkg1/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/nspkg1/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d7e2f9874746ec497695c87c2979347862fed095 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/nspkg1/__init__.py @@ -0,0 +1,3 @@ + +import pkg_resources +pkg_resources.declare_namespace(__name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/nspkg1/ccc.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/nspkg1/ccc.py new file mode 100644 index 0000000000000000000000000000000000000000..51bd1b194b7aa6b6b8c8bc618b7c589a3af58506 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_ccc.egg/nspkg1/ccc.py @@ -0,0 +1,2 @@ + +print(('this is module %s' % __name__)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_empty.egg b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_empty.egg new file mode 100644 index 0000000000000000000000000000000000000000..d972fcbc6b951660159f13221a71e83a8efe07b3 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg1-pkg/nspkg1_empty.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/aaa/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/aaa/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..51bd1b194b7aa6b6b8c8bc618b7c589a3af58506 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/aaa/__init__.py @@ -0,0 +1,2 @@ + +print(('this is module %s' % __name__)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/bbb/zzz/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/bbb/zzz/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..51bd1b194b7aa6b6b8c8bc618b7c589a3af58506 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/bbb/zzz/__init__.py @@ -0,0 +1,2 @@ + +print(('this is module %s' % __name__)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/ccc.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/ccc.py new file mode 100644 index 0000000000000000000000000000000000000000..51bd1b194b7aa6b6b8c8bc618b7c589a3af58506 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2/ccc.py @@ -0,0 +1,2 @@ + +print(('this is module %s' % __name__)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_aaa-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_aaa-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..c9dcc9556c6ce50917d48845991b718c7d0d69e9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_aaa-nspkg.pth @@ -0,0 +1 @@ +import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('nspkg2',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('nspkg2',types.ModuleType('nspkg2')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_bbb-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_bbb-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..928d8d82300f6c40d496169ef4c49694d5dc5286 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_bbb-nspkg.pth @@ -0,0 +1,2 @@ +import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('nspkg2',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('nspkg2',types.ModuleType('nspkg2')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) +import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('nspkg2', 'bbb')); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('nspkg2.bbb',types.ModuleType('nspkg2.bbb')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p); m and setattr(sys.modules['nspkg2'], 'bbb', m) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_ccc-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_ccc-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..c9dcc9556c6ce50917d48845991b718c7d0d69e9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_ccc-nspkg.pth @@ -0,0 +1 @@ +import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('nspkg2',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('nspkg2',types.ModuleType('nspkg2')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_empty-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_empty-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..9eecbdda12d3a0aa6a73297743797e9b8342f964 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg2-pkg/nspkg2_empty-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('nspkg2',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and sys.modules.setdefault('nspkg2', types.ModuleType('nspkg2'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/PKG-INFO b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..ae713d028e852e5b5fec3c93b72102e360cde1ec --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: nspkg3-aaa +Version: 0.1 +Summary: A test package for name-spaces +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/SOURCES.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..3406b0ba302a555b0231053c60a056828b255c13 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,9 @@ +setup.py +nspkg3/__init__.py +nspkg3/aaa/__init__.py +nspkg3_aaa.egg-info/PKG-INFO +nspkg3_aaa.egg-info/SOURCES.txt +nspkg3_aaa.egg-info/dependency_links.txt +nspkg3_aaa.egg-info/namespace_packages.txt +nspkg3_aaa.egg-info/not-zip-safe +nspkg3_aaa.egg-info/top_level.txt \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/dependency_links.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/namespace_packages.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/namespace_packages.txt new file mode 100644 index 0000000000000000000000000000000000000000..56c441dbb17d080a9984f753ca019e3ec75ceb7b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/namespace_packages.txt @@ -0,0 +1 @@ +nspkg3 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/not-zip-safe b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/not-zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/top_level.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..56c441dbb17d080a9984f753ca019e3ec75ceb7b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +nspkg3 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/nspkg3/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/nspkg3/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..225b10b4e3410ae5cc5f8ee4034fc665a8501174 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/nspkg3/__init__.py @@ -0,0 +1,3 @@ + +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/nspkg3/aaa/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/nspkg3/aaa/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..47dbb64b7c44d483ceaaed762f6c67479fa892bc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_aaa.egg/nspkg3/aaa/__init__.py @@ -0,0 +1,2 @@ + +print ('this is module %s' % __name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_bbb.egg b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_bbb.egg new file mode 100644 index 0000000000000000000000000000000000000000..623b442a86f77b38b37ef36ffd9578f9f2746b89 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_bbb.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/PKG-INFO b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..0189a89bb0b39e290f24025185d42cac5cbe0bf4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: nspkg3-ccc +Version: 0.1 +Summary: A test package for name-spaces +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/SOURCES.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..96cb115713152063307729d1b3119968a91ed5e7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,9 @@ +setup.py +nspkg3/__init__.py +nspkg3/ccc.py +nspkg3_ccc.egg-info/PKG-INFO +nspkg3_ccc.egg-info/SOURCES.txt +nspkg3_ccc.egg-info/dependency_links.txt +nspkg3_ccc.egg-info/namespace_packages.txt +nspkg3_ccc.egg-info/not-zip-safe +nspkg3_ccc.egg-info/top_level.txt \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/dependency_links.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/namespace_packages.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/namespace_packages.txt new file mode 100644 index 0000000000000000000000000000000000000000..56c441dbb17d080a9984f753ca019e3ec75ceb7b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/namespace_packages.txt @@ -0,0 +1 @@ +nspkg3 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/not-zip-safe b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/not-zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/top_level.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..56c441dbb17d080a9984f753ca019e3ec75ceb7b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +nspkg3 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/nspkg3/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/nspkg3/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..225b10b4e3410ae5cc5f8ee4034fc665a8501174 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/nspkg3/__init__.py @@ -0,0 +1,3 @@ + +from pkgutil import extend_path +__path__ = extend_path(__path__, __name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/nspkg3/ccc.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/nspkg3/ccc.py new file mode 100644 index 0000000000000000000000000000000000000000..47dbb64b7c44d483ceaaed762f6c67479fa892bc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_ccc.egg/nspkg3/ccc.py @@ -0,0 +1,2 @@ + +print ('this is module %s' % __name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_empty.egg b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_empty.egg new file mode 100644 index 0000000000000000000000000000000000000000..6e7f5e464c54ba33f677deb6e19ba858a8c2cfac Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/modules/nspkg3-pkg/nspkg3_empty.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg3/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg3/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg3/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg3/sample-data.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg3/sample-data.txt new file mode 100644 index 0000000000000000000000000000000000000000..580088aea9e43da905540eb290ad44a47f767130 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg3/sample-data.txt @@ -0,0 +1 @@ +This is data text for testing the packaging module data. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/sub1/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/sub1/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..49b6fec2e18ebbabbe02b9a9da20f0977c7740af --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/sub1/__init__.py @@ -0,0 +1,9 @@ +# + +print ('This is module ' + __name__) + +# Tick: import a package in a way, PyInstaller is not able to track +# it. But we want to import it anyway to make the submodule `sub11` +# print it's name. +sub11 = ''.join('sub11') +__import__(__name__ + '.' + sub11, globals=globals(), level=0) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/sub1/sub11/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/sub1/sub11/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c719c62674c5aba08431e3dc6e5d1ac4c93429be --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pkg_without_hook_for_pkg/sub1/sub11/__init__.py @@ -0,0 +1,2 @@ +# +print('This is ' + __name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_collect_submodules_mod.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_collect_submodules_mod.py new file mode 100644 index 0000000000000000000000000000000000000000..0d358d027aee03c2c7142bdc3baaa977ce0d2d7c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_collect_submodules_mod.py @@ -0,0 +1,12 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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 used by pyi_collect_submodules.py. See the explanation there. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_dummy_module.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_dummy_module.py new file mode 100644 index 0000000000000000000000000000000000000000..059d6abca472362eb41f1eae6c9536b915890eb7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_dummy_module.py @@ -0,0 +1,15 @@ +""" + pyi_dummy_module + + This module exists only so it can be imported and its __file__ inspected in + the test `test_compiled_filenames` +""" + + +def dummy(): + pass + + +class DummyClass(object): + def dummyMethod(self): + pass diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/PKG-INFO b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/PKG-INFO new file mode 100644 index 0000000000000000000000000000000000000000..ba01fc0f8250a96895eddb8c0bb931e24b72dfb4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: unzipped-egg +Version: 0.1 +Summary: A unzipped egg for testing PyInstaller +Home-page: UNKNOWN +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/SOURCES.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/SOURCES.txt new file mode 100644 index 0000000000000000000000000000000000000000..e062b36a56469b61326ad8e9a56f9884d0963a7b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/SOURCES.txt @@ -0,0 +1,8 @@ +README.txt +setup-unzipped.py +unzipped_egg/__init__.py +unzipped_egg.egg-info/PKG-INFO +unzipped_egg.egg-info/SOURCES.txt +unzipped_egg.egg-info/dependency_links.txt +unzipped_egg.egg-info/not-zip-safe +unzipped_egg.egg-info/top_level.txt \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/dependency_links.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/dependency_links.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/not-zip-safe b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/not-zip-safe new file mode 100644 index 0000000000000000000000000000000000000000..d3f5a12faa99758192ecc4ed3fc22c9249232e86 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/not-zip-safe @@ -0,0 +1 @@ + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/top_level.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/top_level.txt new file mode 100644 index 0000000000000000000000000000000000000000..e1f3abf9475223e45f578a911c11860c760efc56 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/EGG-INFO/top_level.txt @@ -0,0 +1 @@ +unzipped_egg diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/unzipped_egg/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/unzipped_egg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..33dcaaf069d3c8c3327a53392bde27cf9c830d3b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/unzipped_egg/__init__.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 file is part of the package for testing eggs in `PyInstaller`. + + +import pkg_resources +data = pkg_resources.resource_string(__name__, 'data/datafile.txt').rstrip() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/unzipped_egg/data/datafile.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/unzipped_egg/data/datafile.txt new file mode 100644 index 0000000000000000000000000000000000000000..7b2df8efb407242de48b9c48a04350aa4e04d36a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_unzipped.egg/unzipped_egg/data/datafile.txt @@ -0,0 +1 @@ +This is data file for `unzipped`. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_zipped.egg b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_zipped.egg new file mode 100644 index 0000000000000000000000000000000000000000..b4ff61613023f2441f789f11da781fc780c06576 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_egg_zipped.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_get_datadir.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_get_datadir.py new file mode 100644 index 0000000000000000000000000000000000000000..6e79eb501113ca9b008e8fc5251c81270329032f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_get_datadir.py @@ -0,0 +1,39 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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 assists in determining the location of the tests/functional/data +# directory. + +# Library imports +# --------------- +import sys +import os.path + +# Local imports +# ------------- +from pyi_testmod_gettemp import gettemp + +# Functions +# --------- +# This function returns the location of the +# tests/functional/data directory when run from a test, either built by +# Pyinstaller or simply run from the Python interpreter. +def get_data_dir(): + # Some tests need input files to operate on. There are two cases: + if getattr(sys, 'frozen', False): + # 1. Frozen: rely on gettemp to find the correct directory both in + # onefile and in onedir modes. + return gettemp('data') + else: + # 2. Not frozen: rely on the filesystem layout of this git repository. + return os.path.join(os.path.dirname(os.path.abspath(__file__)), + '..', 'data') + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/QtCore.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/QtCore.py new file mode 100644 index 0000000000000000000000000000000000000000..9be46073631f1093d36792d15ccbd9c99d49db32 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/QtCore.py @@ -0,0 +1,16 @@ +# fake module to make PyQt5 hooks and run-time-hooks happy +__pyinstaller_fake_module_marker__ = '__pyinstaller_fake_module_marker__' + +class QLibraryInfo: + def __init__(*args, **kw): pass + + PrefixPath = 1 + BinariesPath = 2 + + @classmethod + def location(cls, val): + return "." + + @classmethod + def isDebugBuild(cls): + return False diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a3fe973ca83615124f023f560ca4d50f59c73c84 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/__init__.py @@ -0,0 +1,2 @@ +# fake module +__pyinstaller_fake_module_marker__ = '__pyinstaller_fake_module_marker__' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..76aa587c7583c4f6f723d634d8c713acc57457cb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/__init__.py @@ -0,0 +1,2 @@ +print('this is my faked PyQt5.uic package') +__pyinstaller_fake_module_marker__ = '__pyinstaller_fake_module_marker__' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v2/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..78df8985bd40baaa0a1761ae81339ba426778eeb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v2/__init__.py @@ -0,0 +1,3 @@ +__pyinstaller_fake_module_marker__ = '__pyinstaller_fake_module_marker__' +print('this is PyQtx.uic.port_v3') +from . import test diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v2/test.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v2/test.py new file mode 100644 index 0000000000000000000000000000000000000000..546f6a95e0502097221a16e74309c7aa00312089 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v2/test.py @@ -0,0 +1,3 @@ +__pyinstaller_fake_module_marker__ = '__pyinstaller_fake_module_marker__' +import sys +assert sys.version_info[0] < 3, "this does not work for Python 3" diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v3/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v3/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3de6bca74a1a6233f82f2513315a9b0ac8c456dc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v3/__init__.py @@ -0,0 +1,3 @@ +__pyinstaller_fake_module_marker__ = '__pyinstaller_fake_module_marker__' +print('this is PyQtx.uic.port_v2') +from . import test diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v3/test.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v3/test.py new file mode 100644 index 0000000000000000000000000000000000000000..7f12043519d4592ab780a6411be6bb9b75e9fc24 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_import_pyqt_uic_port/PyQt5/uic/port_v3/test.py @@ -0,0 +1,3 @@ +__pyinstaller_fake_module_marker__ = '__pyinstaller_fake_module_marker__' +import sys +assert sys.version_info[0] >= 3, "this does not work for Python 2" diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_issue_4141/app/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_issue_4141/app/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_issue_4141/app/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/hooks/hook-pyi_pkgres_testpkg.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/hooks/hook-pyi_pkgres_testpkg.py new file mode 100644 index 0000000000000000000000000000000000000000..1e178e13457dfc2e53e0f9c597acd7139c693afe --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/hooks/hook-pyi_pkgres_testpkg.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2020, 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 PyInstaller.utils.hooks import collect_data_files + +datas = collect_data_files('pyi_pkgres_testpkg', excludes=['**/__pycache__', ]) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..5b860593c0c1408b166eadc5b91b9863dda5d9b2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/__init__.py @@ -0,0 +1,2 @@ +from . import a, b # noqa: F401 +from . import subpkg1, subpkg2, subpkg3 # noqa: F401 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/a.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/a.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/a.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/b.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/b.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/b.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a1e57d79f1f6972d65fd2405b108b9e5c7b12a68 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/__init__.py @@ -0,0 +1 @@ +from . import c, d # noqa: F401 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/c.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/c.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/c.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/d.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/d.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/d.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry1.txt b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry1.txt new file mode 100644 index 0000000000000000000000000000000000000000..22767eec0b0fa1e19129a67c89d6a9b6a949cfdf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry1.txt @@ -0,0 +1 @@ +Data entry #1 in subpkg1/data. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry2.md b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry2.md new file mode 100644 index 0000000000000000000000000000000000000000..35168f3a31c80e3bbab7d2d519d4b351282303ef --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry2.md @@ -0,0 +1 @@ +Data entry #2 in `subpkg1/data`. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry3.rst b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry3.rst new file mode 100644 index 0000000000000000000000000000000000000000..4bccd8718b769325dfcd443f124b90f94904ec60 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/entry3.rst @@ -0,0 +1 @@ +Data entry #3 in ``subpkg1/data``. diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/extra/extra_entry1.json b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/extra/extra_entry1.json new file mode 100644 index 0000000000000000000000000000000000000000..eb0936f86291064ee2eedaa3bfae1ecd8faf4db3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg1/data/extra/extra_entry1.json @@ -0,0 +1,3 @@ +{ + "_comment": "Extra data entry #1 in subpkg1/data/extra." +} diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..862336d157f1595a3f449e5b5d909f1640a71f3f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/__init__.py @@ -0,0 +1,2 @@ +from . import subsubpkg21 # noqa: F401 +from . import mod # noqa: F401 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/mod.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/mod.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/subsubpkg21/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/subsubpkg21/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..178e66735cce41bbbaf61347f3c87ce699f8f6cd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/subsubpkg21/__init__.py @@ -0,0 +1 @@ +from . import mod # noqa: F401 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/subsubpkg21/mod.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/subsubpkg21/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg2/subsubpkg21/mod.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg3/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg3/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg3/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg3/_datafile.json b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg3/_datafile.json new file mode 100644 index 0000000000000000000000000000000000000000..d3cbf20f4166cea7493e97e292e23b74a69570aa --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/pyi_pkgres_testpkg/subpkg3/_datafile.json @@ -0,0 +1,3 @@ +{ + "_comment": "Data file in supbkg3." +} diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/setup.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..2a135dc583a4d2502a9a3397f99bd308e8454272 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_pkg_resources_provider/package/setup.py @@ -0,0 +1,33 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2020, 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 assists in creating an ``.egg`` package for use with the +# ``test_pkg_resources_provider``. To build the package, execute +# ``python setup.py bdist_egg``. + +import setuptools + +setuptools.setup( + name='pyi_pkgres_testpkg', + version='1.0.0', + setup_requires="setuptools >= 40.0.0", + author='PyInstaller development team', + packages=setuptools.find_packages(), + package_data={ + "pyi_pkgres_testpkg": [ + "subpkg1/data/*.txt", + "subpkg1/data/*.md", + "subpkg1/data/*.rst", + "subpkg1/data/extra/*.json", + "subpkg3/*.json", + ], + } +) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_dynamic.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_dynamic.py new file mode 100644 index 0000000000000000000000000000000000000000..cfc642f594949a7ec85e3340b6cc279a19c8c9b1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_dynamic.py @@ -0,0 +1,37 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +""" +When frozen, a module that dynamically recreates itself at runtime (by replacing +itself in sys.modules) should be returned by __import__ statement. + +This example should return True: + >>> sys.modules[] is __import__() + True +""" + +import sys +import types + +# The DynamicModule should override this attribute. +foo = None + + +class DynamicModule(types.ModuleType): + __file__ = __file__ + def __init__(self, name): + super(DynamicModule, self).__init__(name) + self.foo = "A new value!" + + +# Replace module 'pyi_testmod_dynamic' by class DynamicModule. +sys.modules[__name__] = DynamicModule(__name__) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_gettemp.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_gettemp.py new file mode 100644 index 0000000000000000000000000000000000000000..54ffe23008b36a9bf5c746b416e22f34667ead42 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_gettemp.py @@ -0,0 +1,39 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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, sys + +def gettemp(basename): + """ + Get the path to a temp file previously written by the temp runner. Useful to + compare results between running in interpreter and running frozen. + """ + exec_dir = os.path.dirname(sys.executable) + # onedir mode: + # tmpdir + # ├── python_exe.build + # ├── build + # └── dist + #    └── appname + #    └── appname.exe + file_onedir = os.path.join(exec_dir, '..', '..', basename) + # onefile mode: + # tmpdir + # ├── python_exe.build + # ├── build + # └── dist + #    └── appname.exe + file_onefile = os.path.join(exec_dir, '..', basename) + + if os.path.exists(file_onedir): + return file_onedir + else: + return file_onefile diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4869e15ff58ff17f80fe445bd51340e45b2cabd5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/__init__.py @@ -0,0 +1,44 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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 pyi_testmod_metapath1.extern import aaa # __import__ below works +#import pyi_testmod_metapath1.extern # __import__ below works +#import pyi_testmod_metapath1.extern.aaa # __import__ below fails! +__import__('pyi_testmod_metapath1.extern.bbb') + +try: + bbb = pyi_testmod_metapath1.extern.bbb +except NameError: + pass +else: + print("name:", bbb.__name__) + print("__file__:", bbb.__file__ or repr(bbb.__file__)) + assert bbb.__name__.endswith('._vendor.bbb') + + +# Second test: import sub-sub-package +# Mimic this lines from pkg_resources.__init__.py as of setuptools 28.6.1: +# from pkg_resources.extern import packaging +# __import__('pkg_resources.extern.packaging.version') +from pyi_testmod_metapath1.extern import ccc +__import__('pyi_testmod_metapath1.extern.ccc.ddd') + +try: + ddd = pyi_testmod_metapath1.extern.ccc.ddd +except NameError: + pass +else: + print("name:", ddd.__name__) + print("__file__:", ddd.__file__ or repr(ddd.__file__)) + assert ddd.__name__.endswith('._vendor.ccc.ddd') + +# Third test: import sub-sub-sub-package +__import__('pyi_testmod_metapath1.extern.ccc.eee.fff') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/aaa.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/aaa.py new file mode 100644 index 0000000000000000000000000000000000000000..d72b51e6a50be0d17dfe71d15e6ac421d0a425d4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/aaa.py @@ -0,0 +1,12 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +assert __name__.endswith('_vendor.aaa'), __name__ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/bbb.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/bbb.py new file mode 100644 index 0000000000000000000000000000000000000000..97a9792dc338c2ac727ae3179c56e91b6b316a62 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/bbb.py @@ -0,0 +1,12 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +assert __name__.endswith('_vendor.bbb'), __name__ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/ddd.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/ddd.py new file mode 100644 index 0000000000000000000000000000000000000000..2fd5587a3c48a31b0de43183efe6f6854496d526 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/ddd.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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, when imported as 'pyi_testmod_metapath1.extern.ccc.ddd', has +# actually has this __name__, even if the parent module's __name__ is +# 'pyi_testmod_metapath1._vendor.ccc'. This is since the parent module is +# known to sys.modules as '.extern.ccc'. + +assert __name__.endswith('.ccc.ddd'), __name__ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/eee/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/eee/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/eee/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/eee/fff.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/eee/fff.py new file mode 100644 index 0000000000000000000000000000000000000000..3b5818b9f364f057e7f62055611c4eda32d23496 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/_vendor/ccc/eee/fff.py @@ -0,0 +1,12 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + +assert __name__.endswith('.eee.fff'), __name__ diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/extern/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/extern/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..981a2187b2ed11f0ec8f605ae283b11baea2321b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_metapath1/extern/__init__.py @@ -0,0 +1,110 @@ +# Copyright (C) 2017-2021 PyInstaller Development Team. +# Copyright (C) 2016 Jason R Coombs +# +# This file includes an almost complete copy of +# pkg_resources/extern/__init__.py, taken from setuptools 28.6.1. +# For PyInstaller the only change is to install a sub-class of VendorImporter. +# +# setuptools is licensed under the MIT License (expat) license: +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# +# SPDX-License-Identifier: MIT + +#--- Code of pkg_resources/extern/__init__.py starts here +import sys + +class VendorImporter: + """ + A PEP 302 meta path importer for finding optionally-vendored + or otherwise naturally-installed packages from root_name. + """ + + def __init__(self, root_name, vendored_names=(), vendor_pkg=None): + self.root_name = root_name + self.vendored_names = set(vendored_names) + self.vendor_pkg = vendor_pkg or root_name.replace('extern', '_vendor') + + @property + def search_path(self): + """ + Search first the vendor package then as a natural package. + """ + yield self.vendor_pkg + '.' + yield '' + + def find_module(self, fullname, path=None): + """ + Return self when fullname starts with root_name and the + target module is one vendored through this importer. + """ + root, base, target = fullname.partition(self.root_name + '.') + if root: + return + if not any(map(target.startswith, self.vendored_names)): + return + return self + + def load_module(self, fullname): + """ + Iterate over the search path to locate and load fullname. + """ + root, base, target = fullname.partition(self.root_name + '.') + for prefix in self.search_path: + try: + extant = prefix + target + __import__(extant) + mod = sys.modules[extant] + sys.modules[fullname] = mod + # mysterious hack: + # Remove the reference to the extant package/module + # on later Python versions to cause relative imports + # in the vendor package to resolve the same modules + # as those going through this importer. + if sys.version_info > (3, 3): + del sys.modules[extant] + return mod + except ImportError: + pass + else: + raise ImportError( + "The '{target}' package is required; " + "normally this is bundled with this package so if you get " + "this warning, consult the packager of your " + "distribution.".format(**locals()) + ) + def install(self): + """ + Install this importer into sys.meta_path if not already present. + """ + if self not in sys.meta_path: + sys.meta_path.append(self) + +#--- Code of pkg_resources/extern/__init__.py ends here + +class MyVendorImporter(VendorImporter): + @property + def search_path(self): + """ + Only search the vendor package, and not a natural package. + """ + yield self.vendor_pkg + '.' + +names = ('aaa', 'bbb', 'ccc') +MyVendorImporter(__name__, names).install() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_missing_submod/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_missing_submod/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_missing_submod/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_missing_submod/aaa/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_missing_submod/aaa/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_missing_submod/aaa/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_nameclash/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_nameclash/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_nameclash/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_nameclash/nameclash.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_nameclash/nameclash.py new file mode 100644 index 0000000000000000000000000000000000000000..e983c3a15034a934fc7f62d9c0a88d6df071cd68 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_nameclash/nameclash.py @@ -0,0 +1,2 @@ +# this module is required for test_nameclash +imports = 1 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6ea41de79a6211bfa92db4f527903652c7d4f349 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/__init__.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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.path +# Insert a/ at the beginning of __path__. +__path__.insert(0, os.path.join(__path__[0], 'a')) +# Import b, which should now find a/b.py, no ./b.py. +import b diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/a/b.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/a/b.py new file mode 100644 index 0000000000000000000000000000000000000000..ef49148f05a4f67b41eff39f69caaa7303556a73 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/a/b.py @@ -0,0 +1,11 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/b.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/b.py new file mode 100644 index 0000000000000000000000000000000000000000..4a2ec96c32c2dbb2b18d653ae26e181146d917d2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_path/b.py @@ -0,0 +1,11 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- +raise ImportError diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/C.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/C.py new file mode 100644 index 0000000000000000000000000000000000000000..69f7a173111ca0fbb2ed46af8dfed8e0143d3f43 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/C.py @@ -0,0 +1,24 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.B.C' + +from . import D # Imports pyi_testmod_relimp.B.D +from .D import X # Imports pyi_testmod_relimp.B.D.X +from .. import E # Imports pyi_testmod_relimp.E +from ..F import G # Imports pyi_testmod_relimp.F.G +from ..F import H # Imports pyi_testmod_relimp.F.H + +assert D.name == 'pyi_testmod_relimp.B.D' +assert E.name == 'pyi_testmod_relimp.E' +assert G.name == 'pyi_testmod_relimp.F.G' +assert H.name == 'pyi_testmod_relimp.F.H' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/D.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/D.py new file mode 100644 index 0000000000000000000000000000000000000000..0d172e3619a972d35812bce56ea711f8ab4a12a2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/D.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.B.D' + +class X: + name = 'pyi_testmod_relimp.B.D.X' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a7632099256822dbe85cb4645925def3898ee383 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/B/__init__.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.B' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/E.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/E.py new file mode 100644 index 0000000000000000000000000000000000000000..354990c949060c2c797805c0b800fd05f6e263d4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/E.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.E' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/F/G.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/F/G.py new file mode 100644 index 0000000000000000000000000000000000000000..abd87f4df69e26cbe9d68a908f06eb047d9fd459 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/F/G.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.F.G' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/F/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/F/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ae8c1d390362e0ccb3ea7c284f409a7687674476 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/F/__init__.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.F' + +class H: + name = 'pyi_testmod_relimp.F.H' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a1c3491870e3fed0d8708347adf821379a4a663a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/__init__.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a1b81a48929b8228aae0a8b1a3fb19d9e889792f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/__init__.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.pyi_testmod_relimp' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/relimp2.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/relimp2.py new file mode 100644 index 0000000000000000000000000000000000000000..a0983e7fd796907c2501b0e78d9fa5faee9b848c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/relimp2.py @@ -0,0 +1,31 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.pyi_testmod_relimp.relimp2' + +from . import relimp3 +assert relimp3.name == 'pyi_testmod_relimp.pyi_testmod_relimp.relimp3' + +from .. import pyi_testmod_relimp +assert pyi_testmod_relimp.name == 'pyi_testmod_relimp.pyi_testmod_relimp' + +import pyi_testmod_relimp +assert pyi_testmod_relimp.name == 'pyi_testmod_relimp' + +import pyi_testmod_relimp.relimp2 +assert pyi_testmod_relimp.relimp2.name == 'pyi_testmod_relimp.relimp2' + +# While this seams to work when running Python, it is wrong: +# .pyi_testmod_relimp should be a sibling of this package +#from .pyi_testmod_relimp import relimp2 +#assert pyi_testmod_relimp2.name == 'pyi_testmod_relimp.relimp2' + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/relimp3.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/relimp3.py new file mode 100644 index 0000000000000000000000000000000000000000..cfe9fc46b1bb9f0f436e7be9400990e296e2a45c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/pyi_testmod_relimp/relimp3.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.pyi_testmod_relimp.relimp3' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/relimp1.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/relimp1.py new file mode 100644 index 0000000000000000000000000000000000000000..0af90f068d33a95cd9ea2057a24b1d885f14077f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/relimp1.py @@ -0,0 +1,25 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.relimp1' + +from . import relimp2 as upper +from . pyi_testmod_relimp import relimp2 as lower + +assert upper.name == 'pyi_testmod_relimp.relimp2' +assert lower.name == 'pyi_testmod_relimp.pyi_testmod_relimp.relimp2' + +if upper.__name__ == lower.__name__: + raise SystemExit("Imported the same module") + +if upper.__file__ == lower.__file__: + raise SystemExit("Imported the same file") diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/relimp2.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/relimp2.py new file mode 100644 index 0000000000000000000000000000000000000000..c81712d02797f443493668b4b01a23a7415423ad --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp/relimp2.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +name = 'pyi_testmod_relimp.relimp2' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b16beb32e00ba155dfbb18150923fc9da00e2904 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/__init__.py @@ -0,0 +1,10 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..baccc35991bd674313457c3ae33ec357e49e16f3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/__init__.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 .baz import say_hello_please diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/bar2/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/bar2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..34c5e09fb85b9a9519a236618bcec17690895981 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/bar2/__init__.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 ..baz import say_hello_please diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/baz.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/baz.py new file mode 100644 index 0000000000000000000000000000000000000000..d4ed2b981f78eb08d20b84a31bac5d1b8c237ced --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp2/bar/baz.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +def say_hello_please(): + print("Hello World!") diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b16beb32e00ba155dfbb18150923fc9da00e2904 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/__init__.py @@ -0,0 +1,10 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b16beb32e00ba155dfbb18150923fc9da00e2904 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/__init__.py @@ -0,0 +1,10 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/a1.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/a1.py new file mode 100644 index 0000000000000000000000000000000000000000..b48a25f61ed800b0c0eac557a1aa9cee3020cabb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/a1.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# NOTE Relative imports do not work beyond toplevel package. + + +# Should look for module 'pyi_testmod_relimp3c' in current directory. +from .pyi_testmod_relimp3c import c1 +# Should look for module 'b1' in directory '../pyi_testmod_relimp3b' - one level up. +from ..pyi_testmod_relimp3b import b1 + +def getString(): + return b1.string + c1.string + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/pyi_testmod_relimp3c.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/pyi_testmod_relimp3c.py new file mode 100644 index 0000000000000000000000000000000000000000..20156c09ec3d43968732e9bf34477ae905cadc61 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/aa/pyi_testmod_relimp3c.py @@ -0,0 +1,14 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 c1: + string = "... and this" diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3b/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3b/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b16beb32e00ba155dfbb18150923fc9da00e2904 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3b/__init__.py @@ -0,0 +1,10 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3b/b1.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3b/b1.py new file mode 100644 index 0000000000000000000000000000000000000000..236b5f82e0a810913f1dc6dadde5fff647dc5595 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3b/b1.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +string = "I hope you see this!" diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3c.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3c.py new file mode 100644 index 0000000000000000000000000000000000000000..84c0d8e2ef608544973b916c17ce44d0ed129e81 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3a/pyi_testmod_relimp3c.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +raise ValueError diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3b/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3b/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..b16beb32e00ba155dfbb18150923fc9da00e2904 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3b/__init__.py @@ -0,0 +1,10 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3b/b1.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3b/b1.py new file mode 100644 index 0000000000000000000000000000000000000000..84c0d8e2ef608544973b916c17ce44d0ed129e81 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3b/b1.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +raise ValueError diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3c/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3c/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ca793fff35bd724b7c5f50b9f9819d32958a207d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_relimp3c/__init__.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +raise RuntimeError diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_from_aliased_pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_from_aliased_pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_from_aliased_pkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_from_aliased_pkg/submodule.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_from_aliased_pkg/submodule.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_from_aliased_pkg/submodule.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_shadowed/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_shadowed/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..636e27bff478d02f42c127e01ccef661c4754077 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_shadowed/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +''' +Mock package defining a global variable of the same name as a mock submodule of +this package. + +This package is exercised by the `test_import_submodule_global_shadowed` +functional test. +''' + + +submodule = 'That is not dead which can eternal lie.' +''' +Global variable of the same name as a mock submodule of this package. + +This variable's value is arbitrary. This variable's type, however, is asserted +to be `str` by this test. +''' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_shadowed/submodule.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_shadowed/submodule.py new file mode 100644 index 0000000000000000000000000000000000000000..0ecc7f60f4382ea58c5e8fa6094290fa0bdb12ad --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_shadowed/submodule.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +''' +Mock module of the same name as _and_ shadowed by a global variable defined by +the `__init__` submodule of this package. + +This module is exercised by the `test_import_submodule_global_shadowed` +functional test. +''' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_unshadowed/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_unshadowed/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..2bb8a0bf9c2b8bfa7a1f83264971ba3818d77ee6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_unshadowed/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +''' +Mock package defining and then deleting a global variable of the same name as a +mock submodule of this package. + +This package is exercised by the `test_import_submodule_global_unshadowed` +functional test. +''' + + +submodule = 'And with strange aeons even death may die.' +''' +Global variable of the same name as a mock submodule of this package. + +This variable's value is both arbitrary _and_ always ignored by this test. +''' + + +# Permit the "submodule" submodule to be imported. Since globals take precedence +# over submodules of the same name, failing to undefine this global would +# prevent this submodule from being imported. +# +# PyInstaller explicitly detects both the definition and undefinition of globals +# in the modules containing those globals -- namely, here. PyInstaller does not, +# however, detect either the definition or undefinition of these globals from +# other modules. In particular, PyInstaller ignores attempts to undefine this +# global from the functional test exercising this module (e.g., using +# "del pyi_testmod_submodule_global.submodule"). +del submodule diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_unshadowed/submodule.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_unshadowed/submodule.py new file mode 100644 index 0000000000000000000000000000000000000000..f235dad7c9b53def128729ff844bd437b373416c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_submodule_global_unshadowed/submodule.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +''' +Mock module of the same name as but _not_ shadowed by a global variable defined +by the `__init__` submodule of this package. + +This module is exercised by the `test_import_submodule_global_unshadowed` +functional test. +''' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_threading.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_threading.py new file mode 100644 index 0000000000000000000000000000000000000000..ceb37ba104105eef8dc1967ed3a10072942f5447 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/pyi_testmod_threading.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 time +# add a short delay to trigger concurrency problems, +# see issue #2371. +time.sleep(1) + +x = 5 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/modules/resources_testmod.py b/3rdparty/pyinstaller-4.3/tests/functional/modules/resources_testmod.py new file mode 100644 index 0000000000000000000000000000000000000000..7228d1375ab24145dc94178ae15a96e375538a9e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/modules/resources_testmod.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 is a module whose name begins with 're'. It will be used to verify that the +regex for excluding modules listed in PY3_BASE_MODULES (which includes 're') does +not exclude this module. +""" \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/list_pytest11_entry_point.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/list_pytest11_entry_point.py new file mode 100644 index 0000000000000000000000000000000000000000..9628ffc0f82b22e11fa0530021d3e47658839d98 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/list_pytest11_entry_point.py @@ -0,0 +1,19 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +"""Print all modules exporting the entry point 'pytest11'.""" + +import pkg_resources + +plugins = sorted( + i.module_name for i in pkg_resources.iter_entry_points("pytest11")) + +print("\n".join(plugins)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkg_resource_res_string.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkg_resource_res_string.py new file mode 100644 index 0000000000000000000000000000000000000000..b68cc4206874e5759e635534abac20ec209ce81b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkg_resource_res_string.py @@ -0,0 +1,27 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +# With module 'pkg_resources' it should not matter if a file is stored +# on file system, in zip archive or bundled with frozen app. +import pkg_resources as res +import pkg3 + +expected_data = 'This is data text for testing the packaging module data.'.encode('ascii') + +# With frozen app the resources is available in directory +# os.path.join(sys._MEIPASS, 'pkg3/sample-data.txt') +data = res.resource_string(pkg3.__name__, 'sample-data.txt') +if not data: + raise SystemExit('Error: Could not read data with pkgutil.get_data().') + +if data.strip() != expected_data: + raise SystemExit('Error: Read data is wrong: %r' % data) +print('Okay: Resource data read.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkgutil_get_data.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkgutil_get_data.py new file mode 100644 index 0000000000000000000000000000000000000000..3def5841a174cc2ee5bd4b5eccadbc2cf83aab2e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkgutil_get_data.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 pkgutil +import pkg3 + +expected_data = 'This is data text for testing the packaging module data.'.encode('ascii') + +data = pkgutil.get_data('pkg3', 'sample-data.txt') +if not data: + raise SystemExit('Error: Could not read data with pkgutil.get_data().') + +if data.strip() != expected_data: + raise SystemExit('Error: Read data is wrong: %r' % data) +print('Okay: Resource data read.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkgutil_get_data__main__.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkgutil_get_data__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..5a79feaab8f6e0fdd463fb87acb7d0eb21b9cd9f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pkgutil_get_data__main__.py @@ -0,0 +1,22 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 pkgutil + +expected_data = 'This is data text for testing the packaging module data.'.encode('ascii') + +data = pkgutil.get_data('__main__', 'pkg3/sample-data.txt') +if not data: + raise SystemExit('Error: Could not read data with pkgutil.get_data().') + +if data.strip() != expected_data: + raise SystemExit('Error: Read data is wrong: %r' % data) +print('Okay: Resource data read.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_absolute_ld_library_path.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_absolute_ld_library_path.py new file mode 100644 index 0000000000000000000000000000000000000000..200bfac7f41d7afc9f873ccde94611bd44680b17 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_absolute_ld_library_path.py @@ -0,0 +1,41 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys + +# Bootloader should override set LD_LIBRARY_PATH. +# For Linux, Solaris, AIX and other Unixes only +libpath = sys._MEIPASS + +# The name of the environment variable used to define the path where the +# OS should search for dynamic libraries. +if sys.platform.startswith('aix'): + libpath_var_name = 'LIBPATH' +else: + libpath_var_name = 'LD_LIBRARY_PATH' + +orig_libpath = os.environ.get(libpath_var_name + "_ORIG") +if orig_libpath: + libpath += ':' + orig_libpath +print(('LD_LIBRARY_PATH expected: ' + libpath)) + +libpath_from_env = os.environ.get(libpath_var_name) +print(('LD_LIBRARY_PATH current: ' + libpath_from_env)) + +if not libpath == libpath_from_env: + raise SystemExit("Expected LD_LIBRARY_PATH doesn't match.") + +# LD_LIBRARY_PATH set by bootloader should ba an absolute path +part1 = libpath_from_env.split(os.pathsep, 1)[0] +print('First path element:', part1) +if not os.path.isabs(part1): + raise SystemExit("LD_LIBRARY_PATH is not an absolute path.") diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_absolute_python_path.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_absolute_python_path.py new file mode 100644 index 0000000000000000000000000000000000000000..a31adbc219038f2ce70c3d327d88247fc2a869fc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_absolute_python_path.py @@ -0,0 +1,39 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# sys.path should contain absolute paths. +# With relative paths frozen application will +# fail to import modules when currect working +# directory is changed. + + +import os + +import sys +import tempfile + +print('sys.stderr.encoding:', sys.stderr.encoding) +print('sys.path', sys.path) +print('CWD:', repr(os.getcwd())) + +# Change working directory. +os.chdir(tempfile.gettempdir()) +print('Changing working directory...') +print('CWD:', repr(os.getcwd())) + +# Try import a module. It should fail +try: + for pth in sys.path: + if not os.path.isabs(pth): + raise SystemExit('ERROR: sys.path not absolute') + import datetime +except: + raise SystemExit('ERROR: sys.path not absolute') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_app_with_plugin.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_app_with_plugin.py new file mode 100644 index 0000000000000000000000000000000000000000..fba5440090e0cb3f01e5c9c384193097c935cb43 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_app_with_plugin.py @@ -0,0 +1,67 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 little sample application generates a plugin on the fly, +# and then tries to import it. + + +import os +import sys + + +# We first import a static plugin; the application might have +# certain plugins that it always loads. +try: + print('Attempting to import static_plugin...') + mdl = __import__('static_plugin') +except ImportError: + raise SystemExit('Failed to import the static plugin.') + + +plugin_contents = """ +print('DYNAMIC PLUGIN IMPORTED.') +print('This is some user-generated plugin that does not exist until') +print(' the application starts and other modules in the directory') +print(' are imported (like the static_plugin).') +""" + + +# Create the dynamic plugin in the same directory as the executable. +if hasattr(sys, 'frozen'): + program_dir = os.path.abspath(sys.prefix) +else: + program_dir = os.path.dirname(os.path.abspath(__file__)) +plugin_filename = os.path.join(program_dir, 'dynamic_plugin.py') +fp = open(plugin_filename, 'w') +fp.write(plugin_contents) +fp.close() + + +# Try import dynamic plugin. +is_error = False +try: + print('Attempting to import dynamic_plugin...') + mdl = __import__('dynamic_plugin') +except ImportError: + is_error = True + + +# Clean up. Remove files dynamic_plugin.py[c] +for f in (plugin_filename, plugin_filename + 'c'): + try: + os.remove(plugin_filename) + except OSError: + pass + +if is_error: + # Raise exeption. + raise SystemExit('Failed to import the dynamic plugin.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_arbitrary_ext.foo b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_arbitrary_ext.foo new file mode 100644 index 0000000000000000000000000000000000000000..41d5df29c9586328d17ac22bb14bedd52ee188e7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_arbitrary_ext.foo @@ -0,0 +1,5 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Verify that PyInstaller can load a script from a file with an arbitrary +# extension, not just from ``.py`` files. +print('hello') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_c_extension.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_c_extension.py new file mode 100644 index 0000000000000000000000000000000000000000..c157307d7028136d519dcc2fdc18cf9048ece546 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_c_extension.py @@ -0,0 +1,39 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +# In dist directory are Python C-extension file names like module.submodule.so +# E.g. ./simplejson/_speedups.so -> ./simplejson._speedups.so + +import os +import sys + +# List of suffixes for Python C extension modules. +from importlib.machinery import EXTENSION_SUFFIXES + +from simplejson import _speedups + +modpath = os.path.join(sys.prefix, 'simplejson._speedups') +frozen_modpath = _speedups.__file__ + +print('Module path expected:', modpath, '+ ext', file=sys.stderr) +print('Module path current:', frozen_modpath, file=sys.stderr) + +# Filename extensions can include several dots (e.g. +# '.cpython-33m.so'), so we can not simply use os.path.splitext(), but +# have to loop over all possible extensions. +for ext in EXTENSION_SUFFIXES: + if modpath + ext == frozen_modpath: + print(' matched:', modpath + ext, file=sys.stderr) + break +else: + if not getattr(sys, 'frozen', False): + raise SystemExit('This script only works corretly when frozen') + raise SystemExit('Python C-extension file name is not correct.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_codecs.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_codecs.py new file mode 100644 index 0000000000000000000000000000000000000000..8c67ccbb9c498d0275216fe5c6d7e3e46620a44f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_codecs.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Encode unicode string into utf8 and then back to unicode strings. +# The original string and the result should be equal. + +import codecs + + +str_a = 'foo bar fóó bář, fěě, Äěž' +str_a_utf8 = codecs.getencoder('utf-8')(str_a)[0] +str_b = codecs.getdecoder('utf-8')(str_a_utf8)[0] + +print('codecs working: %s' % (str_a == str_b)) +assert str_a == str_b diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_filename.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_filename.py new file mode 100644 index 0000000000000000000000000000000000000000..5fb0dab6acff09dfc1a0b504db7b33325787b745 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_filename.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import os + +assert os.path.dirname(__file__) == sys._MEIPASS +assert os.path.basename(__file__) == 'pyi_filename.py' diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_future.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_future.py new file mode 100644 index 0000000000000000000000000000000000000000..e15abd683edbab15f0ac5f518589ad609d0742d8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_future.py @@ -0,0 +1,109 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 test code is taken from the example code for the `future` library, with +# a few modifications to allow execution on 32-bit platforms. +# http://python-future.org/overview.html#code-examples + +from builtins import (bytes, str, open, super, range, + zip, round, input, int, pow, object) + +# Backported Py3 bytes object +b = bytes(b'ABCD') +assert list(b) == [65, 66, 67, 68] +assert repr(b) == "b'ABCD'" +# These raise TypeErrors: +try: + b + u'EFGH' +except TypeError: + pass +else: + assert False, "`bytes + str` did not raise TypeError" + +try: + bytes(b',').join([u'Fred', u'Bill']) +except TypeError: + pass +else: + assert False, "`bytes.join([str, str])` did not raise TypeError" + +# Backported Py3 str object +s = str(u'ABCD') +assert s != bytes(b'ABCD') +assert isinstance(s.encode('utf-8'), bytes) +assert isinstance(b.decode('utf-8'), str) +assert repr(s) == "'ABCD'" # consistent repr with Py3 (no u prefix) +# These raise TypeErrors: +try: + bytes(b'B') in s +except TypeError: + pass +else: + assert False, "`bytes in str` did not raise TypeError" + +try: + s.find(bytes(b'A')) +except TypeError: + pass +else: + assert False, "`str.find(bytes)` did not raise TypeError" + + +# New zero-argument super() function: +class VerboseList(list): + def append(self, item): + print('Adding an item') + super().append(item) + +# Fix: this fails on 32-bit Python. The traceback:: +# +# E:\pyinstaller>python tests\functional\scripts\pyi_future.py +# Traceback (most recent call last): +# File "tests\functional\scripts\pyi_future.py", line 66, in +# for i in range(10**15)[:10]: +# File "C:\Users\bjones\Downloads\WinPython-32bit-2.7.10.3\python-2.7.10\lib\site-packages\future\types\newrange.py", line 122, in __getitem__ +# return self.__getitem_slice(index) +# File "C:\Users\bjones\Downloads\WinPython-32bit-2.7.10.3\python-2.7.10\lib\site-packages\future\types\newrange.py", line 134, in __getitem_slice +# scaled_indices = (self._step * n for n in slce.indices(self._len)) +# OverflowError: cannot fit 'long' into an index-sized integer +# +# So, pick a smaller (32-bit capable) range to iterate over. +# +# New iterable range object with slicing support +for i in range(2**30)[:10]: + pass + +# Other iterators: map, zip, filter +my_iter = zip(range(3), ['a', 'b', 'c']) +assert my_iter != list(my_iter) + +# The round() function behaves as it does in Python 3, using +# "Banker's Rounding" to the nearest even last digit: +assert round(0.1250, 2) == 0.12 + +# pow() supports fractional exponents of negative numbers like in Py3: +z = pow(-1, 0.5) + +# Compatible output from isinstance() across Py2/3: +assert isinstance(2**64, int) # long integers +assert isinstance(u'blah', str) +assert isinstance('blah', str) # only if unicode_literals is in effect + +# Py3-style iterators written as new-style classes (subclasses of +# future.types.newobject) are automatically backward compatible with Py2: +class Upper(object): + def __init__(self, iterable): + self._iter = iter(iterable) + def __next__(self): # note the Py3 interface + return next(self._iter).upper() + def __iter__(self): + return self +assert list(Upper('hello')) == list('HELLO') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_get_meipass_value.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_get_meipass_value.py new file mode 100644 index 0000000000000000000000000000000000000000..e2852906a115e25848cbdec89f229da1d7a55665 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_get_meipass_value.py @@ -0,0 +1,51 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# Bootloader unsets _MEIPASS2 for child processes so that if the program +# invokes another PyInstaller one-file program as subprocess, this +# subprocess will not fooled into thinking that it is already unpacked. +# +# This test checks if it is really unset in a subprocess. + + +import os +import subprocess +import sys + + +def _get_meipass_value(): + if sys.platform.startswith('win'): + command = 'echo %_MEIPASS2%' + else: + command = 'echo $_MEIPASS2' + + stdout = subprocess.check_output(command, shell=True) + meipass = stdout.strip() + + # Win32 fix. + if meipass.startswith(b'%'): + meipass = '' + + + return meipass + + +meipass = _get_meipass_value() + + + +print(meipass) +print(('_MEIPASS2 value: %s' % sys._MEIPASS)) + + +if meipass: + raise SystemExit('Error: _MEIPASS2 env variable available in subprocess.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_getfilesystemencoding.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_getfilesystemencoding.py new file mode 100644 index 0000000000000000000000000000000000000000..f7cbc7c49f95c29960ab6fd5b6caacefeefbb0a4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_getfilesystemencoding.py @@ -0,0 +1,37 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys + + +frozen_encoding = str(sys.getfilesystemencoding()) + + +# For various OS is encoding different. +if sys.platform.startswith('win'): + # See PEP 529 for more information. + encoding = 'utf-8' +# On Mac OS X the value should be still the same. +elif sys.platform.startswith('darwin'): + encoding = 'utf-8' +# On Linux and other unixes it should be usually 'utf-8' +else: + encoding = 'utf-8' + + +print('Encoding expected: ' + encoding) +print('Encoding current: ' + frozen_encoding) + + +if not frozen_encoding == encoding: + raise SystemExit('Frozen encoding %s is not the same as unfrozen %s.' % + (frozen_encoding, encoding)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_helloworld.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_helloworld.py new file mode 100644 index 0000000000000000000000000000000000000000..8a4751fe737b1eaf4352cde39ad8d3c39cf76bb6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_helloworld.py @@ -0,0 +1,13 @@ +# ---------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +# ---------------------------------------------------------------------------- + + +print('Hello Python!') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pkg_without_hook_for_pkg.sub1.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pkg_without_hook_for_pkg.sub1.py new file mode 100644 index 0000000000000000000000000000000000000000..87cf0e0e0dc5894edf04213d3550e1c5af03c7bc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pkg_without_hook_for_pkg.sub1.py @@ -0,0 +1,26 @@ + +hiddenimports = ['pkg_without_hook_for_pkg.sub1.sub11'] + +NAME = 'pkg_without_hook_for_pkg.sub1' + +# self-test for this test-case: +import PyInstaller.hooks +import os +# TODO For Python 3 replace this by importlib, imp is deprecated +import imp + +# 1. ensure self-test is working by searching for _this_ hook +hookmodnm = 'hook-' + NAME +searchpath = PyInstaller.hooks.__path__ + [os.path.dirname(__file__)] +assert imp.find_module(hookmodnm, searchpath) is not None, "Error in the hook's self-test" + +# 2. The actual self-test: there must be no hook for the parent module +hookmodnm = 'hook-pkg_without_hook_for_pkg' +try: + imp.find_module(hookmodnm, searchpath) + raise Exception('Self-test of hook %s failed: hook for parent exists' + % NAME) +except ImportError as e: + if e.name != hookmodnm: + raise Exception('Self-test of hook %s failed: hook for parent exists ' + 'and has import errors.' % NAME) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_collect_submodules_mod.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_collect_submodules_mod.py new file mode 100644 index 0000000000000000000000000000000000000000..3af05106b43ea53cfc7586269246774bc5315178 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_collect_submodules_mod.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_submodules + +hiddenimports = collect_submodules('pyi_testmod_relimp') + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_testmod_metapath1.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_testmod_metapath1.py new file mode 100644 index 0000000000000000000000000000000000000000..8e3597dffc9f8004b37988f69d1fd06b7b430070 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_testmod_metapath1.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_submodules + +# pyi_testmod_metapath1._vendor is not imported directly and won't be +# found by modulegraph. So, explicitly include this sub-package. +hiddenimports = collect_submodules('pyi_testmod_metapath1._vendor') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_testmod_path.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_testmod_path.py new file mode 100644 index 0000000000000000000000000000000000000000..b3a1e558d176b1493636f96c4544cb4fba2c1dbc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/hook-pyi_testmod_path.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 PyInstaller.utils.hooks import collect_data_files + +# Since pyi_testmod_path/b lacks an __init__, it's not a module and won't be +# found by modulegraph. So, include its contents in the filesystem. +datas = collect_data_files('pyi_testmod_path', True, 'a') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/pre_safe_import_module/hook-alias_name.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/pre_safe_import_module/hook-alias_name.py new file mode 100644 index 0000000000000000000000000000000000000000..858204eadbb7f14c410f58b4d2c1a01d54077f43 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/pre_safe_import_module/hook-alias_name.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, PyInstaller Development Team. +# +# Distributed under the terms of the GNU General Public License with exception +# for distributing bootloader. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + + +def pre_safe_import_module(api): + api.add_alias_module('pyi_testmod_submodule_from_aliased_pkg', + 'alias_name') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub.py new file mode 100644 index 0000000000000000000000000000000000000000..cd27cd365706c80eb31020b5ec4d5302dcde3abd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub.py @@ -0,0 +1,74 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional test exercising PyPubSub's default protocol. + +The default protocol conditionally depends on the current wxPython version. If: + +* **wxPython 2.8** or older is available, the default protocol is version 1 + (referred to elsewhere as either `pubsub1` or `v1`). +* **wxPython 2.9** or newer is available, the default protocol is the version 3 + `kwargs` protocol. + +Hence, this test is identical to explicitly importing the setup package for the +default protocol specific to the current wxPython version (e.g., `setupv1` for +wxPython 2.8 or older) _before_ the existing import from `wx.lib.pubsub` below. + + +NOTE: "wx.lib.pubsub.__init__" on 2.8 tries to use `imp.find_loader` +to find out if the module is importable; because `imp.find_loader` does not honor +our FrozenImporter path hook, it always returns None for *any* frozen module. This +means 2.8 never uses the v1 API when frozen, a deviation from its non-frozen behavior. + +We work around this in the wx.lib.pubsub hook by including `autosetuppubsubv1.py` +as a data file instead of a python module, which stores it as a plain file in the +exe folder (or temp folder) and makes it visible to `imp.find_loader`. +""" + +# Attempt to import the placeholder "wx.lib.pubsub.autosetuppubsubv1" module. +# We can't use `find_loader` to locate it because it may be present on both +# wxPython 2.8 and 2.9; version 2.9 explicitly raises ImportError to +# pretend it is not found. For whatever reason. +# +# Importing this module has no side effects; it either raises ImportError +# or does nothing. +try: + import wx.lib.pubsub.autosetuppubsubv1 +# If that failed, the current version of wxPython is 2.9 or newer, in which case +# the default protocol is the version 3 "kwargs" protocol. +except ImportError: + from wx.lib.pubsub import pub as Publisher + print('wxPython 2.9 or newer detected.') + + def on_message(number): + print('Message received.') + if not number == 762: + raise SystemExit( + 'Message data "762" expected but received "%s".' % str(number)) + + Publisher.subscribe(on_message, 'topic.subtopic') + Publisher.sendMessage('topic.subtopic', number=762) +# Else that succeeded, implying the current version of wxPython to be 2.8 or +# older, in which case the default protocol is the version 1 protocol. For +# details on why this works, see "wx.lib.pubsub.__init__" in wxPython 2.8. +else: + from wx.lib.pubsub import Publisher + print('wxPython 2.8 or older detected.') + + def on_message(message): + print('Message received.') + if not message.data == 762: + raise SystemExit( + 'Message data "762" expected but received "%s".' % str(message.data)) + + Publisher.subscribe(on_message, 'topic.subtopic') + Publisher.sendMessage('topic.subtopic', 762) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub_setuparg1.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub_setuparg1.py new file mode 100644 index 0000000000000000000000000000000000000000..4e6309318dd2020fdb87a08157180f49ca687a50 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub_setuparg1.py @@ -0,0 +1,27 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional test exercising the non-default protocol `arg1` of version 3 of the +PyPubSub API. +""" + +from wx.lib.pubsub import setuparg1 +from wx.lib.pubsub import pub as Publisher + +def on_message(message): + print('Message received.') + if not message.data == 762: + raise SystemExit( + 'Message data "762" expected but received "%s".' % str(message.data)) + +Publisher.subscribe(on_message, 'topic.subtopic') +Publisher.sendMessage('topic.subtopic', 762) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub_setupkwargs.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub_setupkwargs.py new file mode 100644 index 0000000000000000000000000000000000000000..f20b6067d1165629aa46e0caa6d9be3b5bc45fb1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_hooks/wx_lib_pubsub_setupkwargs.py @@ -0,0 +1,27 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional test exercising the default protocol `kwargs` of version 3 of the +PyPubSub API. +""" + +from wx.lib.pubsub import setupkwargs +from wx.lib.pubsub import pub as Publisher + +def on_message(number): + print('Message received.') + if not number == 762: + raise SystemExit( + 'Message data "762" expected but received "%s".' % str(number)) + +Publisher.subscribe(on_message, 'topic.subtopic') +Publisher.sendMessage('topic.subtopic', number=762) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_import_pyqt5_uic_port.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_import_pyqt5_uic_port.py new file mode 100644 index 0000000000000000000000000000000000000000..235f0589a6ab6b3a7ddfd0f89be61a19722b7ea2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_import_pyqt5_uic_port.py @@ -0,0 +1,35 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 requires you hand-crafted PyQt5 package, since some +# distributions do not include PyQt5.uic.port_v3 for Python and the +# other way round. +# +# PyQt5.uic.port_v2.test raises an AssertionError if imported under +# Python 3. But the hook should prohibit the inclusion of this module, +# so an ImportError should be raised. So the ImportError is what we +# expect and we just ignore it. + + +import PyQt5 + +# Ensure it's our fake module +assert PyQt5.__pyinstaller_fake_module_marker__ == '__pyinstaller_fake_module_marker__' + +import PyQt5.uic.port_v3 +print(PyQt5.uic.port_v3.__path__) + +try: + import PyQt5.uic.port_v2 + print(PyQt5.uic.port_v2.__path__) +except ImportError: + print('PyQt5.uic.port_v2 not imported') + pass diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_issue_4141.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_issue_4141.py new file mode 100644 index 0000000000000000000000000000000000000000..168af62fd896d40389789c21ce13d3594804387f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_issue_4141.py @@ -0,0 +1,43 @@ +import os, sys +from pathlib import Path + +# Import the application's "app" package. +# This does not(!) contain a sub-module app.hook. +import app + +# Paths from #4141, where the script was called "main" +# dist/main/app.py +# p/plugin_1/app.py +# Paths here: +# dist/pyi_issue_4141/app.py +# my-plugins/plugin_11/app.py +# 123456789 +dist_main_len = len(os.path.join("dist", "main")) + +# Create some "plugins" in sub-directory "p" +# +# Each of the plugins contains a sub-module app.hook, which get +# imported by the plugin's main module. If PyInstaller picks up the +# application's "app" package instead of the plugin's (which is issue +# 4141), importing sub-module app.hook will fail. +# +plugins_dir = Path("p").absolute() +plugin_names = [chr(ord("a") + 10 + i) * (dist_main_len + i) + for i in range(5, -5, -1)] +print(plugin_names) + +for pn in plugin_names: + print(plugins_dir / pn / "app") + (plugins_dir / pn / "app").mkdir(parents=True, exist_ok=True) + (plugins_dir / pn / "__init__.py").write_text("from .app import hook") + (plugins_dir / pn / "app" / "hook.py").touch() +del pn + +sys.path.insert(0, str(plugins_dir)) +print("sys.path[0]:", sys.path[0]) + +for plugin in plugin_names: + mod = __import__(plugin) + print(mod) + # double check + assert mod.__file__ == str(plugins_dir / plugin / "__init__.py") diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_PIL_img_conversion.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_PIL_img_conversion.py new file mode 100644 index 0000000000000000000000000000000000000000..c73c72624e8eacf9ca56aef10deb9d6a849fc603 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_PIL_img_conversion.py @@ -0,0 +1,26 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import os + +import PIL.Image + + +# Disable "leaking" the installed version. +PIL.Image.__file__ = '/' + + +# Convert tiff to png. +basedir = sys._MEIPASS +im = PIL.Image.open(os.path.join(basedir, "tinysample.tiff")) +im.save(os.path.join(basedir, "tinysample.png")) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_PyQt5-uic.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_PyQt5-uic.py new file mode 100644 index 0000000000000000000000000000000000000000..86fe48e2eb9c62ab0c65e96605c1660e0e69a24e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_PyQt5-uic.py @@ -0,0 +1,39 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +# Library imports +# --------------- +import os + +# Third-party imports +# ------------------- +# Instead of creating a custom .spec file: inform PyInstaller of the +# hidden import of QtQuickWidgets, which is performed inside of uic.loadUi. +import PyQt5.QtQuickWidgets +# Other Qt imports used in the code below. +from PyQt5.QtWidgets import QApplication, QDialog +from PyQt5 import uic +from PyQt5.QtCore import QTimer + +# Local imports +# ------------- +from pyi_get_datadir import get_data_dir + +# Test code +# --------- +app = QApplication([]) +window = QDialog() +uic.loadUi(os.path.join(get_data_dir(), 'PyQt5_uic', 'PyQt5-uic.ui'), window) +window.show() +# Exit Qt when the main loop becomes idle. +QTimer.singleShot(0, app.exit) +# Run the main loop, displaying the WebKit widget. +app.exec_() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_requests.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_requests.py new file mode 100644 index 0000000000000000000000000000000000000000..2059d74b20cddeb4b0927fc1deb98ca8d41e5ab4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_requests.py @@ -0,0 +1,103 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 socket + +try: + import BaseHTTPServer + import SimpleHTTPServer +except ImportError: + import http.server as BaseHTTPServer + import http.server as SimpleHTTPServer + +import os +import ssl +import sys +import threading +import time + +import requests + +""" +Note: to re-create the server.pem file use the +following commands. + +cd /path/to/pyinstaller.git/tests/functional +openssl req -new -x509 -keyout data/requests/server.pem \ + -text -out data/requests/server.pem -days 36500 \ + -nodes -config data/requests/openssl.conf +""" + +if getattr(sys, 'frozen', False): + # we are running in a |PyInstaller| bundle + basedir = sys._MEIPASS +else: + # we are running in a normal Python environment + basedir = os.path.dirname(__file__) + + +SERVER_CERT = os.path.join(basedir, u"server.pem") + + +if not os.path.exists(SERVER_CERT): + raise SystemExit('Certificate-File %s is missing' % SERVER_CERT) + + +def main(): + + SERVER_PORT = 8443 + httpd = None + + # Since unit tests run in parallel, the port may be in use, so + # retry creating the server while incrementing the port number + while SERVER_PORT < 8493: # Max 50 retries + try: + # SSL server copied from here: + # http://www.piware.de/2011/01/creating-an-https-server-in-python/ + httpd = BaseHTTPServer.HTTPServer( + ('localhost', SERVER_PORT), + SimpleHTTPServer.SimpleHTTPRequestHandler) + except socket.error as e: + if e.errno == 98: # Address in use + SERVER_PORT += 1 + continue + else: + # Some other socket.error + raise + else: + # Success + break + else: + # Did not break from loop, so we ran out of retries + assert False, "Could not bind server port: all ports in use." + + httpd.socket = ssl.wrap_socket( + httpd.socket, certfile=SERVER_CERT, server_side=True + ) + + def ssl_server(): + httpd.serve_forever() + + # Start the SSL server + thread = threading.Thread(target=ssl_server) + thread.daemon = True + thread.start() + + # Wait a bit for the server to start + time.sleep(1) + + # Use requests to get a page from the server + requests.get( + u"https://localhost:{}".format(SERVER_PORT), + verify=SERVER_CERT) + # requests.get("https://github.com") + +if __name__ == '__main__': + main() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_sphinx.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_sphinx.py new file mode 100644 index 0000000000000000000000000000000000000000..7207b27da42af93352f4f3a989a0dca73ad71115 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_sphinx.py @@ -0,0 +1,45 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + +# Verify packaging of Sphinx, which relies on jinja2 and on docutils. Sphinx and +# docutils rely on data files in their module directories, which their +# respective hook scripts must find and copy. + +# Library imports +# --------------- +import sys +import os.path + +# Third-party imports +# ------------------- +import sphinx.cmd.build + +# Local imports +# ------------- +from pyi_get_datadir import get_data_dir + +sphinx_path = os.path.join(get_data_dir(), 'sphinx') + +# Invoke Sphinx. See +# http://sphinx-doc.org/invocation.html#invocation-of-sphinx-build for more +# details of the options below. +ret = sphinx.cmd.build.main([ + # Rebuild all files. + '-a', '-E', + # Produce html output. + '-b', 'html', + # Specify an output directory for data files. + '-d', os.path.join(sphinx_path, '_build', 'doctrees'), + # Specify the location of the source (index.rst). + sphinx_path, + # Build directory for the resulting HTML files. + os.path.join(sphinx_path, '_build', 'html') ]) +raise SystemExit(ret) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_tkinter.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_tkinter.py new file mode 100644 index 0000000000000000000000000000000000000000..e490f05cbd5867831785ffc8d5ce99c4d9e75df6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_lib_tkinter.py @@ -0,0 +1,50 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# Ensure environment variables TCL_LIBRARY and TK_LIBRARY are set properly. +# and data files are bundled. + + +import glob +import os + + +# In Python 3 module name is 'tkinter' +try: + from tkinter import * +except ImportError: + from Tkinter import * + + +def compare(test_name, expect, frozen): + expect = os.path.normpath(expect) + print(test_name) + print((' Expected: ' + expect)) + print((' Current: ' + frozen)) + print('') + # Path must match. + if not frozen == expect: + raise SystemExit('Data directory is not set properly.') + # Directory must exist. + if not os.path.exists(frozen): + raise SystemExit('Data directory does not exist.') + # Directory must contain some .tcl files and not to be empty. + if not len(glob.glob(frozen + '/*.tcl')) > 0: + raise SystemExit('Data directory does not contain .tcl files.') + + +tcl_dir = os.environ['TCL_LIBRARY'] +tk_dir = os.environ['TK_LIBRARY'] + + +compare('Tcl', os.path.join(sys.prefix, 'tcl'), tcl_dir) +compare('Tk', os.path.join(sys.prefix, 'tk'), tk_dir) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_load_dll_using_ctypes.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_load_dll_using_ctypes.py new file mode 100644 index 0000000000000000000000000000000000000000..d729e183003cc44ca7b50444e5be28de0cfda3bb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_load_dll_using_ctypes.py @@ -0,0 +1,35 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Library imports +# --------------- +import os +import sys +from ctypes import CDLL + +# Local imports +# ------------- +from pyi_get_datadir import get_data_dir + +# Library name based on platform. +if sys.platform.startswith('win32'): + name = 'ctypes_dylib.dll' +elif sys.platform.startswith("darwin"): + name = 'ctypes_dylib.dylib' +else: + name = 'ctypes_dylib.so' + +# Test resolving dynamic libraries loaded in Python code at runtime +# by Python module 'ctypes'. +tct = CDLL(os.path.join(get_data_dir(), 'ctypes_dylib', name)) +# The "dummy" function in ctypes_dylib returning value + 12. +assert tct.dummy(42) == (42 + 12) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_log_args.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_log_args.py new file mode 100644 index 0000000000000000000000000000000000000000..a9947491081f56e156b2ea7a68eff6adacea7919 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_log_args.py @@ -0,0 +1,25 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2019-2021, 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 sys + +if len(sys.argv) > 1: + basedir = os.path.dirname(sys.executable) + # if script is inside .app package + if os.path.basename(basedir) == 'MacOS': + basedir = os.path.abspath( + os.path.join(basedir, os.pardir, os.pardir, os.pardir)) + + logfile = os.path.join(basedir, 'args.log') + with open(logfile, 'w') as file: + for arg in sys.argv[1:]: + file.write(arg) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module__file__attribute.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module__file__attribute.py new file mode 100644 index 0000000000000000000000000000000000000000..f7cce1bc09f03d54bc28f136a42a1ebd7f5d6f36 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module__file__attribute.py @@ -0,0 +1,42 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# Test the value of the __file__ module attribute. +# In frozen mode it is for package set to +# +# sys.prefix/package/__init__.pyc +# sys.prefix/module.pyc + + +import os +import sys + +import shutil as module +import xml.sax as package + + +correct_mod = os.path.join(sys.prefix, 'shutil.pyc') +correct_pkg = os.path.join(sys.prefix, 'xml', 'sax', '__init__.pyc') + + +# Print. +print(('Actual mod.__file__: %s' % module.__file__)) +print(('Expected mod.__file__: %s' % correct_mod)) +print(('Actual pkg.__file__: %s' % package.__file__)) +print(('Expected pkg.__file__: %s' % correct_pkg)) + + +# Test correct values. +if not module.__file__ == correct_mod: + raise SystemExit('MODULE.__file__ attribute is wrong.') +if not package.__file__ == correct_pkg: + raise SystemExit('PACKAGE.__file__ attribute is wrong.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module_attributes.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module_attributes.py new file mode 100644 index 0000000000000000000000000000000000000000..eb30582f34ade2ab4b8f39f80b302be8b7b9bd44 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module_attributes.py @@ -0,0 +1,85 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# Compare attributes of ElementTree (cElementTree) module from frozen executable +# with ElementTree (cElementTree) module from standard python. + + +import copy +import os +import subprocess +import xml.etree.ElementTree as ET +import xml.etree.cElementTree as cET + +from pyi_testmod_gettemp import gettemp + +_pyexe_file = gettemp("python_exe.build") + +with open(_pyexe_file) as fp: + _lines = fp.readlines() + _pyexe = _lines[0].strip() + _env_path = _lines[2].strip() + + +def exec_python(pycode): + """ + Wrap running python script in a subprocess. + + Return stdout of the invoked command. + """ + # Environment variable 'PATH' has to be defined on Windows. + # Otherwise dynamic library pythonXY.dll cannot be found by + # Python executable. + env = copy.deepcopy(os.environ) + env['PATH'] = _env_path + out = subprocess.Popen([_pyexe, '-c', pycode], env=env, + stdout=subprocess.PIPE, shell=False).stdout.read() + # In Python 3 stdout is a byte array and must be converted to string. + out = out.decode('ascii').strip() + + return out + + +def compare(test_name, expect, frozen): + # Modules in Python 3 contain attr '__cached__' - add it to the frozen list. + if '__cached__' not in frozen: + frozen.append('__cached__') + frozen.sort() + frozen = str(frozen) + + print(test_name) + print(' Attributes expected: ' + expect) + print(' Attributes current: ' + frozen) + print('') + # Compare attributes of frozen module with unfronzen module. + if not frozen == expect: + raise SystemExit('Frozen module has no same attributes as unfrozen.') + + +# General Python code for subprocess. +subproc_code = """ +import {0} as myobject +lst = dir(myobject) +# Sort attributes. +lst.sort() +print(lst) +""" + +## Pure Python module. +_expect = exec_python(subproc_code.format('xml.etree.ElementTree')) +_frozen = dir(ET) +compare('ElementTree', _expect, _frozen) + +## C-extension Python module. +_expect = exec_python(subproc_code.format('xml.etree.cElementTree')) +_frozen = dir(cET) +compare('cElementTree', _expect, _frozen) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module_reload.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module_reload.py new file mode 100644 index 0000000000000000000000000000000000000000..ac8a0b14fd3fcbfedeeb766eb4e981452c29b1d2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_module_reload.py @@ -0,0 +1,56 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# PyInstaller always loads modules from the embedded archive before +# looking at sys.path. +# +# This tests creates module with the same name as the one in the +# embbedded archive. Python should always load module from the +# embedded archive. + + +# imp module is deprecated since Python 3.4. +try: + import importlib as imp + # Python 3.3 does not have function importlib.reload(). + if not hasattr(imp, 'reload'): + raise ImportError +except ImportError: + import imp +import os +import sys + + +# Create module. +txt = """ +x = %d +""" +mod_filename = os.path.join(sys._MEIPASS, 'data_reload.py') +with open(mod_filename, 'w') as f: + f.write(txt % 2) + + +# Import created module. +import data_reload +orig_x = data_reload.x +print(('data_reload.x is %s' % data_reload.x)) + + +# Modify code of module - increment x. +with open(mod_filename, 'w') as f: + f.write(txt % (data_reload.x + 1)) + +# Reload module. +imp.reload(data_reload) +print(('data_reload.x is now %s' % data_reload.x)) +# The value of 'x' should be the orig_x + 1. +assert data_reload.x == orig_x + 1 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..27915da541879234e680b0f52f1e128ce6f7c724 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess.py @@ -0,0 +1,36 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 multiprocessing +import sys + + +class SendeventProcess(multiprocessing.Process): + def __init__(self, resultQueue): + multiprocessing.Process.__init__(self) + self.resultQueue = resultQueue + self.start() + + def run(self): + print('SendeventProcess begins') + self.resultQueue.put((1, 2)) + print('SendeventProcess ends') + + +if __name__ == '__main__': + multiprocessing.freeze_support() + print('main begins') + resultQueue = multiprocessing.Queue() + sp = SendeventProcess(resultQueue) + assert resultQueue.get() == (1, 2) + print('get ends') + sp.join() + print('main ends') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess_forking.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess_forking.py new file mode 100644 index 0000000000000000000000000000000000000000..63f89404cc29d5689a8e10469f0fc56dda4b6a1f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess_forking.py @@ -0,0 +1,34 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys +import multiprocessing + + +class SendeventProcess(multiprocessing.Process): + def __init__(self, resultQueue): + self.resultQueue = resultQueue + + multiprocessing.Process.__init__(self) + self.start() + + def run(self): + print('SendeventProcess') + self.resultQueue.put((1, 2)) + print('SendeventProcess') + + +if __name__ == '__main__': + multiprocessing.freeze_support() + print('main') + resultQueue = multiprocessing.Queue() + SendeventProcess(resultQueue) + print('main') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess_pool.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess_pool.py new file mode 100644 index 0000000000000000000000000000000000000000..6c0f3e4dd01b41379562df84491c9dd07ce3ef93 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_multiprocess_pool.py @@ -0,0 +1,30 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multiprocessing + + +def f(x): + return x*x + + +if __name__ == '__main__': + multiprocessing.freeze_support() + # Start 4 worker processes. + pool = multiprocessing.Pool(processes=4) + print('Evaluate "f(10)" asynchronously.') + res = pool.apply_async(f, [10]) + print(res.get(timeout=1)) # prints "100" + print('Print "[0, 1, 4,..., 81]"') + print(pool.map(f, range(10))) + # this is old code, based on Python 2, not using Pool as a context, so we + # need to terminate the pool explicitly + pool.terminate() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_path_encoding.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_path_encoding.py new file mode 100644 index 0000000000000000000000000000000000000000..aa09ef952c09f5fdc5293305237228ae957166cc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_path_encoding.py @@ -0,0 +1,31 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 script is used by multiple tests. It checks that various paths set by the +# bootloader are usable filenames. + +import sys, os + +print("sys.executable:", ascii(sys.executable)) + +if not os.path.exists(sys.executable): + raise SystemExit("sys.executable does not exist.") + +print("sys.argv[0]:", ascii(sys.argv[0])) + +if not os.path.exists(sys.argv[0]): + raise SystemExit("sys.argv[0] does not exist.") + +print("sys._MEIPASS:", ascii(sys._MEIPASS)) + +if not os.path.exists(sys._MEIPASS): + raise SystemExit("sys._MEIPASS does not exist.") + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_pkg_resources_provider.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_pkg_resources_provider.py new file mode 100644 index 0000000000000000000000000000000000000000..bc96f08b1956f3719fba83ffc306221328b0d86d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_pkg_resources_provider.py @@ -0,0 +1,409 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2020, 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) +#----------------------------------------------------------------------------- +# +# A test script for validation of pkg_resources provider implementation. +# +# The test package has the following structure: +# +# pyi_pkgres_testpkg/ +# ├── a.py +# ├── b.py +# ├── __init__.py +# ├── subpkg1 +# │   ├── c.py +# │   ├── data +# │   │   ├── entry1.txt +# │   │   ├── entry2.md +# │   │   ├── entry3.rst +# │   │   └── extra +# │   │   └── extra_entry1.json +# │   ├── d.py +# │   └── __init__.py +# ├── subpkg2 +# │   ├── __init__.py +# │   ├── mod.py +# │   └── subsubpkg21 +# │   ├── __init__.py +# │   └── mod.py +# └── subpkg3 +# ├── _datafile.json +# └── __init__.py +# +# When run as unfrozen script, this script can be used to check the +# behavior of "native" providers that come with pkg_resources, e.g., +# DefaultProvider (for regular packages) and ZipProvider (for eggs). +# +# When run as a frozen application, this script validates the behavior +# of the frozen provider implemented by PyInstaller. Due to transitivity +# of test results, this script running without errors both as a native +# script and as a frozen application serves as proof of conformance for +# the PyInstaller's provider. +# +# Wherever the behavior between the native providers is inconsistent, +# we allow the same leeway for the PyInstaller's frozen provider. + +import sys +from pkg_resources import resource_exists, resource_isdir, resource_listdir +from pkg_resources import get_provider, DefaultProvider, ZipProvider + +pkgname = 'pyi_pkgres_testpkg' + +# Identify provider type +provider = get_provider(pkgname) +is_default = isinstance(provider, DefaultProvider) +is_zip = isinstance(provider, ZipProvider) +is_frozen = getattr(sys, 'frozen', False) + +assert is_default or is_zip or is_frozen, "Unsupported provider type!" + + +######################################################################## +# Validate behavior of resource_exists() # +######################################################################## +# Package's directory +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider returns True +ret = resource_exists(pkgname, '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Package's directory, with empty path +assert resource_exists(pkgname, '') + +# Subpackage's directory (relative to main package): +assert resource_exists(pkgname, 'subpkg1') +assert resource_exists(pkgname, 'subpkg2') +assert resource_exists(pkgname, 'subpkg2/subsubpkg21') +assert resource_exists(pkgname, 'subpkg3') + +# Subpackage's directory (relative to subpackage itself): +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider returns True +ret = resource_exists(pkgname + '.subpkg1', '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Subpackage's directory (relative to subpackage itself), with empty path: +assert resource_exists(pkgname + '.subpkg1', '') + +# Data directory in subpackage +assert resource_exists(pkgname, 'subpkg1/data') +assert resource_exists(pkgname + '.subpkg1', 'data') + +# Subdirectory in data directory +assert resource_exists(pkgname, 'subpkg1/data/extra') +assert resource_exists(pkgname + '.subpkg1', 'data/extra') + +# File in data directory +assert resource_exists(pkgname, 'subpkg1/data/entry1.txt') + +# Deeply nested data file +assert resource_exists(pkgname, 'subpkg1/data/extra/extra_entry1.json') + +# A non-existant file/directory - should return False +assert not resource_exists(pkgname, 'subpkg1/non-existant') + +# A source script file in package +# > PyiFrozenProvider returns False because frozen application does +# not contain source files +ret = resource_exists(pkgname, '__init__.py') +assert (not is_frozen and ret) or \ + (is_frozen and not ret) + +# Parent of pacakge's top-level directory +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider disallows jumping to parent, and returns False +# NOTE: using .. in path is deprecated (since setuptools 40.8.0) and +# will raise exception in a future release +ret = resource_exists(pkgname, '..') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and not ret) + +# Parent of subpackage's directory +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider disallows jumping to parent, and returns False +# NOTE: using .. in path is deprecated (since setuptools 40.8.0) and +# will raise exception in a future release +ret = resource_exists(pkgname + '.subpkg1', '..') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and not ret) + +# Submodule in main package +ret = resource_exists(pkgname + '.a', '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Submodule in main package, with empty path +assert resource_exists(pkgname + '.a', '') + +# Submodule in subpackage +ret = resource_exists(pkgname + '.subpkg1.c', '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Submodule in subpackage, with empty path +assert resource_exists(pkgname + '.subpkg1.c', '') + + +######################################################################## +# Validate behavior of resource_isdir() # +######################################################################## +# Package's directory +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider returns True +ret = resource_isdir(pkgname, '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Package's directory, with empty path +assert resource_isdir(pkgname, '') + +# Subpackage's directory (relative to main pacakge): +# * both DefaultProvider and ZipProvider return True +assert resource_isdir(pkgname, 'subpkg1') +assert resource_isdir(pkgname, 'subpkg2') +assert resource_isdir(pkgname, 'subpkg2/subsubpkg21') +assert resource_isdir(pkgname, 'subpkg3') + +# Subpackage's directory (relative to subpackage itself): +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider returns True +ret = resource_isdir(pkgname + '.subpkg1', '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Subpackage's directory (relative to subpackage itself), with empty path: +assert resource_isdir(pkgname + '.subpkg1', '') + +# Data directory in subpackage +assert resource_isdir(pkgname, 'subpkg1/data') +assert resource_isdir(pkgname + '.subpkg1', 'data') + +# Subdirectory in data directory +assert resource_isdir(pkgname, 'subpkg1/data/extra') +assert resource_isdir(pkgname + '.subpkg1', 'data/extra') + +# File in data directory - should return False +assert not resource_isdir(pkgname, 'subpkg1/data/entry1.txt') + +# Deeply nested data file - should return False +assert not resource_isdir(pkgname, 'subpkg1/data/extra/extra_entry1.json') + +# A non-existant file-directory - should return False +assert not resource_isdir(pkgname, 'subpkg1/non-existant') + +# A source script file in package - should return False +# NOTE: PyFrozenProvider returns False because the file does not +# exist. +assert not resource_isdir(pkgname, '__init__.py') + +# Parent of package's top-level directory +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider disallows jumping to parent, and returns False +# NOTE: using .. in path is deprecated (since setuptools 40.8.0) and +# will raise exception in a future release +ret = resource_isdir(pkgname, '..') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and not ret) + +# Parent of subpacakge's directory +# * DefaultProvider returns True +# * ZipProvider returns False +# > PyiFrozenProvider disallows jumping to parent, and returns False +# NOTE: using .. in path is deprecated (since setuptools 40.8.0) and +# will raise exception in a future release +ret = resource_isdir(pkgname + '.subpkg1', '..') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and not ret) + +# Submodule in main package +ret = resource_isdir(pkgname + '.a', '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Submodule in main package, with empty path +assert resource_isdir(pkgname + '.a', '') + +# Submodule in subpackage +ret = resource_isdir(pkgname + '.subpkg1.c', '.') +assert (is_default and ret) or \ + (is_zip and not ret) or \ + (is_frozen and ret) + +# Submodule in subpackage, with empty path +assert resource_isdir(pkgname + '.subpkg1.c', '') + + +######################################################################## +# Validate behavior of resource_listdir() # +######################################################################## +# A helper for resource_listdir() tests. +def _listdir_test(pkgname, path, expected): + # For frozen application, remove .py files from expected results + if is_frozen: + expected = [x for x in expected if not x.endswith('.py')] + # List the content + content = resource_listdir(pkgname, path) + # Ignore pycache + if '__pycache__' in content: + content.remove('__pycache__') + assert sorted(content) == sorted(expected) + + +# List package's top-level directory +# * DefaultProvider lists the directory +# * ZipProvider returns empty list +# > PyiFrozenProvider lists the directory, but does not provide source +# .py files +if is_zip: + expected = [] +else: + expected = ['__init__.py', 'a.py', 'b.py', 'subpkg1', 'subpkg2', 'subpkg3'] +_listdir_test(pkgname, '.', expected) + +# List package's top-level directory, with empty path +# > PyiFrozenProvider lists the directory, but does not provide source +# .py files +expected = ['__init__.py', 'a.py', 'b.py', 'subpkg1', 'subpkg2', 'subpkg3'] +_listdir_test(pkgname, '', expected) + +# List subpackage's directory (relative to main package) +# > PyiFrozenProvider lists the directory, but does not provide source +# .py files +expected = ['__init__.py', 'c.py', 'd.py', 'data'] +_listdir_test(pkgname, 'subpkg1', expected) + +# List data directory in subpackage (relative to main package) +expected = ['entry1.txt', 'entry2.md', 'entry3.rst', 'extra'] +_listdir_test(pkgname, 'subpkg1/data', expected) + +# List data directory in subpackage (relative to subpackage itself) +expected = ['entry1.txt', 'entry2.md', 'entry3.rst', 'extra'] +_listdir_test(pkgname + '.subpkg1', 'data', expected) + +# List data in subdirectory of data directory in subpackage +expected = ['extra_entry1.json'] +_listdir_test(pkgname + '.subpkg1', 'data/extra', expected) + +# Attempt to list a file (existing resource but not a directory). +# * DefaultProvider raises NotADirectoryError +# * ZipProvider returns empty list +# > PyiFrozenProvider returns empty list +try: + content = resource_listdir(pkgname + '.subpkg1', 'data/entry1.txt') +except NotADirectoryError: + assert is_default +except Exception: + raise +else: + assert (is_zip or is_frozen) and content == [] + +# Attempt to list an non-existant directory in main package. +# * DefaultProvider raises FileNotFoundError +# * ZipProvider returns empty list +# > PyiFrozenProvider returns empty list +try: + content = resource_listdir(pkgname, 'non-existant') +except FileNotFoundError: + assert is_default +except Exception: + raise +else: + assert (is_zip or is_frozen) and content == [] + +# Attempt to list an non-existant directory in subpackage +# * DefaultProvider raises FileNotFoundError +# * ZipProvider returns empty list +# > PyiFrozenProvider returns empty list +try: + content = resource_listdir(pkgname + '.subpkg1', 'data/non-existant') +except FileNotFoundError: + assert is_default +except Exception: + raise +else: + assert (is_zip or is_frozen) and content == [] + +# Attempt to list pacakge's parent directory +# * DefaultProvider actually lists the parent directory +# * ZipProvider returns empty list +# > PyiFrozenProvider disallows jumping to parent, and returns empty list +# NOTE: using .. in path is deprecated (since setuptools 40.8.0) and +# will raise exception in a future release +content = resource_listdir(pkgname, '..') +assert (is_default and pkgname in content) or \ + (is_zip and content == []) or \ + (is_frozen and content == []) + +# Attempt to list subpackage's parent directory +# * DefaultProvider actually lists the parent directory +# * ZipProvider returns empty list +# > PyiFrozenProvider disallows jumping to parent, and returns False +# NOTE: using .. in path is deprecated (since setuptools 40.8.0) and +# will raise exception in a future release +if is_default: + expected = ['__init__.py', 'a.py', 'b.py', 'subpkg1', 'subpkg2', 'subpkg3'] +else: + expected = [] +_listdir_test(pkgname + '.subpkg1', '..', expected) + +# Attempt to list directory of subpackage that has no data files or +# directories (relative to main package) +expected = ['__init__.py', 'mod.py', 'subsubpkg21'] +_listdir_test(pkgname, 'subpkg2', expected) + +# Attempt to list directory of subpackage that has no data files or +# directories (relative to subpackage itself) +expected = ['__init__.py', 'mod.py', 'subsubpkg21'] +_listdir_test(pkgname + '.subpkg2', '', expected) # empty path! + +# Attempt to list directory of subsubpackage that has no data +# files/directories (relative to main package) +expected = ['__init__.py', 'mod.py'] +_listdir_test(pkgname, 'subpkg2/subsubpkg21', expected) + +# Attempt to list directory of subsubpackage that has no data +# files/directories (relative to parent subpackage) +expected = ['__init__.py', 'mod.py'] +_listdir_test(pkgname + '.subpkg2', 'subsubpkg21', expected) + +# Attempt to list directory of subsubpackage that has no data +# files/directories (relative to subsubpackage itself) +expected = ['__init__.py', 'mod.py'] +_listdir_test(pkgname + '.subpkg2.subsubpkg21', '', expected) # empty path! + +# Attempt to list submodule in main package - should give the same results +# as listing the package itself +assert sorted(resource_listdir(pkgname + '.a', '')) == \ + sorted(resource_listdir(pkgname, '')) # empty path! + +# Attempt to list submodule in subpackage - should give the same results +# as listing the subpackage itself +assert sorted(resource_listdir(pkgname + '.subpkg1.c', '')) == \ + sorted(resource_listdir(pkgname + '.subpkg1', '')) # empty path! diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_pyqt5_log_events.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_pyqt5_log_events.py new file mode 100644 index 0000000000000000000000000000000000000000..d0a2773022753378c53c4239aed1a87302351916 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_pyqt5_log_events.py @@ -0,0 +1,86 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2019-2021, 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 json +import os +import sys + +from PyQt5.QtCore import QObject, QEvent, QTimer +from PyQt5.QtWidgets import QWidget, QApplication, qApp + + +class EventHandler(QObject): + + def __init__(self, logfile, parent=None): + super().__init__(parent=parent) + assert isinstance(logfile, str) and logfile + self.logfile = logfile + self.activate_count = 0 + + def eventFilter(self, obj, event): + """ This event filter just logs the URLs it receives as FileOpen + events to self.logfile """ + try: + if event.type() == QEvent.FileOpen: + with open(self.logfile, 'a') as file: + file.write("url {}\n".format(event.url().toString())) + file.write("activate_count {}\n" + .format(self.activate_count)) + qApp.quit() # Tell app to quit after receiving this event + return True + elif event.type() == QEvent.ApplicationActivate: + self.activate_count += 1 + except Exception as e: + print("Caught exception in eventFilter exception: " + repr(e), + file=sys.stderr) + return super().eventFilter(obj, event) + + def log_started(self): + try: + with open(self.logfile, 'wt') as file: + file.write("started {}\n" + .format(json.dumps({"argv": sys.argv}))) + except Exception as e: + print("Caught exception while attempting to write/open", + self.logfile, "exception: " + repr(e), file=sys.stderr) + qApp.quit() + + +def main(): + basedir = os.path.dirname(sys.executable) + # if script is inside .app package + if os.path.basename(basedir) == 'MacOS': + basedir = os.path.abspath( + os.path.join(basedir, os.pardir, os.pardir, os.pardir)) + + logfile = os.path.join(basedir, 'events.log') + + app = QApplication(list(sys.argv)) # Copy args to prevent qApp modifying + dummy = QWidget() + dummy.showMinimized() + app.setQuitOnLastWindowClosed(False) + eh = EventHandler(logfile=logfile) + app.installEventFilter(eh) + # Log that we did start so the calling app knows + QTimer.singleShot(0, eh.log_started) + timeout = 7000 # Default 7 seconds + try: + # Last arg is timeout (may be passed-in from test script) + timeout = int(1000 * float(sys.argv[-1])) + except (ValueError, IndexError): + """Arg was missing or bad arg, use default""" + # Quit the app after timeout milliseconds if we never get the event + QTimer.singleShot(timeout, app.quit) + app.exec_() + + +if __name__ == '__main__': + main() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_python_home.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_python_home.py new file mode 100644 index 0000000000000000000000000000000000000000..d8b80acea72e3194e71317176bbff52174e6b909 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_python_home.py @@ -0,0 +1,26 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# PYTHONHOME (sys.prefix) has to be same as sys._MEIPASS. + + +import sys + + +print(('sys._MEIPASS: ' + sys._MEIPASS)) +print(('sys.prefix: ' + sys.prefix)) +print(('sys.exec_prefix: ' + sys.exec_prefix)) + +if not sys.prefix == sys._MEIPASS: + raise SystemExit('sys.prefix is not set to path as in sys._MEIPASS.') +if not sys.exec_prefix == sys._MEIPASS: + raise SystemExit('sys.exec_prefix is not set to path as in sys._MEIPASS.') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_site_module_disabled.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_site_module_disabled.py new file mode 100644 index 0000000000000000000000000000000000000000..78a2855757a166e67800d7a81ea7d03a0bb3050d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_site_module_disabled.py @@ -0,0 +1,54 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +# Test that the Pythons 'site' module is disabled and Python is not searching +# for any user-specific site directories. + +# Check that option -S is passed to Python interpreter and that sys.path has +# not been modified. + + +import sys + +# The option -S tells Python not to import `site` on startup. +# If site has been imported already, that's instant failure. +if 'site' in sys.modules: + raise SystemExit('site module already imported') + +import site + +# Check it is really disabled. +if not sys.flags.no_site: + raise SystemExit('site module is enabled!') + +# Default values 'site' module when it is disabled. +# On Py2, ENABLE_USER_SITE should be False; on Py3, it should be None. +if site.ENABLE_USER_SITE not in (None, False): + raise SystemExit('ENABLE_USER_SITE is %s, expected %s.' % + (site.ENABLE_USER_SITE, (None, False))) + +# Since we import `site` here in the test, this causes USER_SITE and USER_BASE to be +# initialized on Py2, so all we can do is confirm that the paths aren't in sys.path + +if site.USER_SITE is not None: + if site.USER_SITE in sys.path: + raise SystemExit('USER_SITE found in sys.path') + +# This should never happen, USER_BASE isn't a site-modules folder and is only used by +# distutils for installing module datas. +if site.USER_BASE is not None: + if site.USER_SITE in sys.path: + raise SystemExit('USER_BASE found in sys.path') + + +# Check if this is realy our fake-site module +assert site.__pyinstaller__faked__site__module__ == True diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_stderr_encoding.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_stderr_encoding.py new file mode 100644 index 0000000000000000000000000000000000000000..b438973f59136aaafc9dbb1ca0dc7f66b8491634 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_stderr_encoding.py @@ -0,0 +1,32 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import codecs + +# Get the expected stdout/stderr encoding for this platform. +from pyi_testmod_gettemp import gettemp + +with open(gettemp("stderr_encoding.build")) as f: + encoding = f.read() +frozen_encoding = sys.stderr.encoding + +# Normalize encoding names - "UTF-8" should be the same as "utf8" +encoding = codecs.lookup(encoding).name +frozen_encoding = codecs.lookup(frozen_encoding).name + +print('Encoding expected:', encoding) +print('Encoding current:', frozen_encoding) + +if frozen_encoding != encoding: + raise SystemExit('Frozen encoding %s is not the same as unfrozen %s.' % + (frozen_encoding, encoding)) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_stdout_encoding.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_stdout_encoding.py new file mode 100644 index 0000000000000000000000000000000000000000..f2f76068619eb40d552e6e87de3507a22909fb2c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_stdout_encoding.py @@ -0,0 +1,32 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import codecs + +# Get the expected stdout/stderr encoding for this platform. +from pyi_testmod_gettemp import gettemp + +with open(gettemp("stdout_encoding.build")) as f: + encoding = f.read() +frozen_encoding = sys.stdout.encoding + +# Normalize encoding names - "UTF-8" should be the same as "utf8" +encoding = codecs.lookup(encoding).name +frozen_encoding = codecs.lookup(frozen_encoding).name + +print('Encoding expected:', encoding) +print('Encoding current:', frozen_encoding) + +if frozen_encoding != encoding: + raise SystemExit('Frozen encoding %s is not the same as unfrozen %s.' % + (frozen_encoding, encoding)) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_symlink_basename_is_kept.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_symlink_basename_is_kept.py new file mode 100644 index 0000000000000000000000000000000000000000..a68862e48c3d5024c21329dcbf0ca51f9733aff5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_symlink_basename_is_kept.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 ; mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys + +# To verify that the bootloader actually uses the unresolved symlink basename +# when executing the second process, check what `Name:` entry in +# '/proc/self/status' says. This value is truncated to 15 characters. +# +# This test is run twice: once with a short basename and once with a long +# basename to detect if this 15 character limit is no longer true. + +with open('/proc/self/status', 'r') as fh: + for line in fh.readlines(): + if line.startswith('Name:'): + name_from_proc = line.split(":")[1].strip() + break + +name_from_argv = os.path.basename(sys.argv[0]) +# linux will truncate the name to 15 chars +truncated_name_from_argv = name_from_argv[:15] + +print('ARGV:', repr(name_from_argv), "(complete)") +print('ARGV:', repr(truncated_name_from_argv), "(truncated") +print('PROC:', repr(name_from_proc)) +assert truncated_name_from_argv == name_from_proc diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_threading_module2.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_threading_module2.py new file mode 100644 index 0000000000000000000000000000000000000000..319384bb9f6f7cc4c753ffac6a0e5add42767e83 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_threading_module2.py @@ -0,0 +1,74 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# Test bootloader behaviour for threading code. +# Default behaviour of Python interpreter is to wait for all threads +# before exiting main process. +# Bootloader should behave also this way. + + +import os +import sys +import threading + + +_OUT_EXPECTED = ['ONE', 'TWO', 'THREE'] + + +# Code for the subprocess. +if 'PYI_THREAD_TEST_CASE' in os.environ: + class TestThreadClass(threading.Thread): + def __init__(self): + threading.Thread.__init__(self) + + def run(self): + print('ONE') + print('TWO') + print('THREE') + # Main process should not exit before the thread stops. + # This is the behaviour of Python interpreter. + TestThreadClass().start() + + +# Execute itself in a subprocess. +else: + # Differenciate subprocess code. + itself = sys.argv[0] + # Run subprocess. + import subprocess + + # Preserve environment to avoid `Failed to initialize Windows random API (CryptoGen)` + env = dict(os.environ) + env['PYI_THREAD_TEST_CASE'] = 'yes' + + proc = subprocess.Popen([itself], stdout=subprocess.PIPE, + env=env, + stderr=subprocess.PIPE, shell=False) + # Waits for subprocess to complete. + out, err = proc.communicate() + + # Make output from subprocess visible. + print(out) + out = out.decode('ascii') + print(out) + + # Remove empty lines from output. + out = out.strip().splitlines() + for line in out: + if not line.strip(): # Empty line detected. + out.remove(line) + # Check output. + if out != _OUT_EXPECTED: + print(" +++++++ SUBPROCESS ERROR OUTPUT +++++++") + print(err) + raise SystemExit('Subprocess did not print ONE, TWO, THREE in correct order. ' + '(output was %r, return code was %s)' % (out, proc.returncode)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_unbuffered_output.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_unbuffered_output.py new file mode 100644 index 0000000000000000000000000000000000000000..6e3d26e13fea1ed3322686a9fe2cbb8435bb81c6 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_unbuffered_output.py @@ -0,0 +1,62 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2021, 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) +#----------------------------------------------------------------------------- + +# A simple program for testing unbuffered mode of python stdout and +# stderr streams (both binary and text layers). +# +# The program periodically prints star ('*') character to a single line +# on the specified output stream (stdout or stderr). Once the selected +# number of stars have been printed, the end-of-transmission is signalled +# by 'E' character. +# +# In the unbuffered mode, the caller should receive the characters +# individually, and have enough time to process them. In the buffered +# mode, all printed characters including the terminating E will be +# received at once. +# +# NOTE: the unbuffered mode for text layers was introduced in Python 3.7. + +import argparse +import time +import sys + +# Argument parser +parser = argparse.ArgumentParser(description="Unbuffered stdio test") +parser.add_argument('--num-stars', type=int, default=5, + help="Number of star characters to print.") +parser.add_argument('--output-stream', type=str, default='stdout', + help="Output stream ('stdout' or 'stderr')") +parser.add_argument('--stream-mode', type=str, default='text', + help="Output stream mode ('text' or 'binary')") +args = parser.parse_args() + +# Select output stream and mode +assert args.output_stream in {'stdout', 'stderr'}, \ + f"Invalid output stream: {args.output_stream}!" +assert args.stream_mode in {'text', 'binary'}, \ + f"Invalid output stream mode: {args.stream_mode}!" + +stream = sys.stdout if args.output_stream == 'stdout' else sys.stderr +if args.stream_mode == 'binary': + stream = stream.buffer # Use binary layer + STAR = b'*' + EOT = b'E' +else: + STAR = '*' + EOT = 'E' + +# Print the specified number of stars in a single line +for i in range(args.num_stars): + stream.write(STAR) + time.sleep(1) + +# End-of-transmission +stream.write(EOT) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_win_py3_no_shortpathname.py b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_win_py3_no_shortpathname.py new file mode 100644 index 0000000000000000000000000000000000000000..25fb1706a5ae97316fe3bc83a4e02f5fff8df918 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/scripts/pyi_win_py3_no_shortpathname.py @@ -0,0 +1,47 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 script is used by multiple tests. It checks that various paths set by the +# bootloader are usable filenames. + +import sys, os +import win32api + +def check_shortpathname(fn): + lfn = win32api.GetLongPathNameW(fn) + fn = os.path.normcase(fn) + lfn = os.path.normcase(lfn) + if lfn != fn: + print("ShortPathName: Expected %s, got %s" % (fn, lfn)) + raise SystemExit(-1) + +print("sys.executable:", ascii(sys.executable)) + +if not os.path.exists(sys.executable): + raise SystemExit("sys.executable does not exist.") +check_shortpathname(sys.executable) + +print("sys.argv[0]:", ascii(sys.argv[0])) + +if not os.path.exists(sys.argv[0]): + raise SystemExit("sys.argv[0] does not exist.") +check_shortpathname(sys.argv[0]) + +print("sys._MEIPASS:", ascii(sys._MEIPASS)) + +if not os.path.exists(sys._MEIPASS): + raise SystemExit("sys._MEIPASS does not exist.") +tmp = os.path.normcase(win32api.GetTempPath()) +if os.path.normcase(win32api.GetLongPathNameW(tmp)) == tmp: + # Test only if TempPath is not a short path. This might happen if e.g + # TMP=c:\users\runner~1\appdata\local\temp, since the username is too long + check_shortpathname(sys._MEIPASS) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/list_pytest11_entry_point.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/list_pytest11_entry_point.spec new file mode 100644 index 0000000000000000000000000000000000000000..fcb6cccee5adc42ea6a52fd1dfb65224a4435396 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/list_pytest11_entry_point.spec @@ -0,0 +1,35 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2021, 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 pathlib import Path +from PyInstaller.utils.hooks import collect_entry_point + +app_name = 'list_pytest11_entry_point' + +datas, hidden = collect_entry_point("pytest11") + +a = Analysis([str(Path(SPECPATH).parent / 'scripts' / 'list_pytest11_entry_point.py')], + datas=datas, + hiddenimports=hidden) +pyz = PYZ(a.pure, a.zipped_data) +exe = EXE(pyz, + a.scripts, + [('u', None, 'OPTION'), ], + exclude_binaries=True, + name=app_name, + debug=False, + console=True) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + name=app_name) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/extra-hooks/hook-multipackage_test_pkg.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/extra-hooks/hook-multipackage_test_pkg.py new file mode 100644 index 0000000000000000000000000000000000000000..826bcbd07482b8e98312aa4bfc52856a9bb23351 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/extra-hooks/hook-multipackage_test_pkg.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2021, 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 PyInstaller.utils.hooks import collect_data_files +datas = collect_data_files('multipackage_test_pkg') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage1_B.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage1_B.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage1_B.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage2_B.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage2_B.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage2_B.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage3_B.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage3_B.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage3_B.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage4_B.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage4_B.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage4_B.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage5_B.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage5_B.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage5_B.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage5_C.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage5_C.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage5_C.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage_test_pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage_test_pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..006f529aa2b7ec01199573456172cb2f1d99ef3e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage_test_pkg/__init__.py @@ -0,0 +1,44 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2021, 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) +#----------------------------------------------------------------------------- + +def _test_basic_imports(): + # import a very simple and rarely used pure-python lib ... + import getopt # noqa: F401 + # ... and a module importing a shared lib + import ssl # noqa: F401 + + print('Hello World!') + + +def _test_nested_data_file(): + # try reading secret from a file in sub-directory + import os + + secret_file = os.path.join(__path__[0], 'data', 'secret.txt') + print("Reading secret from %s..." % (secret_file)) + with open(secret_file, 'r') as fp: + secret = fp.read().strip() + print("Secret: %s" % (secret)) + + assert secret == 'Secret1234' + + +def _test_nested_extensions(): + # import psutil, which contains an extension in its package directory + import psutil # noqa: F401 + print("Successfully imported psutil!") + + +def test_function(): + # Main test function + _test_basic_imports() + _test_nested_data_file() + _test_nested_extensions() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage_test_pkg/data/secret.txt b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage_test_pkg/data/secret.txt new file mode 100644 index 0000000000000000000000000000000000000000..ca315a5313562269a45d8deb2e953f7b7dd03c59 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/multipackage_test_pkg/data/secret.txt @@ -0,0 +1 @@ +Secret1234 diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage1.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage1.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage1.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage2.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage2.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage2.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage3.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage3.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage3.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage4.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage4.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage4.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage5.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage5.py new file mode 100644 index 0000000000000000000000000000000000000000..4dc53602d866f688b865cd7a49d3f96f2969540a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/multipackage-scripts/test_multipackage5.py @@ -0,0 +1,13 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 multipackage_test_pkg +multipackage_test_pkg.test_function() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_custom_protocol_handler.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_custom_protocol_handler.spec new file mode 100644 index 0000000000000000000000000000000000000000..444a263c3c37f28d4af5abf38206b53fc4b32e72 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_custom_protocol_handler.spec @@ -0,0 +1,40 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +app_name = 'pyi_osx_custom_protocol_handler' +custom_url_scheme = os.environ.get('PYI_CUSTOM_URL_SCHEME', 'pyi-test-app') + +a = Analysis(['../scripts/pyi_log_args.py']) +pyz = PYZ(a.pure, a.zipped_data) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + name=app_name, + debug=True, + bootloader_ignore_signals=False, + strip=False, + upx=False, + console=False ) +app = BUNDLE(exe, + name=app_name + '.app', + # Register custom protocol handler + info_plist={ + 'CFBundleURLTypes': [{ + 'CFBundleURLName': 'PYITestApp', + 'CFBundleTypeRole': 'Viewer', + 'CFBundleURLSchemes': [custom_url_scheme] + }] + }) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_event_forwarding.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_event_forwarding.spec new file mode 100644 index 0000000000000000000000000000000000000000..2009dd35409e7b51791f96f16a3c90a772606765 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_event_forwarding.spec @@ -0,0 +1,48 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +app_name = 'pyi_osx_event_forwarding' +custom_url_scheme = os.environ.get('PYI_CUSTOM_URL_SCHEME', 'pyi-test-app') +custom_file_ext = os.environ.get('PYI_CUSTOM_FILE_EXT', 'pyi_test_ext') + +a = Analysis([os.path.join(os.path.dirname(SPECPATH), 'scripts/pyi_pyqt5_log_events.py')]) +pyz = PYZ(a.pure, a.zipped_data) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + name=app_name, + debug=True, + bootloader_ignore_signals=False, + strip=False, + upx=False, + console=False ) +app = BUNDLE(exe, + name=app_name + '.app', + # Register custom protocol handler and custom file extension + info_plist={ + 'CFBundleURLTypes': [{ + 'CFBundleURLName': 'PYITestApp' + custom_url_scheme, + 'CFBundleTypeRole': 'Viewer', + 'CFBundleURLSchemes': [custom_url_scheme], + }], + 'CFBundleDocumentTypes': [{ + 'CFBundleTypeName': "PYITestApp_" + custom_file_ext, + 'CFBundleTypeExtensions': [ + custom_file_ext, + ], + 'CFBundleTypeRole': "Viewer", + }], + }) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_override_info_plist.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_override_info_plist.spec new file mode 100644 index 0000000000000000000000000000000000000000..fc8043e50d6e648c148c0ef73c1837f943e73b26 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_osx_override_info_plist.spec @@ -0,0 +1,52 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +block_cipher = None +app_name = 'pyi_osx_override_info_plist' + + +a = Analysis(['../scripts/pyi_helloworld.py'], + pathex=[], + binaries=None, + datas=None, + hiddenimports=[], + hookspath=[], + runtime_hooks=None, + excludes=None, + cipher=block_cipher) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name=app_name, + debug=True, + strip=None, + upx=False, + console=False ) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=None, + upx=True, + name=app_name) +app = BUNDLE(coll, + name=app_name + '.app', + icon=None, + bundle_identifier=None, + # Override some values in generated 'Info.plist'. + info_plist={ + 'NSHighResolutionCapable': 'True', + 'LSUIElement': '1', + }) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_unbuffered_output.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_unbuffered_output.spec new file mode 100644 index 0000000000000000000000000000000000000000..7103217d1ebf2cc2fbbb73e3edfde7d828a3cccd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/pyi_unbuffered_output.spec @@ -0,0 +1,34 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2021, 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) +#----------------------------------------------------------------------------- + +app_name = 'pyi_unbuffered_output' + +a = Analysis([os.path.join(os.path.dirname(SPECPATH), 'scripts/pyi_unbuffered_output.py')]) +pyz = PYZ(a.pure, a.zipped_data) +exe = EXE(pyz, + a.scripts, + [('u', None, 'OPTION'), ], + exclude_binaries=True, + name=app_name, + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=False, + console=True) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=False, + upx_exclude=[], + name=app_name) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/basemod.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/basemod.py new file mode 100644 index 0000000000000000000000000000000000000000..f5477241d5ea3b536834d7a78b615b0bc00a2fde --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/basemod.py @@ -0,0 +1,4 @@ + +class Popen(object): + def __init__(self, *args, **kw): + print(repr(args), repr(kw)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/main-script1.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/main-script1.py new file mode 100644 index 0000000000000000000000000000000000000000..0a745ee90ab7cd2d7cf5315bea032b460fab147b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/main-script1.py @@ -0,0 +1,16 @@ +import basemod + +import inspect +import sys + +class _Popen(basemod.Popen): + def __init__(self, *args, **kw): + print(inspect.getfile(self.__init__)) + print(inspect.getfile(super(_Popen, self).__init__)) + super(_Popen, self).__init__(*args, **kw) + +# Reduce recursion limit to shorten traceback +sys.setrecursionlimit(50) + +basemod.Popen = _Popen +p = basemod.Popen() diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/main-script2.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/main-script2.py new file mode 100644 index 0000000000000000000000000000000000000000..839ed464041d0c48e81091c8db4272a4a323b46a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/main-script2.py @@ -0,0 +1,6 @@ +try: + DATA_FROM_RTHOOK +except: + pass +else: + raise RuntimeError("Can access data from earlier script.") diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/rt-hook-script.py b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/rt-hook-script.py new file mode 100644 index 0000000000000000000000000000000000000000..47895f4b6093b6e4c3f3a1b6ca7cca91e6ad8ebc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts/rt-hook-script.py @@ -0,0 +1,9 @@ +import basemod + +DATA_FROM_RTHOOK = "This is data from the RT-Hook" + +class _Popen(basemod.Popen): + def __init__(self, *args, **kw): + super(_Popen, self).__init__(*args, **kw) + +basemod.Popen = _Popen diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts1.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts1.spec new file mode 100644 index 0000000000000000000000000000000000000000..daa2565225fc9bed466cdc781d71587960245e63 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts1.spec @@ -0,0 +1,30 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Verify each script has it's own global vars (original case, see issue +# #2949). + +app_name = "several-scripts1" + +a = Analysis(['several-scripts/rt-hook-script.py', + 'several-scripts/main-script1.py']) +pyz = PYZ(a.pure, a.zipped_data) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name=app_name, + debug=False, + console=True) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + name=app_name) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts2.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts2.spec new file mode 100644 index 0000000000000000000000000000000000000000..9130f3dfca301e3368aa1dda3a8f3531a4ccb0c2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/several-scripts2.spec @@ -0,0 +1,29 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Verify each script has it's own global vars (basic test). + +app_name = "several-scripts2" + +a = Analysis(['several-scripts/rt-hook-script.py', + 'several-scripts/main-script2.py']) +pyz = PYZ(a.pure, a.zipped_data) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name=app_name, + debug=False, + console=True) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + name=app_name) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/spec-with-utf8.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/spec-with-utf8.spec new file mode 100644 index 0000000000000000000000000000000000000000..2bc2579e33386b2ab3b435650c06206c6958d196 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/spec-with-utf8.spec @@ -0,0 +1,31 @@ +# -*- coding: utf-8 ; mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# spec-file containing some utf-8 umlauts +# äöu ÄŒapek +"äöü ÄŒapek" + +app_name = "spec-with-utf8" + +a = Analysis(['../scripts/pyi_helloworld.py']) +pyz = PYZ(a.pure, a.zipped_data) +exe = EXE(pyz, + a.scripts, + exclude_binaries=True, + name=app_name, + debug=False, # the test is this .sepc-file + console=True) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + name=app_name) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/symlink_basename_is_kept.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/symlink_basename_is_kept.spec new file mode 100644 index 0000000000000000000000000000000000000000..a26b4942144d316b574a2a64b0edb4bf517acb58 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/symlink_basename_is_kept.spec @@ -0,0 +1,49 @@ +# -*- coding: utf-8 ; mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +# substituted by test-case +script_name = '@SCRIPTDIR@/pyi_symlink_basename_is_kept.py' +symlink_name = "@SYMLINKNAME@_1" + +# ensure substitutes are done +assert not symlink_name.startswith("@SYMLINK" + "NAME@_"), \ + "SYMLINKNAME was not substituted in spec-file" +assert not script_name.startswith("@SCRIPT" + "DIR@/"), \ + "SCRIPTDIR was not substituted in spec-file" + +app_name = "keep_symlink_basename" + +a = Analysis([script_name]) + +pyz = PYZ(a.pure, a.zipped_data) + +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + name=app_name, + strip=False, + debug=False, # the test is this .spec-file + console=True) + +dst = os.path.join(DISTPATH, symlink_name) +src = app_name +dirname = os.path.dirname(dst) +if not os.path.exists(dirname): + os.makedirs(dirname) + # need to adjust the link src relative to the dst directory + src = os.path.relpath(src, os.path.dirname(symlink_name)) +print("creating symlink", src, dst) +os.symlink(src, dst) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage1.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage1.spec new file mode 100644 index 0000000000000000000000000000000000000000..0b054b2831470bd4172e7629a8891862e7678754 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage1.spec @@ -0,0 +1,55 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# MULTIPROCESS FEATURE: file A (onefile pack) depends on file B (onefile pack). +import os + +SCRIPT_DIR = 'multipackage-scripts' +__testname__ = 'test_multipackage1' +__testdep__ = 'multipackage1_B' + +a = Analysis([os.path.join(SCRIPT_DIR, __testname__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) +b = Analysis([os.path.join(SCRIPT_DIR, __testdep__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) + +MERGE((b, __testdep__, __testdep__), (a, __testname__, __testname__)) + +pyz = PYZ(a.pure) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + a.dependencies, + name=os.path.join('dist', __testname__), + debug=True, + strip=False, + upx=False, + console=1 ) + +pyzB = PYZ(b.pure) +exeB = EXE(pyzB, + b.scripts, + b.binaries, + b.zipfiles, + b.datas, + b.dependencies, + name=os.path.join('dist', __testdep__), + debug=True, + strip=False, + upx=False, + console=1 ) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage2.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage2.spec new file mode 100644 index 0000000000000000000000000000000000000000..e7a087a969f1ad14702613cfd8b08c7aee50f12c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage2.spec @@ -0,0 +1,64 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# MULTIPROCESS FEATURE: file A (onefile pack) depends on file B (onedir pack) +import os +import sys + +SCRIPT_DIR = 'multipackage-scripts' +__testname__ = 'test_multipackage2' +__testdep__ = 'multipackage2_B' + +a = Analysis([os.path.join(SCRIPT_DIR, __testname__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) +b = Analysis([os.path.join(SCRIPT_DIR, __testdep__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) + +MERGE((b, __testdep__, os.path.join(__testdep__, __testdep__)), + (a, __testname__, __testname__)) + +pyz = PYZ(a.pure) +exe = EXE(pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + a.dependencies, + name=os.path.join('dist', __testname__), + debug=True, + strip=False, + upx=True, + console=1 ) + +pyzB = PYZ(b.pure) +exeB = EXE(pyzB, + b.scripts, + b.dependencies, + exclude_binaries=1, + name=os.path.join('build', 'pyi.'+sys.platform, __testdep__, + __testdep__), + debug=True, + strip=False, + upx=True, + console=1 ) + +coll = COLLECT( exeB, + b.binaries, + b.zipfiles, + b.datas, + strip=False, + upx=True, + name=os.path.join('dist', __testdep__)) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage3.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage3.spec new file mode 100644 index 0000000000000000000000000000000000000000..19d9eb9c165c68f29fc8164b390b0e454686e882 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage3.spec @@ -0,0 +1,64 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# TESTING MULTIPROCESS FEATURE: file A (onedir pack) depends on file B (onefile pack). +import os +import sys + +SCRIPT_DIR = 'multipackage-scripts' +__testname__ = 'test_multipackage3' +__testdep__ = 'multipackage3_B' + +a = Analysis([os.path.join(SCRIPT_DIR, __testname__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) +b = Analysis([os.path.join(SCRIPT_DIR, __testdep__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) + +MERGE((b, __testdep__, os.path.join(__testdep__)), + (a, __testname__, os.path.join(__testname__, __testname__))) + +pyz = PYZ(a.pure) +exe = EXE(pyz, + a.scripts, + a.dependencies, + exclude_binaries=1, + name=os.path.join('build', 'pyi.'+sys.platform, __testname__, + __testname__), + debug=True, + strip=False, + upx=True, + console=1 ) + +coll = COLLECT( exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name=os.path.join('dist', __testname__ )) + +pyzB = PYZ(b.pure) +exeB = EXE(pyzB, + b.scripts, + b.binaries, + b.zipfiles, + b.datas, + b.dependencies, + name=os.path.join('dist', __testdep__), + debug=True, + strip=False, + upx=True, + console=1 ) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage4.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage4.spec new file mode 100644 index 0000000000000000000000000000000000000000..717f21d1d7d72e13990b4b70e35d1ee0cc22d3a4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage4.spec @@ -0,0 +1,71 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# TESTING MULTIPROCESS FEATURE: file A (onedir pack) depends on file B (onedir pack). +import os +import sys + +SCRIPT_DIR = 'multipackage-scripts' +__testname__ = 'test_multipackage4' +__testdep__ = 'multipackage4_B' + +a = Analysis([os.path.join(SCRIPT_DIR, __testname__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) +b = Analysis([os.path.join(SCRIPT_DIR, __testdep__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) + +MERGE((b, __testdep__, os.path.join(__testdep__, __testdep__)), + (a, __testname__, os.path.join(__testname__, __testname__))) + +pyz = PYZ(a.pure) +exe = EXE(pyz, + a.scripts, + a.dependencies, + exclude_binaries=1, + name=os.path.join('build', 'pyi.'+sys.platform, __testname__, + __testname__), + debug=True, + strip=False, + upx=True, + console=1 ) + +coll = COLLECT( exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name=os.path.join('dist', __testname__)) + +pyzB = PYZ(b.pure) +exeB = EXE(pyzB, + b.scripts, + b.dependencies, + exclude_binaries=1, + name=os.path.join('build', 'pyi.'+sys.platform, __testdep__, + __testdep__), + debug=True, + strip=False, + upx=True, + console=1 ) + +coll = COLLECT( exeB, + b.binaries, + b.zipfiles, + b.datas, + strip=False, + upx=True, + name=os.path.join('dist', __testdep__)) + diff --git a/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage5.spec b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage5.spec new file mode 100644 index 0000000000000000000000000000000000000000..5a93c2b95822ce432694d84fc3ebd6149a3a7eb2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/specs/test_multipackage5.spec @@ -0,0 +1,90 @@ +# -*- mode: python -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# TESTING MULTIPROCESS FEATURE: file A (onedir pack) depends on file B (onedir pack) +# and file C (onefile pack) +import os +import sys + +SCRIPT_DIR = 'multipackage-scripts' +__testname__ = 'test_multipackage5' +__testdep__ = 'multipackage5_B' +__testdep2__ = 'multipackage5_C' + +a = Analysis([os.path.join(SCRIPT_DIR, __testname__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) +b = Analysis([os.path.join(SCRIPT_DIR, __testdep__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) +c = Analysis([os.path.join(SCRIPT_DIR, __testdep2__ + '.py')], + hookspath=[os.path.join(SPECPATH, SCRIPT_DIR, 'extra-hooks')], + pathex=['.']) + + +MERGE((b, __testdep__, os.path.join(__testdep__, __testdep__)), + (c, __testdep2__, os.path.join(__testdep2__)), + (a, __testname__, os.path.join(__testname__, __testname__))) + +pyz = PYZ(a.pure) +exe = EXE(pyz, + a.scripts, + a.dependencies, + exclude_binaries=1, + name=os.path.join('build', 'pyi.'+sys.platform, __testname__, + __testname__), + debug=True, + strip=False, + upx=True, + console=1 ) + +coll = COLLECT( exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name=os.path.join('dist', __testname__)) + +pyzB = PYZ(b.pure) +exeB = EXE(pyzB, + b.scripts, + b.dependencies, + exclude_binaries=1, + name=os.path.join('build', 'pyi.'+sys.platform, __testdep__, + __testdep__), + debug=True, + strip=False, + upx=True, + console=1 ) + +coll = COLLECT( exeB, + b.binaries, + b.zipfiles, + b.datas, + strip=False, + upx=True, + name=os.path.join('dist', __testdep__)) + +pyzC = PYZ(c.pure) +exeC = EXE(pyzC, + c.scripts, + c.binaries, + c.zipfiles, + c.datas, + c.dependencies, + name=os.path.join('dist', __testdep2__), + debug=True, + strip=False, + upx=True, + console=1 ) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_apple_events.py b/3rdparty/pyinstaller-4.3/tests/functional/test_apple_events.py new file mode 100644 index 0000000000000000000000000000000000000000..09a0487618d1b04f0f4108ba6377c754c044b00b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_apple_events.py @@ -0,0 +1,227 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +OSX-specific test to check handling AppleEvents by bootloader +""" + +# Library imports +# --------------- +import json +import os +import subprocess +import time + +# Third-party imports +# ------------------- +import pytest + +# Local imports +# ------------- +from PyInstaller.utils.tests import importorskip + + +@pytest.mark.darwin +def test_osx_custom_protocol_handler(tmpdir, pyi_builder_spec): + app_path = os.path.join(tmpdir, 'dist', + 'pyi_osx_custom_protocol_handler.app') + logfile_path = os.path.join(tmpdir, 'dist', 'args.log') + + # Generate new URL scheme to avoid collisions + custom_url_scheme = "pyi-test-%i" % time.time() + os.environ["PYI_CUSTOM_URL_SCHEME"] = custom_url_scheme + + pyi_builder_spec.test_spec('pyi_osx_custom_protocol_handler.spec') + + # First run using 'open' registers custom protocol handler + subprocess.check_call(['open', app_path]) + # 'open' starts program in a different process + # so we need to wait for it to finish + time.sleep(5) + + # Call custom protocol handler + url = custom_url_scheme + "://url-args" + subprocess.check_call(['open', url]) + # Wait for the program to finish + time.sleep(5) + assert os.path.exists(logfile_path), 'Missing args logfile' + with open(logfile_path, 'r') as fh: + log_lines = fh.readlines() + assert log_lines and log_lines[-1] == url, 'Invalid arg appended' + + +@pytest.mark.darwin +@importorskip('PyQt5') +def test_osx_event_forwarding(tmpdir, pyi_builder_spec): + app_path = os.path.join(tmpdir, 'dist', + 'pyi_osx_event_forwarding.app') + + logfile_path = os.path.join(tmpdir, 'dist', 'events.log') + + # Generate unique URL scheme & file ext to avoid collisions + unique_key = int(time.time()) + custom_url_scheme = "pyi-test-%i" % unique_key + custom_file_ext = 'pyi_test_%i' % unique_key + os.environ["PYI_CUSTOM_URL_SCHEME"] = custom_url_scheme + os.environ["PYI_CUSTOM_FILE_EXT"] = custom_file_ext + + # test_script builds the app then implicitly runs the script, so we + # pass arg "0" to tell the built script to exit right away here. + pyi_builder_spec.test_spec('pyi_osx_event_forwarding.spec', + app_args=["0"]) + + timeout = 60.0 # Give up after 60 seconds + polltime = 0.25 # Poll events.log every 250ms + + def wait_for_started(): + t0 = time.time() # mark start time + # Poll logfile for app to be started (it writes "started" to the first + # log line) + while True: + elapsed = time.time() - t0 + if elapsed > timeout: + return + if os.path.exists(logfile_path): + with open(logfile_path) as fh: + log_lines = fh.readlines() + if log_lines: + first = log_lines[0] + assert first.startswith('started '), \ + "Unexpected line in log file" + # Now, parse the logged args + # e.g. 'started {"argv": ["Arg1, ...]}' + dd = json.loads(first.split(" ", 1)[-1]) + assert 'argv' in dd, "First line missing argv" + return dd['argv'] # it started ok, abort loop + else: + # Try again later + time.sleep(polltime) + + # wait for the app started for us by test_spec to exit + assert wait_for_started(), "App did not start" + + time.sleep(2) # presumably app has exited after 2 seconds + + # clean up the log file created by test_spec() running the app + os.remove(logfile_path) + + # Run using 'open', passing a 0-timeout as an arg. + # macOS will auto-register the custom protocol handler and extension + # association. Then app will quit immediately due to the "0" arg. + subprocess.check_call(['open', app_path, '--args', "0"]) + + assert wait_for_started(), 'App start timed out' + time.sleep(2) # wait for app to exit + + # App exited immediately, clean-up + os.remove(logfile_path) + + # At this point both the protocol handler and the file ext are registered + # 1. Try the file extension -- this tests the AppleEvent rewrite of + # a "file://" event to a regular filesystem path. + + # Create 32 files that are associated with this app. + # This tests the robustness of the argv-emu by spamming it with + # lots of args and seeing what happens. + n_files = 32 + assoc_files = [] + for ii in range(n_files): + assoc_path = os.path.join(tmpdir, 'dist', + 'AFile{}.{}'.format(ii, custom_file_ext)) + with open(assoc_path, 'wt') as fh: + fh.write("File contents #{}\n".format(ii)) + assoc_files.append(assoc_path) + + # Open app again by "open"ing the associated files. + # + # These are sent as Apple Events to the app immediately after it starts, + # which the bootloader translates back into file paths at startup, passing + # them as argv to the subordinate app. + # + # The generator below produces odd numbered files as "file://" URLs, and + # even numbered are just file paths. They all should end up appended to + # sys.argv in the app as simple file paths. + subprocess.check_call(['open', *[('file://' if ii % 2 else '') + ff + for ii, ff in enumerate(assoc_files)]]) + + args = wait_for_started() + assert args is not None, 'App start timed out' + # Test that all the file paths were received in argv via pre-startup + # translation of file:// AppleEvent -> argv filesystem path. + assert assoc_files == args[1:], \ + "An expected file path was not received by the app" + + # At this point the app is running. + + # This is a trick to make our app lose focus so that Qt forwards + # the "Activated" events properly to our event handler in + # pyi_pyqt5_log_events.py + subprocess.check_call(['osascript', "-e", + 'tell application "System Events" to activate']) + time.sleep(1.0) # delay for above applescript + + # The app is running now, in the background, and doesn't have focus + + # 2. Call open passing the app path again -- this should activate the + # already-running app and the activation_count should be 2 after it + # exits. + subprocess.check_call(['open', app_path]) + time.sleep(1.0) # the activate event gets sent with a delay + + # 3. Call open again using the url associated with the app. This should + # forward the Apple URL event to the already-running app. + url = custom_url_scheme + "://lowecase_required/hello_world/" + # Test support for large URL data ~64KB. + # Note: We would have gone larger but 'open' itself seems to not + # consistently like data over a certain size. + url += 'x' * 64000 # Append 64 KB of data to URL to stress-test + subprocess.check_call(['open', url]) + activation_count = None + + def wait_for_event_in_logfile(): + t0 = time.time() # mark start time + # Wait for the program to finish -- poll for expected line to appear + # in events.log + while True: + assert os.path.exists(logfile_path), 'Missing events logfile' + with open(logfile_path, 'rt') as fh: + log_lines = fh.readlines() + if len(log_lines) >= 3: + url_line = log_lines[1] + activation_line = log_lines[2] + assert url_line.startswith("url ") + assert activation_line.startswith("activate_count ") + url_part = url_line.split(" ", 1)[-1] + assert url_part.strip().lower() == url.lower(), \ + 'Logged url does not match expected' + activation_part = activation_line.split(" ", 1)[-1] + nonlocal activation_count + activation_count = int(activation_part.strip()) + return True + else: + # Try again later + time.sleep(polltime) + elapsed = time.time() - t0 + if elapsed > timeout: + return False + + assert wait_for_event_in_logfile(), \ + 'URL event did not appear in log before timeout' + + assert activation_count == 2, \ + "App did not receive rapp (re-Open app) event properly" + + # Delete all the temp files to be polite + for ff in assoc_files: + try: + os.remove(ff) + except OSError: + pass diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_basic.py b/3rdparty/pyinstaller-4.3/tests/functional/test_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..50788835682f1099a7946cad89cede088ff091a4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_basic.py @@ -0,0 +1,714 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Library imports +# --------------- +import locale +import os +import sys + +# Third-party imports +# ------------------- +import pytest + +# Local imports +# ------------- +from PyInstaller.compat import is_darwin, is_win, is_py37 +from PyInstaller.utils.tests import importorskip, skipif, \ + skipif_no_compiler, xfail + + +def test_run_from_path_environ(pyi_builder): + pyi_builder.test_script('pyi_absolute_python_path.py', run_from_path=True) + + +@pytest.mark.linux +def test_absolute_ld_library_path(pyi_builder): + pyi_builder.test_script('pyi_absolute_ld_library_path.py') + + +def test_absolute_python_path(pyi_builder): + pyi_builder.test_script('pyi_absolute_python_path.py') + + +@pytest.mark.linux +@skipif(not os.path.exists('/proc/self/status'), + reason='/proc/self/status does not exist') +@pytest.mark.parametrize("symlink_name", + ["symlink", + "very_long_name_in_symlink", + "sub/dir/progam"]) +def test_symlink_basename_is_kept(pyi_builder_spec, symlink_name, + tmpdir, SPEC_DIR, SCRIPT_DIR): + + def patch(spec_name, symlink_name): + content = SPEC_DIR.join(spec_name).read_text(encoding="utf-8") + content = content.replace("@SYMLINKNAME@", symlink_name) + content = content.replace("@SCRIPTDIR@", str(SCRIPT_DIR)) + outspec = tmpdir.join(spec_name) + outspec.write_text(content, encoding="utf-8", ensure=True) + return outspec + + specfile = patch("symlink_basename_is_kept.spec", symlink_name) + pyi_builder_spec.test_spec(str(specfile), app_name=symlink_name) + + +def test_pyz_as_external_file(pyi_builder, monkeypatch): + # This tests the not well documented and seldom used feature of + # having the PYZ-archive in a separate file (.pkg). + + def MyEXE(*args, **kwargs): + kwargs['append_pkg'] = False + return EXE(*args, **kwargs) + + # :todo: find a better way to not even run this test in onefile-mode + if pyi_builder._mode == 'onefile': + pytest.skip('only --onedir') + + import PyInstaller.building.build_main + EXE = PyInstaller.building.build_main.EXE + monkeypatch.setattr('PyInstaller.building.build_main.EXE', MyEXE) + + pyi_builder.test_source("print('Hello Python!')") + +def test_base_modules_regex(pyi_builder): + """ + Verify that the regex for excluding modules listed in + PY3_BASE_MODULES does not exclude other modules. + """ + pyi_builder.test_source( + """ + import resources_testmod + print('OK') + """) + + +def test_celementtree(pyi_builder): + pyi_builder.test_source( + """ + from xml.etree.cElementTree import ElementTree + print('OK') + """) + + +# Test a build with some complexity with the ``noarchive`` debug option. +def test_noarchive(pyi_builder): + pyi_builder.test_source("from xml.etree.cElementTree import ElementTree", + pyi_args=['--debug=noarchive']) + + +@importorskip('codecs') +def test_codecs(pyi_builder): + pyi_builder.test_script('pyi_codecs.py') + + +def test_compiled_filenames(pyi_builder): + pyi_builder.test_source(""" + import pyi_dummy_module + from os.path import isabs + + assert not isabs(pyi_dummy_module.dummy.__code__.co_filename), "pyi_dummy_module.dummy.__code__.co_filename has compiled filename: %s" % (pyi_dummy_module.dummy.__code__.co_filename,) + assert not isabs(pyi_dummy_module.DummyClass.dummyMethod.__code__.co_filename), "pyi_dummy_module.DummyClass.dummyMethod.__code__.co_filename has compiled filename: %s" % (pyi_dummy_module.DummyClass.dummyMethod.__code__.co_filename,) + """) + +def test_decoders_ascii(pyi_builder): + pyi_builder.test_source( + """ + # Convert type 'bytes' to type 'str'. + assert b'foo'.decode('ascii') == 'foo' + """) + + +def test_distutils_submod(pyi_builder): + # Test import of submodules of distutils package + # PyI fails to include `distutils.version` when running from virtualenv + pyi_builder.test_source( + """ + from distutils.version import LooseVersion + """) + + +def test_dynamic_module(pyi_builder): + pyi_builder.test_source( + """ + import pyi_testmod_dynamic + + # The value 'foo' should not be None. + print("'foo' value: %s" % pyi_testmod_dynamic.foo) + assert pyi_testmod_dynamic.foo is not None + assert pyi_testmod_dynamic.foo == 'A new value!' + """) + + +def test_email(pyi_builder): + pyi_builder.test_source( + """ + from email import utils + from email.header import Header + from email.mime.text import MIMEText + from email.mime.multipart import MIMEMultipart + from email.mime.nonmultipart import MIMENonMultipart + """) + + +@importorskip('tinyaes') +def test_feature_crypto(pyi_builder): + pyi_builder.test_source( + """ + from pyimod00_crypto_key import key + from pyimod02_archive import CRYPT_BLOCK_SIZE + + # Test against issue #1663: importing a package in the bootstrap + # phase should not interfere with subsequent imports. + import tinyaes + + assert type(key) is str + # The test runner uses 'test_key' as key. + assert key == 'test_key'.zfill(CRYPT_BLOCK_SIZE) + """, + pyi_args=['--key=test_key']) + + +def test_feature_nocrypto(pyi_builder): + pyi_builder.test_source( + """ + try: + import pyimod00_crypto_key + + raise AssertionError('The pyimod00_crypto_key module must NOT be there if crypto is disabled.') + except ImportError: + pass + """) + + +def test_filename(pyi_builder): + pyi_builder.test_script('pyi_filename.py') + + +def test_getfilesystemencoding(pyi_builder): + pyi_builder.test_script('pyi_getfilesystemencoding.py') + + +def test_helloworld(pyi_builder): + pyi_builder.test_source("print('Hello Python!')") + + +def test_module__file__attribute(pyi_builder): + pyi_builder.test_script('pyi_module__file__attribute.py') + + +def test_module_attributes(tmpdir, pyi_builder): + # Create file in tmpdir with path to python executable and if it is running + # in debug mode. + # Test script uses python interpreter to compare module attributes. + with open(os.path.join(tmpdir.strpath, 'python_exe.build'), 'w') as f: + f.write(sys.executable + "\n") + f.write('debug=%s' % __debug__ + '\n') + # On Windows we need to preserve systme PATH for subprocesses in tests. + f.write(os.environ.get('PATH') + '\n') + pyi_builder.test_script('pyi_module_attributes.py') + + +@xfail(is_darwin, reason='Issue #1895.') +def test_module_reload(pyi_builder): + pyi_builder.test_script('pyi_module_reload.py') + + +# TODO test it on OS X. +@skipif_no_compiler +def test_load_dll_using_ctypes(monkeypatch, pyi_builder, compiled_dylib): + # Note that including the data_dir fixture copies files needed by this test. + # + # TODO Make sure PyInstaller is able to find the library and bundle it with the app. + # # If the required dylib does not reside in the current directory, the Analysis + # # class machinery, based on ctypes.util.find_library, will not find it. This + # # was done on purpose for this test, to show how to give Analysis class + # # a clue. + # if is_win: + # os.environ['PATH'] = os.path.abspath(CTYPES_DIR) + ';' + os.environ['PATH'] + # else: + # os.environ['LD_LIBRARY_PATH'] = CTYPES_DIR + # os.environ['DYLD_LIBRARY_PATH'] = CTYPES_DIR + # os.environ['LIBPATH'] = CTYPES_DIR + + # Build and run the app. + pyi_builder.test_script('pyi_load_dll_using_ctypes.py') + + +def test_get_meipass_value(pyi_builder): + pyi_builder.test_script('pyi_get_meipass_value.py') + + +def test_chdir_meipass(pyi_builder): + # Ensure meipass dir exists. + pyi_builder.test_source( + """ + import os, sys + os.chdir(sys._MEIPASS) + print(os.getcwd()) + """) + + +def test_option_exclude_module(pyi_builder): + """ + Test to ensure that when using option --exclude-module=xml.sax + the module 'xml.sax' won't be bundled. + """ + pyi_builder.test_source( + """ + try: + import xml.sax + # Option --exclude-module=xml.sax did not work and the module + # was successfully imported. + raise SystemExit('Module xml.sax was excluded but it is ' + 'bundled with the executable.') + except ImportError: + # The Import error is expected since PyInstaller should + # not bundle 'xml.sax' module. + pass + """, + pyi_args=['--exclude-module', 'xml.sax']) + + +def test_option_verbose(pyi_builder, monkeypatch): + "Test to ensure that option V can be set and has effect." + # This option is like 'python -v' - trace import statements. + # 'None' should be allowed or '' also. + + def MyEXE(*args, **kwargs): + args = list(args) + args.append([('v', None, 'OPTION')]) + return EXE(*args, **kwargs) + + import PyInstaller.building.build_main + EXE = PyInstaller.building.build_main.EXE + monkeypatch.setattr('PyInstaller.building.build_main.EXE', MyEXE) + + pyi_builder.test_source( + """ + print('test - PYTHONVERBOSE - trace import statements') + import re # just import anything + print('test - done') + """) + + +def test_option_w_unset(pyi_builder): + "Test to ensure that option W is not set by default." + pyi_builder.test_source( + """ + import sys + assert 'ignore' not in sys.warnoptions + """) + + +def test_option_w_ignore(pyi_builder, monkeypatch, capsys): + "Test to ensure that option W can be set." + + def MyEXE(*args, **kwargs): + args = list(args) + args.append([('W ignore', '', 'OPTION')]) + return EXE(*args, **kwargs) + + import PyInstaller.building.build_main + EXE = PyInstaller.building.build_main.EXE + monkeypatch.setattr('PyInstaller.building.build_main.EXE', MyEXE) + + pyi_builder.test_source( + """ + import sys + assert 'ignore' in sys.warnoptions + """) + + _, err = capsys.readouterr() + assert "'import warnings' failed" not in err + + +@pytest.mark.parametrize("distutils", ["", "from distutils "]) +def test_python_makefile(pyi_builder, distutils): + """Tests hooks for ``sysconfig`` and its near-duplicate + ``distutils.sysconfig``. Raises an import error if we failed to collect the + special module that contains the details from pyconfig.h and the makefile. + """ + # Ideally we'd test that the contents of `sysconfig.get_config_vars()` dict + # are the same frozen vs unfrozen but because some values are paths into + # a Python installation's guts, these will point into the frozen app when + # frozen and therefore noy match. Without some fiddly filtering away paths, + # this is impossible. + + # As a compromise, test that the dictionary keys are the same to be sure + # that there is no conditional initialisation of get_config_vars(). i.e. + # get_config_vars() doesn't silently return an empty dictionary if it can't + # find the information it needs. + if distutils: + from distutils import sysconfig + else: + import sysconfig + unfrozen_keys = sorted(sysconfig.get_config_vars().keys()) + + pyi_builder.test_source(""" + # The error is raised immediately on import. + {}import sysconfig + + # But just in case, Python later opt for some lazy loading, force + # configuration retrieval: + from pprint import pprint + pprint(sysconfig.get_config_vars()) + + unfrozen_keys = {} + assert sorted(sysconfig.get_config_vars()) == unfrozen_keys + + """.format(distutils, unfrozen_keys)) + + +def test_set_icon(pyi_builder, data_dir): + if is_win: + args = ['--icon', os.path.join(data_dir.strpath, 'pyi_icon.ico')] + elif is_darwin: + # On OS X icon is applied only for windowed mode. + args = ['--windowed', '--icon', os.path.join(data_dir.strpath, 'pyi_icon.icns')] + else: + pytest.skip('option --icon works only on Windows and Mac OS X') + pyi_builder.test_source("print('Hello Python!')", pyi_args=args) + + +def test_python_home(pyi_builder): + pyi_builder.test_script('pyi_python_home.py') + + +def test_stderr_encoding(tmpdir, pyi_builder): + # NOTE: '-s' option to pytest disables output capturing, changing this test's result: + # without -s: py.test process changes its own stdout encoding to 'UTF-8' to + # capture output. subprocess spawned by py.test has stdout encoding + # 'cp1252', which is an ANSI codepage. test fails as they do not match. + # with -s: py.test process has stdout encoding from windows terminal, which is an + # OEM codepage. spawned subprocess has the same encoding. test passes. + # + with open(os.path.join(tmpdir.strpath, 'stderr_encoding.build'), 'w') as f: + if sys.stderr.isatty(): + enc = str(sys.stderr.encoding) + else: + # For non-interactive stderr use locale encoding - ANSI codepage. + # This fixes the test when running with py.test and capturing output. + enc = locale.getpreferredencoding(False) + f.write(enc) + pyi_builder.test_script('pyi_stderr_encoding.py') + + +def test_stdout_encoding(tmpdir, pyi_builder): + with open(os.path.join(tmpdir.strpath, 'stdout_encoding.build'), 'w') as f: + if sys.stdout.isatty(): + enc = str(sys.stdout.encoding) + else: + # For non-interactive stderr use locale encoding - ANSI codepage. + # This fixes the test when running with py.test and capturing output. + enc = locale.getpreferredencoding(False) + f.write(enc) + pyi_builder.test_script('pyi_stdout_encoding.py') + + +def test_site_module_disabled(pyi_builder): + pyi_builder.test_script('pyi_site_module_disabled.py') + + +def test_time_module(pyi_builder): + pyi_builder.test_source( + """ + import time + print(time.strptime(time.ctime())) + """) + + +@pytest.mark.darwin +@pytest.mark.linux +def test_time_module_localized(pyi_builder, monkeypatch): + # This checks that functions 'time.ctime()' and 'time.strptime()' + # use the same locale. There was an issue with bootloader where + # every function was using different locale: + # time.ctime was using 'C' + # time.strptime was using 'xx_YY' from the environment. + monkeypatch.setenv('LC_ALL', 'cs_CZ.UTF-8') + pyi_builder.test_source( + """ + import time + print(time.strptime(time.ctime())) + """) + + +def test_xmldom_module(pyi_builder): + pyi_builder.test_source( + """ + print('Importing xml.dom') + from xml.dom import pulldom + print('Importing done') + """) + + +def test_threading_module(pyi_builder): + pyi_builder.test_source( + """ + import threading + import sys + + print('See stderr for messages') + def print_(*args): print(*args, file=sys.stderr) + + def doit(nm): + print_(nm, 'started') + import pyi_testmod_threading + try: + print_(nm, pyi_testmod_threading.x) + finally: + print_(nm, pyi_testmod_threading) + + t1 = threading.Thread(target=doit, args=('t1',)) + t2 = threading.Thread(target=doit, args=('t2',)) + t1.start() + t2.start() + doit('main') + t1.join() ; print_('t1 joined') + t2.join() ; print_('t2 joined') + print_('finished.') + """) + + +def test_threading_module2(pyi_builder): + pyi_builder.test_script('pyi_threading_module2.py') + + +def test_argument(pyi_builder): + pyi_builder.test_source( + ''' + import sys + assert sys.argv[1] == "--argument", "sys.argv[1] was %r, expected %r" % (sys.argv[1], "--argument") + ''', + app_args=["--argument"]) + + +@importorskip('win32com') +def test_pywin32_win32com(pyi_builder): + pyi_builder.test_source( + """ + # Test importing some modules from pywin32 package. + # All modules from pywin32 depens on module pywintypes. + # This module should be also included. + import win32com + import win32com.client + import win32com.server + """) + + +#@pytest.mark.xfail(reason="Requires post-create-package hooks (issue #1322)") +@importorskip('win32com') +def test_pywin32_comext(pyi_builder): + pyi_builder.test_source( + """ + # Test importing modules from win32com that are actually present in + # win32comext, and made available by __path__ changes in win32com. + from win32com.shell import shell + from win32com.propsys import propsys + from win32com.bits import bits + """) + + +@importorskip('win32ui') +@xfail(reason="https://github.com/mhammond/pywin32/issues/1614") +def test_pywin32_win32ui(pyi_builder): + pyi_builder.test_source( + """ + # Test importing some modules from pywin32 package. + # All modules from pywin32 depens on module pywintypes. + # This module should be also included. + import win32ui + from pywin.mfc.dialog import Dialog + d = Dialog(win32ui.IDD_SIMPLE_INPUT) + """) + + +@pytest.mark.win32 +def test_renamed_exe(pyi_builder): + _old_find_executables = pyi_builder._find_executables + def _find_executables(name): + oldexes = _old_find_executables(name) + newexes = [] + for old in oldexes: + + new = os.path.join(os.path.dirname(old), "renamed_" + os.path.basename(old)) + os.rename(old, new) + newexes.append(new) + return newexes + + pyi_builder._find_executables = _find_executables + pyi_builder.test_source("print('Hello Python!')") + +def test_spec_with_utf8(pyi_builder_spec): + pyi_builder_spec.test_spec('spec-with-utf8.spec') + + +@pytest.mark.darwin +def test_osx_override_info_plist(pyi_builder_spec): + pyi_builder_spec.test_spec('pyi_osx_override_info_plist.spec') + + +def test_hook_collect_submodules(pyi_builder, script_dir): + # This is designed to test the operation of + # PyInstaller.utils.hook.collect_submodules. To do so: + # + # 1. It imports the dummy module pyi_collect_submodules_mod, which + # contains nothing. + # 2. This causes hook-pyi_collect_submodules_mod.py to be run, + # which collects some dummy submodules. In this case, it + # collects from modules/pyi_testmod_relimp. + # 3. Therefore, we should be able to find hidden imports under + # pyi_testmod_relimp. + pyi_builder.test_source( + """ + import pyi_collect_submodules_mod + __import__('pyi_testmod_relimp.B.C') + """, + ['--additional-hooks-dir=%s' % script_dir.join('pyi_hooks')]) + +# Test that PyInstaller can handle a script with an arbitrary extension. +def test_arbitrary_ext(pyi_builder): + pyi_builder.test_script('pyi_arbitrary_ext.foo') + +def test_option_runtime_tmpdir(pyi_builder): + "Test to ensure that option `runtime_tmpdir` can be set and has effect." + + pyi_builder.test_source( + """ + print('test - runtime_tmpdir - custom runtime temporary directory') + import os + import sys + if sys.platform == 'win32': + import win32api + cwd = os.path.abspath(os.getcwd()) + runtime_tmpdir = os.path.abspath(sys._MEIPASS) + # for onedir mode, runtime_tmpdir == cwd + # for onefile mode, os.path.dirname(runtime_tmpdir) == cwd + if not runtime_tmpdir == cwd and not os.path.dirname(runtime_tmpdir) == cwd: + raise SystemExit('Expected sys._MEIPASS to be under current working dir.' + ' sys._MEIPASS = ' + runtime_tmpdir + ', cwd = ' + cwd) + print('test - done') + """, + ['--runtime-tmpdir=.']) # set runtime-tmpdir to current working dir + + +@xfail(reason='Issue #3037 - all scripts share the same global vars') +def test_several_scripts1(pyi_builder_spec): + """Verify each script has it's own global vars (original case, see issue + #2949). + """ + pyi_builder_spec.test_spec('several-scripts1.spec') + + +@xfail(reason='Issue #3037 - all scripts share the same global vars') +def test_several_scripts2(pyi_builder_spec): + """ + Verify each script has it's own global vars (basic test). + """ + pyi_builder_spec.test_spec('several-scripts2.spec') + + +@pytest.mark.win32 +def test_pe_checksum(pyi_builder): + import ctypes + from ctypes import wintypes + + pyi_builder.test_source("print('hello')") + exes = pyi_builder._find_executables('test_source') + assert exes + for exe in exes: + # Validate the PE checksum using the official Windows API for doing so. + # https://docs.microsoft.com/en-us/windows/win32/api/imagehlp/nf-imagehlp-mapfileandchecksumw + header_sum = wintypes.DWORD() + checksum = wintypes.DWORD() + assert ctypes.windll.imagehlp.MapFileAndCheckSumW( + ctypes.c_wchar_p(exe), + ctypes.byref(header_sum), + ctypes.byref(checksum)) == 0 + + assert header_sum.value == checksum.value + + +def test_onefile_longpath(pyi_builder, tmpdir): + """ + Verify that files with paths longer than 260 characters are correctly + extracted from the onefile build. See issue #5615." + """ + # The test is relevant only for onefile builds + if pyi_builder._mode != 'onefile': + pytest.skip('The test is relevant only to onefile builds.') + # Create data file with secret + _SECRET = 'LongDataPath' + src_filename = tmpdir / 'data.txt' + with open(src_filename, 'w') as fp: + fp.write(_SECRET) + # Generate long target filename/path; eight equivalents of SHA256 + # strings plus data.txt should push just the _MEIPASS-relative path + # beyond 260 characters... + dst_filename = os.path.join( + *[32*chr(c) for c in range(ord('A'), ord('A')+8)], 'data.txt') + assert len(dst_filename) >= 260 + # Name for --add-data + if is_win: + add_data_name = src_filename + ';' + os.path.dirname(dst_filename) + else: + add_data_name = src_filename + ':' + os.path.dirname(dst_filename) + + pyi_builder.test_source( + """ + import sys + import os + + data_file = os.path.join(sys._MEIPASS, r'{data_file}') + print("Reading secret from %r" % (data_file)) + with open(data_file, 'r') as fp: + secret = fp.read() + assert secret == r'{secret}' + """.format(data_file=dst_filename, secret=_SECRET), + ['--add-data', str(add_data_name)]) + + +@pytest.mark.win32 +@pytest.mark.parametrize("icon", ["icon_default", "icon_none", "icon_given"]) +def test_onefile_has_manifest(pyi_builder, icon): + """ + Verify that onefile builds on Windows end up having manifest + embedded. See issue #5624. + """ + from PyInstaller.utils.win32 import winmanifest + from PyInstaller import PACKAGEPATH + + # The test is relevant only for onefile builds + if pyi_builder._mode != 'onefile': + pytest.skip('The test is relevant only to onefile builds.') + # Icon type + if icon == 'icon_default': + # Default; no --icon argument + extra_args = [] + elif icon == 'icon_none': + # Disable icon completely; --icon NONE + extra_args = ['--icon', 'NONE'] + elif icon == 'icon_given': + # Locate pyinstaller's default icon, and explicitly give it + # via --icon argument + icon_path = os.path.join(PACKAGEPATH, 'bootloader', 'images', + 'icon-console.ico') + extra_args = ['--icon', icon_path] + # Build the executable... + pyi_builder.test_source("""print('Hello world!')""", extra_args) + # ... and ensure that it contains manifest + exes = pyi_builder._find_executables('test_source') + assert exes + for exe in exes: + res = winmanifest.GetManifestResources(exe) + assert res, "No manifest resources found!" diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_cliutils.py b/3rdparty/pyinstaller-4.3/tests/functional/test_cliutils.py new file mode 100644 index 0000000000000000000000000000000000000000..33afafd7f49fe8dcd392491102b795f960996c14 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_cliutils.py @@ -0,0 +1,25 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.cliutils import makespec + +def test_maskespec_basic(tmpdir, monkeypatch): + py = tmpdir.join('abcd.py').ensure() + print(); print(py) + spec = tmpdir.join('abcd.spec') + monkeypatch.setattr('sys.argv', ['foobar', str(py)]) + # changing cwd does not work, since DEFAULT_SPECPATH is set *very* early + monkeypatch.setattr('PyInstaller.building.makespec.DEFAULT_SPECPATH', + str(tmpdir)) + makespec.run() + assert spec.exists() + text = spec.read_text('utf-8') + assert 'Analysis' in text diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_django.py b/3rdparty/pyinstaller-4.3/tests/functional/test_django.py new file mode 100644 index 0000000000000000000000000000000000000000..58311bc04c9fb085c51fea632af7c704969e92f7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_django.py @@ -0,0 +1,40 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional tests for the Django content management system (CMS). +""" + +import pytest + +from PyInstaller.utils.tests import importorskip + + +# In Django 2.1, ``django/contrib/auth/password_validation.py``, line 168, which +# is +# +# ``DEFAULT_PASSWORD_LIST_PATH = Path(__file__).resolve().parent / 'common-passwords.txt.gz'``, +# +# the call to ``resolve()`` causes Python 3.5 to raise an exception that +# ``password_validation.pyc`` doesn't exist. Python 3.6 added the default +# argument ``strict=False``, which ignores this exception. This file is in the +# archive, but not the filesystem. +@importorskip('django') +# Django test might sometimes hang. +@pytest.mark.timeout(timeout=7*60) +def test_django(pyi_builder, monkeypatch, data_dir): + # Extend sys.path so PyInstaller could find modules from 'tmpdir/django/'. + monkeypatch.syspath_prepend(data_dir.strpath) + # Django uses manage.py as the main script. + script = str(data_dir / 'manage.py') + # Create the exe, run django command 'check' to do basic sanity + # checking of the executable. + pyi_builder.test_script(script, app_name='django_site', app_args=['check']) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hook_utilities.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hook_utilities.py new file mode 100644 index 0000000000000000000000000000000000000000..f1386adac66d7006e1bba6881fb0d4ea48bba679 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hook_utilities.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +""" +""" + +from subprocess import run, PIPE +from os.path import join + + +def test_collect_entry_point(pyi_builder_spec, script_dir, tmpdir): + """Test PyInstaller.utils.hooks.collect_entry_point(). + + On adding ``collect_entry_point('pytest11')`` to the spec file, the list + of modules exporting the 'pytest11' entry point should be same after + freezing. + + """ + import pkg_resources + plugins = sorted( + i.module_name for i in pkg_resources.iter_entry_points("pytest11")) + + assert len(plugins), "The pytest11 entry point appears to have moved." + + pyi_builder_spec.test_spec('list_pytest11_entry_point.spec') + exe = join(tmpdir, "dist", "list_pytest11_entry_point", + "list_pytest11_entry_point") + + p = run([exe], stdout=PIPE, check=True, universal_newlines=True) + collected_plugins = p.stdout.strip("\n").split("\n") + + assert collected_plugins == plugins diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_gi.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_gi.py new file mode 100644 index 0000000000000000000000000000000000000000..2cc7fdd39c87f6b34abd38e46cb643a9834fdadf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_gi.py @@ -0,0 +1,58 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional tests for PyGObject. +""" + +import pytest + +from PyInstaller.utils.tests import importorskip, parametrize + +# Names of all "gi.repository" packages provided by PyGObject to be +# tested below, typically corresponding to those packages hooked by PyInstaller. +gi_repositories = [('Gst', '1.0'), ('GLib', '2.0'), ('GModule', '2.0'), + ('GObject', '2.0'), ('GdkPixbuf', '2.0'), ('Gio', '2.0'), + ('Clutter', '1.0'), ('GtkClutter', '1.0'), + ('Champlain', '0.12'), ('GtkChamplain', '0.12')] +gi_repository_names = [x[0] for x in gi_repositories] + +# Names of the same packages, decorated to be skipped if unimportable. +gi_repositories_skipped_if_unimportable = [ + pytest.param(gi_repository_name, gi_repository_version, + marks=importorskip('gi.repository.' + gi_repository_name)) + for gi_repository_name, gi_repository_version in gi_repositories +] + + +# Test the usability of "gi.repository" packages provided by PyGObject. +@importorskip('gi.repository') +@parametrize( + ('repository_name', 'version'), + gi_repositories_skipped_if_unimportable, + # Ensure human-readable test parameter names. + ids=gi_repository_names) +def test_gi_repository(pyi_builder, repository_name, version): + ''' + Test the importability of the `gi.repository` subpackage with the passed + name installed with PyGObject (e.g., `GLib`, corresponding to the + `gi.repository.GLib` subpackage). Version '1.0' are for PyGObject >=1.0, + '2.0' for PyGObject >= 2.0 and some other libraries have strange version + (eg. Champlain) + ''' + + # Test the importability of this subpackage. + pyi_builder.test_source(''' + import gi + gi.require_version('{repository_name}', '{version}') + from gi.repository import {repository_name} + print({repository_name}) + '''.format(repository_name=repository_name, version=version)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_matplotlib.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_matplotlib.py new file mode 100644 index 0000000000000000000000000000000000000000..4509ea343dffec99e1aa1dd622969838f686a856 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_matplotlib.py @@ -0,0 +1,111 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional tests for Matplotlib. +""" + +import pytest +from PyInstaller.utils.tests import importorskip + + +# List of 3-tuples "(backend_name, package_name, binding)", +# where: +# +# * "backend_name" is the name of a Matplotlib backend to be tested below. +# * "package_name" is the name of the external package required by this backend. +# * "binding" is the binding to use (and to set environment-variable QT_API to). +backend_rcParams_key_values = [ + ('Qt5Agg', 'PyQt5', 'pyqt5'), + ('Qt5Agg', 'PySide2', 'pyside2'), +] + +# Same list, decorated to skip all backends whose packages are unimportable#. +backend_rcParams_key_values_skipped_if_unimportable = [ + pytest.param(*backend_rcParams_key_value, + marks=importorskip(backend_rcParams_key_value[1])) + for backend_rcParams_key_value in backend_rcParams_key_values +] + +print(backend_rcParams_key_values_skipped_if_unimportable) + +# Names of all packages required by backends listed above. +package_names = [ + backend_rcParams_key_value[1] + for backend_rcParams_key_value in backend_rcParams_key_values +] + + +# Test Matplotlib with access to only one backend at a time. +@importorskip('matplotlib') +@pytest.mark.parametrize( + 'backend_name, package_name, binding', + backend_rcParams_key_values_skipped_if_unimportable, + ids=package_names) +def test_matplotlib(pyi_builder, monkeypatch, + backend_name, package_name, binding): + ''' + Test Matplotlib with the passed backend enabled, the passed backend package + included with this frozen application, all other backend packages explicitly + excluded from this frozen application, and the passed rcParam key set to the + corresponding passed value if that key is _not_ `None` or ignore that value + otherwise. + ''' + + # PyInstaller options excluding all backend packages except the passed + # backend package. This is especially critical for Qt backend packages + # (e.g., "PyQt5", "PySide2"). On first importation, Matplotlib attempts to + # import all available Qt packages. However, runtime PyInstaller hooks fail + # when multiple Qt packages are frozen into the same application. For each + # such package, all other Qt packages must be excluded. + pyi_args = [ + '--exclude-module=' + package_name_excludable + for package_name_excludable in package_names + if package_name_excludable != package_name + ] + + # Script to be tested, enabling this Qt backend. + test_script = (""" + import matplotlib, os, sys, tempfile + + # Localize test parameters. + backend_name = {backend_name!r} + binding = {binding!r} + + # Report these parameters. + print('Testing Matplotlib with backend', repr(backend_name), + 'and binding ($QT_API)', repr(binding)) + + # Configure Matplotlib *BEFORE* calling any Matplotlib functions. + matplotlib.rcParams['backend'] = backend_name + os.environ['QT_API'] = binding + + # Enable the desired backend *BEFORE* plotting with this backend. + matplotlib.use(backend_name) + + # A runtime hook should force Matplotlib to create its configuration + # directory in a temporary directory rather than in $HOME/.matplotlib. + configdir = os.environ['MPLCONFIGDIR'] + print('MPLCONFIGDIR:', repr(configdir)) + if not configdir.startswith(tempfile.gettempdir()): + raise SystemExit('MPLCONFIGDIR not pointing to temp directory.') + + # Test access to the standard 'mpl_toolkits' namespace package installed + # with Matplotlib. Note that this import was reported to fail under + # Matplotlib 1.3.0. + from mpl_toolkits import axes_grid1 + """.format( + backend_name=backend_name, + binding=binding, + )) + + # Test this script. + pyi_builder.test_source(test_script, pyi_args=pyi_args) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_pil.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_pil.py new file mode 100644 index 0000000000000000000000000000000000000000..cdd3e193f651e6420110459ee4666637649642b2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_pil.py @@ -0,0 +1,81 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional tests for the Python Imaging Library (PIL). + +Note that the original unmaintained PIL has been obsoleted by the PIL-compatible +fork Pillow, which retains the same Python package `PIL`. +""" + +from PyInstaller.compat import modname_tkinter, is_darwin +from PyInstaller.utils.tests import importorskip, skip, xfail + + +# "excludedimports" support is currently non-deterministic and hence cannot be +# marked as @xfail. If this test were marked as @xfail but succeeded, py.test +# would record this test as an XPASS failure (i.e., an unexpected success). +@importorskip('PIL') +@importorskip(modname_tkinter) +@skip(reason='"excludedimports" support is non-deterministically broken.') +def test_pil_no_tkinter(pyi_builder): + """ + Ensure that the Tkinter package excluded by `PIL` package hooks is + unimportable by frozen applications explicitly importing only the latter. + """ + + pyi_builder.test_source(""" + import PIL.Image + + # Dynamically importing the Tkinter package should fail with an + # "ImportError", implying "PIL" package hooks successfully excluded + # Tkinter. To prevent PyInstaller from parsing this import and thus + # freezing this extension with this test, this import is dynamic. + try: + __import__('{modname_tkinter}') + raise SystemExit('ERROR: Module {modname_tkinter} is bundled.') + except ImportError: + pass + + # Dynamically importing the "_tkinter" shared library should also fail. + try: + __import__('_tkinter') + raise SystemExit('ERROR: Module _tkinter is bundled.') + except ImportError: + pass + """.format(modname_tkinter=modname_tkinter)) + + +@importorskip('PIL') +@importorskip(modname_tkinter) +@xfail(is_darwin, reason='Issue #1895. Known to fail with macpython - python.org binary.') +def test_pil_tkinter(pyi_builder): + """ + Ensure that the Tkinter package excluded by `PIL` package hooks is + importable by frozen applications explicitly importing both. + + == See Also == + + * PyInstaller [issue #1584](https://github.com/pyinstaller/pyinstaller/issues/1584). + """ + + pyi_builder.test_source(""" + import PIL.Image + + # Statically importing the Tkinter package should succeed, implying this + # importation successfully overrode the exclusion of this package + # requested by "PIL" package hooks. To ensure PyInstaller parses this + # import and freezes this package with this test, this import is static. + try: + import {modname_tkinter} + except ImportError: + raise SystemExit('ERROR: Module {modname_tkinter} is NOT bundled.') + """.format(modname_tkinter=modname_tkinter)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_pkg_resources.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_pkg_resources.py new file mode 100644 index 0000000000000000000000000000000000000000..c447ed045a1bfe22684a6ab5e63db327e3592bf7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_pkg_resources.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.tests import importorskip + +@importorskip('pkg_resources') +def test_pkg_resources_importable(pyi_builder): + """ + Check that a trivial example using pkg_resources does build. + """ + pyi_builder.test_source( + """ + import pkg_resources + pkg_resources.working_set.require() + """) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_scipy.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_scipy.py new file mode 100644 index 0000000000000000000000000000000000000000..cf865e473bfacd1d390f118e805ed26c6e38e75b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_scipy.py @@ -0,0 +1,62 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional tests for SciPy. +""" + +from PyInstaller.compat import is_darwin, is_win +from PyInstaller.utils.tests import importorskip, skip, xfail + + +@xfail(is_win, reason='Issue scipy/scipy#5461.') +@xfail(is_darwin, reason='Issue #1895.') +@importorskip('scipy') +def test_scipy(pyi_builder): + pyi_builder.test_source( + """ + from distutils.version import LooseVersion + + # Test top-level SciPy importability. + import scipy + from scipy import * + + # Test hooked SciPy modules. + import scipy.io.matlab + import scipy.sparse.csgraph + + # Test problematic SciPy modules. + import scipy.linalg + import scipy.signal + + # SciPy >= 0.16 privatized the previously public "scipy.lib" package as + # "scipy._lib". Since this package is problematic, test its + # importability regardless of SciPy version. + if LooseVersion(scipy.__version__) >= LooseVersion('0.16.0'): + import scipy._lib + else: + import scipy.lib + """) + + +@xfail(is_win, reason='Issue scipy/scipy#5461.') +@xfail(is_darwin, reason='Issue #1895.') +@importorskip('scipy') +def test_scipy_special(pyi_builder): + """ + Test the importability of the `scipy.special` package and related hooks. + + This importation _must_ be tested independent of the importation of all + other problematic SciPy packages and modules. Combining this test with other + SciPy tests (e.g., `test_scipy()`) fails to properly exercise the hidden + imports required by this package. + """ + pyi_builder.test_source("""import scipy.special""") diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_six.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_six.py new file mode 100644 index 0000000000000000000000000000000000000000..6c65db1eadce57205f2c8daad0b5a3003a1e5efe --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_six.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.tests import importorskip + + +@importorskip('six.moves') +def test_six_moves(pyi_builder): + pyi_builder.test_source( + """ + from six.moves import UserList + UserList + """) + + +# Run the same test a second time to trigger errors like +# Target module "six.moves.urllib" already imported as "AliasNode(…)" +# caused by PyiModuleGraph being cached in a insufficient way. +@importorskip('six.moves') +def test_six_moves_2nd_run(pyi_builder): + return test_six_moves(pyi_builder) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_wx_lib_pubsub.py b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_wx_lib_pubsub.py new file mode 100644 index 0000000000000000000000000000000000000000..0bec8c40c16c07e936d36017d509e4e5abd2f8de --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_hooks/test_wx_lib_pubsub.py @@ -0,0 +1,67 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +Functional tests for the PyPubSub API bundled with wxPython. + +Since wxPython is currently only stably supported under Python 2, these tests +are implicitly skipped under Python 3. +""" + +import pkg_resources +from PyInstaller.utils.tests import importorskip, xfail + + +try: + # These tests fail under wxPython versions which support multiple pubsub + # APIs. See https://github.com/pyinstaller/pyinstaller/issues/1704. + wxPython_fail = ( pkg_resources.parse_version('2.8.10') <= + pkg_resources.get_distribution('wxPython').parsed_version < + pkg_resources.parse_version('2.9') ) +except pkg_resources.DistributionNotFound: + # Linux wxPython installations don't provide distribution metadata, but pass + # all the tests below. + wxPython_fail = False + +@xfail +@xfail(wxPython_fail, reason='Unsupported wxPython version') +@importorskip('wx.lib.pubsub') +def test_wx_lib_pubsub_protocol_default(pyi_builder): + """ + Functional test applicable to all PyPubSub versions. + """ + pyi_builder.test_script('pyi_hooks/wx_lib_pubsub.py') + +@xfail +# This test will pass when test_import.test_import_respects_path passes, since +# that test provides a simple example of what causes this wxPython version to +# fail. +@xfail(wxPython_fail, + reason='PyInstaller does not support this wxPython version') +@importorskip('wx.lib.pubsub.core') +def test_wx_lib_pubsub_protocol_kwargs(pyi_builder): + """ + Functional test specific to version 3 of the PyPubSub API. + + The `wx.lib.pubsub.core` package is specific to this version. + """ + pyi_builder.test_script('pyi_hooks/wx_lib_pubsub_setupkwargs.py') + +@xfail +@xfail(wxPython_fail, reason='Unsupported wxPython version') +@importorskip('wx.lib.pubsub.core') +def test_wx_lib_pubsub_protocol_arg1(pyi_builder): + """ + Functional test specific to version 3 of the PyPubSub API. + + The `wx.lib.pubsub.core` package is specific to this version. + """ + pyi_builder.test_script('pyi_hooks/wx_lib_pubsub_setuparg1.py') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_import.py b/3rdparty/pyinstaller-4.3/tests/functional/test_import.py new file mode 100644 index 0000000000000000000000000000000000000000..d176c8ca17c83e6c9f3bf3626c29f7d7595cdf80 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_import.py @@ -0,0 +1,660 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import glob +import ctypes +import ctypes.util + +import pytest + +from PyInstaller.compat import is_darwin, is_win +from PyInstaller.utils.tests import skipif, importorskip, \ + skipif_no_compiler, xfail, has_compiler + +# :todo: find a way to get this from `conftest` or such +# Directory with testing modules used in some tests. +_MODULES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'modules') + +def test_nameclash(pyi_builder): + # test-case for issue #964: Nameclashes in module information gathering + # All pyinstaller specific module attributes should be prefixed, + # to avoid nameclashes. + pyi_builder.test_source( + """ + import pyi_testmod_nameclash.nameclash + """) + + +def test_relative_import(pyi_builder): + pyi_builder.test_source( + """ + import pyi_testmod_relimp.B.C + from pyi_testmod_relimp.F import H + import pyi_testmod_relimp.relimp1 + + assert pyi_testmod_relimp.relimp1.name == 'pyi_testmod_relimp.relimp1' + assert pyi_testmod_relimp.B.C.name == 'pyi_testmod_relimp.B.C' + assert pyi_testmod_relimp.F.H.name == 'pyi_testmod_relimp.F.H' + """ + ) + + +def test_relative_import2(pyi_builder): + pyi_builder.test_source( + """ + import pyi_testmod_relimp2.bar + import pyi_testmod_relimp2.bar.bar2 + + pyi_testmod_relimp2.bar.say_hello_please() + pyi_testmod_relimp2.bar.bar2.say_hello_please() + """ + ) + + +def test_relative_import3(pyi_builder): + pyi_builder.test_source( + """ + from pyi_testmod_relimp3a.aa import a1 + print(a1.getString()) + """ + ) + +@xfail(reason='modulegraph bug') +def test_import_missing_submodule(pyi_builder): + # If a submodule is missing, the parent submodule must be imported. + pyi_builder.test_source( + """ + try: + import pyi_testmod_missing_submod.aaa.bbb + except ImportError as e: + assert e.message.endswith(' bbb') + else: + raise RuntimeError('Buggy test-case: module' + 'pyi_testmod_missing_submod.aaa.bbb must not exist') + # parent module exists and must be included + __import__('pyi_testmod_missing_submod.aaa') + """) + +def test_import_submodule_global_shadowed(pyi_builder): + """ + Functional test validating issue #1919. + + `ModuleGraph` previously ignored `from`-style imports of submodules from + packages whose `__init__` submodules declared global variables of the same + name as those submodules. This test exercises this sporadic edge case by + unsuccessfully importing a submodule "shadowed" by a global variable of the + same name defined by their package's `__init__` submodule. + """ + + pyi_builder.test_source( + """ + # Assert that this submodule is shadowed by a string global variable. + from pyi_testmod_submodule_global_shadowed import submodule + assert type(submodule) == str + + # Assert that this submodule is still frozen into this test application. + # To do so: + # + # 1. Delete this global variable from its parent package. + # 2. Assert that this submodule is unshadowed by this global variable. + import pyi_testmod_submodule_global_shadowed, sys + del pyi_testmod_submodule_global_shadowed.submodule + from pyi_testmod_submodule_global_shadowed import submodule + assert type(submodule) == type(sys) + """) + + +def test_import_submodule_global_unshadowed(pyi_builder): + ''' + Functional test validating issue #1919. + + `ModuleGraph` previously ignored `from`-style imports of submodules from + packages whose `__init__` submodules declared global variables of the same + name as those submodules. This test exercises this sporadic edge case by + successfully importing a submodule: + + * Initially "shadowed" by a global variable of the same name defined by + their package's `__init__` submodule. + * Subsequently "unshadowed" when this global variable is then undefined by + their package's `__init__` submodule. + ''' + + pyi_builder.test_source( + """ + # Assert that this submodule is unshadowed by this global variable. + import sys + from pyi_testmod_submodule_global_unshadowed import submodule + assert type(submodule) == type(sys) + """) + + +def test_import_submodule_from_aliased_pkg(pyi_builder, script_dir): + pyi_builder.test_source( + """ + import sys + import pyi_testmod_submodule_from_aliased_pkg + + sys.modules['alias_name'] = pyi_testmod_submodule_from_aliased_pkg + + from alias_name import submodule + """, + ['--additional-hooks-dir=%s' % script_dir.join('pyi_hooks')]) + + +def test_module_with_coding_utf8(pyi_builder): + # Module ``utf8_encoded_module`` simply has an ``coding`` header + # and uses same German umlauts. + pyi_builder.test_source("import module_with_coding_utf8") + + +def test_hiddenimport(pyi_builder): + # The script simply does nothing, not even print out a line. + # The check is done by comparing with logs/test_hiddenimport.toc + pyi_builder.test_source('pass', + ['--hidden-import=a_hidden_import']) + + + +def test_error_during_import(pyi_builder): + # See ticket #27: historically, PyInstaller was catching all + # errors during imports... + pyi_builder.test_source( + """ + try: + import error_during_import2 + except KeyError: + print("OK") + else: + raise RuntimeError("failure!") + """) + +def test_import_non_existing_raises_import_error(pyi_builder): + pyi_builder.test_source( + """ + try: + import zzzzzz.zzzzzzzz.zzzzzzz.non.existing.module.error_during_import2 + except ImportError: + print("OK") + else: + raise RuntimeError("ImportError not raised") + """) + + +# Verify that __path__ is respected for imports from the filesystem: +# +# * pyi_testmod_path/ +# +# * __init__.py -- inserts a/ into __path__, then imports b, which now refers +# to a/b.py, not ./b.py. +# * b.py - raises an exception. **Should not be imported.** +# * a/ -- contains no __init__.py. +# +# * b.py - Empty. Should be imported. +@xfail(reason='__path__ not respected for filesystem modules.') +def test_import_respects_path(pyi_builder, script_dir): + pyi_builder.test_source('import pyi_testmod_path', + ['--additional-hooks-dir='+script_dir.join('pyi_hooks').strpath]) + + +# Verify correct handling of sys.meta_path redirects like pkg_resources 28.6.1 +# does: '_vendor.xxx' gets imported as 'extern.xxx' and using '__import__()'. +# Note: This also requires a hook, since 'pyi_testmod_metapath1._vendor' is +# not imported directly and won't be found by modulegraph. +def test_import_metapath1(pyi_builder, script_dir): + pyi_builder.test_source('import pyi_testmod_metapath1', + ['--additional-hooks-dir='+script_dir.join('pyi_hooks').strpath]) + + +@importorskip('PyQt5') +def test_import_pyqt5_uic_port(script_dir, pyi_builder): + extra_path = os.path.join(_MODULES_DIR, 'pyi_import_pyqt_uic_port') + pyi_builder.test_script('pyi_import_pyqt5_uic_port.py', + # Add the path to a fake PyQt5 package, used for this test. + pyi_args=['--path', extra_path]) + + +#--- ctypes ---- + +@skipif_no_compiler +@skipif(is_win, reason="CDLL(None) seams to be not valid on Windows") +def test_ctypes_CDLL_None(pyi_builder): + # Make sure we are able to load CDLL(None) + # -> pip does this for some reason + pyi_builder.test_source( + """ + import ctypes, ctypes.util + lib = ctypes.CDLL(None) + assert lib is not None + """) + +import PyInstaller.depend.utils +__orig_resolveCtypesImports = PyInstaller.depend.utils._resolveCtypesImports + +def __monkeypatch_resolveCtypesImports(monkeypatch, compiled_dylib): + + def mocked_resolveCtypesImports(*args, **kwargs): + from PyInstaller.config import CONF + old_pathex = CONF['pathex'] + CONF['pathex'].append(str(compiled_dylib)) + res = __orig_resolveCtypesImports(*args, **kwargs) + CONF['pathex'] = old_pathex + return res + + # Add the path to ctypes_dylib to pathex, only for + # _resolveCtypesImports. We can not monkeypath CONF['pathex'] + # here, as it will be overwritten when pyi_builder is starting up. + # So be monkeypatch _resolveCtypesImports by a wrapper. + monkeypatch.setattr(PyInstaller.depend.utils, "_resolveCtypesImports", + mocked_resolveCtypesImports) + + +#FIXME: For reusability, move this to "PyInstaller.utils.tests". +def skip_if_lib_missing(libname, text=None): + """ + pytest decorator to evaluate the required shared lib. + + :param libname: Name of the required library. + :param text: Text to put into the reason message + (defaults to 'lib%s.so' % libname) + + :return: pytest decorator with a reason. + """ + soname = ctypes.util.find_library(libname) + if not text: + text = "lib%s.so" % libname + # Return pytest decorator. + return skipif(not (soname and ctypes.CDLL(soname)), + reason="required %s missing" % text) + + +_template_ctypes_CDLL_find_library = """ + import ctypes, ctypes.util, sys, os + lib = ctypes.CDLL(ctypes.util.find_library(%(libname)r)) + print(lib) + assert lib is not None and lib._name is not None + if getattr(sys, 'frozen', False): + soname = ctypes.util.find_library(%(libname)r) + print(soname) + libfile = os.path.join(sys._MEIPASS, soname) + print(libfile) + assert os.path.isfile(libfile), '%%s is missing' %% soname + print('>>> file found') + """ + +# Ghostscript's libgs.so should be available in may Unix/Linux systems +# +# At least on Linux, we can not use our own `ctypes_dylib` because +# `find_library` does not consult LD_LIBRARY_PATH and hence does not +# find our lib. Anyway, this test tests the path of the loaded lib and +# thus checks if libgs.so is included into the frozen exe. +# TODO: Check how this behaves on other platforms. +@skip_if_lib_missing('gs', 'libgs.so (Ghostscript)') +def test_ctypes_CDLL_find_library__gs(pyi_builder): + libname = 'gs' + pyi_builder.test_source(_template_ctypes_CDLL_find_library % locals()) + + +#-- Generate test-cases for the different types of ctypes objects. + +_template_ctypes_test = """ + print(lib) + assert lib is not None and lib._name is not None + import sys, os + if getattr(sys, 'frozen', False): + libfile = os.path.join(sys._MEIPASS, %(soname)r) + print(libfile) + assert os.path.isfile(libfile), '%(soname)s is missing' + print('>>> file found') + """ + +parameters = [] +ids = [] +for prefix in ('', 'ctypes.'): + for funcname in ('CDLL', 'PyDLL', 'WinDLL', 'OleDLL', 'cdll.LoadLibrary'): + ids.append(prefix+funcname) + params = (prefix+funcname, ids[-1]) + # Marking doesn't seem to chain here, so select just one skippping mark + # instead of both. + if not has_compiler: + params = pytest.param(*params, marks=skipif_no_compiler(params)) + elif funcname in ("WinDLL", "OleDLL"): + # WinDLL, OleDLL only work on windows. + params = pytest.param(*params, marks=pytest.mark.win32) + parameters.append(params) + +@pytest.mark.parametrize("funcname,test_id", parameters, ids=ids) +def test_ctypes_gen(pyi_builder, monkeypatch, funcname, compiled_dylib, test_id): + # evaluate the soname here, so the test-code contains a constant. + # We want the name of the dynamically-loaded library only, not its path. + # See discussion in https://github.com/pyinstaller/pyinstaller/pull/1478#issuecomment-139622994. + soname = compiled_dylib.basename + + source = """ + import ctypes ; from ctypes import * + lib = %s(%%(soname)r) + """ % funcname + _template_ctypes_test + + __monkeypatch_resolveCtypesImports(monkeypatch, compiled_dylib.dirname) + pyi_builder.test_source(source % locals(), test_id=test_id) + + +@pytest.mark.parametrize("funcname,test_id", parameters, ids=ids) +def test_ctypes_in_func_gen(pyi_builder, monkeypatch, funcname, + compiled_dylib, test_id): + """ + This is much like test_ctypes_gen except that the ctypes calls + are in a function. See issue #1620. + """ + soname = compiled_dylib.basename + + source = (""" + import ctypes ; from ctypes import * + def f(): + def g(): + lib = %s(%%(soname)r) + """ % funcname + + _template_ctypes_test + """ + g() + f() + """) + __monkeypatch_resolveCtypesImports(monkeypatch, compiled_dylib.dirname) + pyi_builder.test_source(source % locals(), test_id=test_id) + + +def test_ctypes_cdll_builtin_extension(pyi_builder): + # Take a built-in that is provided as an extension + builtin_ext = '_sha256' + if builtin_ext in sys.builtin_module_names: + # On Windows, built-ins do not seem to be extensions + pytest.skip(f"{builtin_ext} is a built-in module without extension.") + + pyi_builder.test_source( + """ + import ctypes + import importlib.machinery + + # Try to load CDLL with all possible extension suffices; this + # should fail in all cases, as built-in extensions should not + # be in the ctypes' search path. + builtin_ext = '{0}' + for suffix in importlib.machinery.EXTENSION_SUFFIXES: + try: + lib = ctypes.CDLL(builtin_ext + suffix) + except OSError: + lib = None + assert lib is None, "Built-in extension picked up by ctypes.CDLL!" + """.format(builtin_ext)) + + +# TODO: Add test-cases for the prefabricated library loaders supporting +# attribute accesses on windows. Example:: +# +# cdll.kernel32.GetModuleHandleA(None) +# +# Of course we need to use dlls which is not are commony available on +# windows but mot excluded in PyInstaller.depend.dylib + + +def test_egg_unzipped(pyi_builder): + pathex = os.path.join(_MODULES_DIR, 'pyi_egg_unzipped.egg') + pyi_builder.test_source( + """ + # This code is part of the package for testing eggs in `PyInstaller`. + import os + import pkg_resources + + # Test ability to load resource. + expected_data = 'This is data file for `unzipped`.'.encode('ascii') + t = pkg_resources.resource_string('unzipped_egg', 'data/datafile.txt') + print('Resource: %s' % t) + t_filename = pkg_resources.resource_filename('unzipped_egg', 'data/datafile.txt') + print('Resource filename: %s' % t_filename) + assert t.rstrip() == expected_data + + # Test ability that module from .egg is able to load resource. + import unzipped_egg + assert unzipped_egg.data == expected_data + + print('Okay.') + """, + pyi_args=['--paths', pathex], + ) + + +def test_egg_zipped(pyi_builder): + pathex = os.path.join(_MODULES_DIR, 'pyi_egg_zipped.egg') + pyi_builder.test_source( + """ + # This code is part of the package for testing eggs in `PyInstaller`. + import os + import pkg_resources + + # Test ability to load resource. + expected_data = 'This is data file for `zipped`.'.encode('ascii') + t = pkg_resources.resource_string('zipped_egg', 'data/datafile.txt') + print('Resource: %s' % t) + t_filename = pkg_resources.resource_filename('zipped_egg', 'data/datafile.txt') + print('Resource filename: %s' % t_filename) + assert t.rstrip() == expected_data + + # Test ability that module from .egg is able to load resource. + import zipped_egg + assert zipped_egg.data == expected_data + + print('Okay.') + """, + pyi_args=['--paths', pathex], + ) + + +#--- namespaces --- + +def test_nspkg1(pyi_builder): + # Test inclusion of namespace packages implemented using + # pkg_resources.declare_namespace + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg1-pkg', '*.egg')) + pyi_builder.test_source( + """ + import nspkg1.aaa + import nspkg1.bbb.zzz + import nspkg1.ccc + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + + +def test_nspkg1_empty(pyi_builder): + # Test inclusion of a namespace-only packages in an zipped egg. + # This package only defines the namespace, nothing is contained there. + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg1-pkg', '*.egg')) + pyi_builder.test_source( + """ + import nspkg1 + print (nspkg1) + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + + +def test_nspkg1_bbb_zzz(pyi_builder): + # Test inclusion of a namespace packages in an zipped egg + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg1-pkg', '*.egg')) + pyi_builder.test_source( + """ + import nspkg1.bbb.zzz + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + + +def test_nspkg2(pyi_builder): + # Test inclusion of namespace packages implemented as nspkg.pth-files + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg2-pkg')) + pyi_builder.test_source( + """ + import nspkg2.aaa + import nspkg2.bbb.zzz + import nspkg2.ccc + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + + +@xfail(reason="modulegraph implements `pkgutil.extend_path` wrong") +def test_nspkg3(pyi_builder): + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg3-pkg', '*.egg')) + pyi_builder.test_source( + """ + import nspkg3.aaa + try: + # pkgutil ignores items of sys.path that are not strings + # referring to existing directories. So this zipped egg + # *must* be ignored. + import nspkg3.bbb.zzz + except ImportError: + pass + else: + raise SystemExit('nspkg3.bbb.zzz found but should not') + try: + import nspkg3.a + except ImportError: + pass + else: + raise SystemExit('nspkg3.a found but should not') + import nspkg3.ccc + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + +def test_nspkg3_empty(pyi_builder): + # Test inclusion of a namespace-only package in a zipped egg + # using pkgutil.extend_path. + # This package only defines namespace, nothing is contained there. + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg3-pkg', '*_empty.egg')) + pyi_builder.test_source( + """ + import nspkg3 + print (nspkg3) + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + +def test_nspkg3_aaa(pyi_builder): + # Test inclusion of a namespace package in an directory using + # pkgutil.extend_path + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg3-pkg', '*.egg')) + pyi_builder.test_source( + """ + import nspkg3.aaa + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + +def test_nspkg3_bbb_zzz(pyi_builder): + # Test inclusion of a namespace package in an zipped egg using + # pkgutil.extend_path + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg3-pkg', '*.egg')) + pyi_builder.test_source( + """ + import nspkg3.bbb.zzz + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + +def test_nspkg_pep420(pyi_builder): + # Test inclusion of PEP 420 namespace packages. + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg-pep420', 'path*')) + pyi_builder.test_source( + """ + import package.sub1 + import package.sub2 + import package.subpackage.sub + import package.nspkg.mod + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + + +def test_nspkg_attributes(pyi_builder): + # Test that non-PEP-420 namespace packages (e.g., the ones using + # pkg_resources.declare_namespace) have proper attributes: + # * __path__ attribute should contain at least one path + # * __file__ attribute should point to an __init__ file within __path__ + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg1-pkg', '*.egg')) + pyi_builder.test_source( + """ + import os + import nspkg1 + + def validate_nspkg(pkg): + from sys import version_info + # Validate __path__ + path = getattr(pkg, '__path__', None) + assert path is not None and len(path) >= 1, "invalid __path__" + # Validate __file__ + file = pkg.__file__ + assert os.path.dirname(file) in path, \ + "dirname(__file__) does not point to __path__" + assert os.path.basename(file).startswith('__init__.'), \ + "basename(__file__) does not start with __init__.!" + + validate_nspkg(nspkg1) + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + + +def test_nspkg_attributes_pep420(pyi_builder): + # Test that PEP-420 namespace packages have proper attributes: + # * __path__ should contain at least one path + # * __file__ should be unset (python 3.6) or None (python 3.7 and later) + pathex = glob.glob(os.path.join(_MODULES_DIR, 'nspkg-pep420', 'path*')) + pyi_builder.test_source( + """ + import package + import package.nspkg + + def validate_nspkg_pep420(pkg): + from sys import version_info + # Validate __path__ + path = getattr(pkg, '__path__', None) + assert path is not None and len(path) >= 1, "invalid __path__" + # Validate __file__ + if version_info[0:2] < (3, 7): + assert not hasattr(pkg, '__file__'), "invalid __file__" + else: + assert getattr(pkg, '__file__') is None, "invalid __file__" + + validate_nspkg_pep420(package) + validate_nspkg_pep420(package.nspkg) + """, + pyi_args=['--paths', os.pathsep.join(pathex)], + ) + +#--- hooks related stuff --- + +def test_pkg_without_hook_for_pkg(pyi_builder, script_dir): + # The package `pkg_without_hook_for_pkg` does not have a hook, but + # `pkg_without_hook_for_pkg.sub1` has one. And this hook includes + # the "hidden" import `pkg_without_hook_for_pkg.sub1.sub11` + pyi_builder.test_source( + 'import pkg_without_hook_for_pkg.sub1', + ['--additional-hooks-dir=%s' % script_dir.join('pyi_hooks')]) + + +@xfail(is_darwin, reason='Issue #1895.') +def test_app_with_plugin(pyi_builder, data_dir, monkeypatch): + datas = os.pathsep.join(('data/*/static_plugin.py', os.curdir)) + pyi_builder.test_script('pyi_app_with_plugin.py', + pyi_args=['--add-data', datas]) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_import_pep302.py b/3rdparty/pyinstaller-4.3/tests/functional/test_import_pep302.py new file mode 100644 index 0000000000000000000000000000000000000000..e56bbc08fc56a4f63fc9c0d05c70bc4ed01968fc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_import_pep302.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# PEP-302 import hooks specification contain section 'Optional +# Extensions to the Importer Protocol' +# +# This section is meant to be optional but the reality is different. +# Some Python modules (e.g. Flask) depends on implementation of these +# optional functions: +# +# loader.is_package(fullmodname) +# loader.get_code(fullmodname) +# loader.get_source(fullmodname) +# +# This test-cases test the return values of these functions for +# importers from pyimod03_importers module. + +# Note: The modules need to be imported at the end of the resp. code. +# Otherwise the pkgutil-functions take a very different branch +# (since the module is already in sys.modules) and what we want +# to test will not be tested. + + +def test_pep302_loader_builtin(pyi_builder): + pyi_builder.test_source( + """ + mod = 'sys' + import pkgutil + ldr = pkgutil.get_loader(mod) + assert ldr + assert ldr.is_package(mod) == False + assert ldr.get_code(mod) is None + assert ldr.get_source(mod) is None + """) + + +def test_pep302_loader_frozen_module(pyi_builder): + pyi_builder.test_source( + """ + mod = 'compileall' + import pkgutil + ldr = pkgutil.get_loader(mod) + assert ldr + assert ldr.is_package(mod) == False + assert ldr.get_code(mod) is not None + assert ldr.get_source(mod) is None + # Import at the very end, just to get the module frozen. + import compileall + """) + + +def test_pep302_loader_frozen_package(pyi_builder): + pyi_builder.test_source( + """ + mod = 'distutils' + import pkgutil + ldr = pkgutil.get_loader(mod) + assert ldr + assert ldr.is_package(mod) == True + assert ldr.get_code(mod) is not None + assert ldr.get_source(mod) is None + # Import at the very end, just to get the module frozen. + import distutils + """) + + +def test_pep302_loader_frozen_submodule(pyi_builder): + pyi_builder.test_source( + """ + mod = 'distutils.config' + import pkgutil + ldr = pkgutil.get_loader(mod) + assert ldr + assert ldr.is_package(mod) == False + assert ldr.get_code(mod) is not None + assert ldr.get_source(mod) is None + # Import at the very end, just to get the module frozen. + import distutils.config + """) + + +def test_pep302_loader_cextension(pyi_builder): + pyi_builder.test_source( + """ + mod = '_sqlite3' + import pkgutil + ldr = pkgutil.get_loader(mod) + assert ldr + assert ldr.is_package(mod) == False + assert ldr.get_code(mod) is None + assert ldr.get_source(mod) is None + # Import at the very end, just to get the module frozen. + import sqlite3 + """) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_interactive.py b/3rdparty/pyinstaller-4.3/tests/functional/test_interactive.py new file mode 100644 index 0000000000000000000000000000000000000000..bc7a96fbb8c308238740b1329887a4bcfc66aa8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_interactive.py @@ -0,0 +1,34 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + + +""" +Interactive tests are successful when they are able to run +the executable for some time. Otherwise it is marked as fail. + +Note: All tests in this file should use the argument 'runtime'. +""" +import pytest + +from PyInstaller.utils.tests import importorskip, xfail +from PyInstaller.compat import is_win + +_RUNTIME = 10 # In seconds. + + +@importorskip('IPython') +@pytest.mark.skipif(is_win, reason='See issue #3535.') +def test_ipython(pyi_builder): + pyi_builder.test_source( + """ + from IPython import embed + embed() + """, runtime=_RUNTIME) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_libraries.py b/3rdparty/pyinstaller-4.3/tests/functional/test_libraries.py new file mode 100644 index 0000000000000000000000000000000000000000..12f0b4457d94ac6766a2a711d5a8b871a7117821 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_libraries.py @@ -0,0 +1,641 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Library imports +# --------------- +import os + +# Third-party imports +# ------------------- +import pytest +import py + +# Local imports +# ------------- +from PyInstaller.compat import is_win, is_darwin, is_linux, is_64bits +from PyInstaller.utils.hooks import is_module_satisfies +from PyInstaller.utils.tests import importorskip, xfail, skipif + +# :todo: find a way to get this from `conftest` or such +# Directory with testing modules used in some tests. +_MODULES_DIR = py.path.local(os.path.abspath(__file__)).dirpath('modules') +_DATA_DIR = py.path.local(os.path.abspath(__file__)).dirpath('data') + + +@importorskip('gevent') +def test_gevent(pyi_builder): + pyi_builder.test_source( + """ + import gevent + gevent.spawn(lambda: x) + """, + # reduce footprint of the test (and avoid issued introduced by one of + # these packages breaking) + excludes=["PySide2", "PyQt5", "numpy", "scipy"]) + + +@importorskip('gevent') +def test_gevent_monkey(pyi_builder): + pyi_builder.test_source( + """ + from gevent.monkey import patch_all + patch_all() + """, + # reduce footprint of the test (and avoid issued introduced by one of + # these packages breaking) + excludes=["PySide2", "PyQt5", "numpy", "scipy"]) + + +@xfail(is_darwin, reason='Issue #1895.') +def test_tkinter(pyi_builder): + pyi_builder.test_script('pyi_lib_tkinter.py') + + +@xfail(is_darwin, reason='Issue #1895.') +@importorskip('FixTk') +def test_tkinter_FixTk(pyi_builder): + # check if Tkinter includes FixTk + # TODO: Python 3 contains module + # 'tkinter._fix' - does it need any special test or handling? + # TODO: How does the following code check if FixTk is included? + pyi_builder.test_source(""" + import tkinter + """) + + +def test_pkg_resource_res_string(pyi_builder, monkeypatch): + # Include some data files for testing pkg_resources module. + datas = os.pathsep.join((str(_MODULES_DIR.join('pkg3', 'sample-data.txt')), + 'pkg3')) + pyi_builder.test_script('pkg_resource_res_string.py', + pyi_args=['--add-data', datas]) + + +def test_pkgutil_get_data(pyi_builder, monkeypatch): + # Include some data files for testing pkg_resources module. + datas = os.pathsep.join((str(_MODULES_DIR.join('pkg3', 'sample-data.txt')), + 'pkg3')) + pyi_builder.test_script('pkgutil_get_data.py', + pyi_args=['--add-data', datas]) + + +@xfail( + reason='Our import mechanism returns the wrong loader-class for __main__.' +) +def test_pkgutil_get_data__main__(pyi_builder, monkeypatch): + # Include some data files for testing pkg_resources module. + datas = os.pathsep.join((str(_MODULES_DIR.join('pkg3', 'sample-data.txt')), + 'pkg3')) + pyi_builder.test_script('pkgutil_get_data__main__.py', + pyi_args=['--add-data', datas]) + + +@importorskip('sphinx') +def test_sphinx(tmpdir, pyi_builder, data_dir): + # Note that including the data_dir fixture copies files needed by this test + pyi_builder.test_script('pyi_lib_sphinx.py') + + +@importorskip('pygments') +def test_pygments(pyi_builder): + pyi_builder.test_source( + """ + # This sample code is taken from http://pygments.org/docs/quickstart/. + from pygments import highlight + from pygments.lexers import PythonLexer + from pygments.formatters import HtmlFormatter + + code = 'print "Hello World"' + print(highlight(code, PythonLexer(), HtmlFormatter())) + """) + + +PYQT5_NEED_OPENGL = pytest.mark.skipif(is_module_satisfies('PyQt5 <= 5.10.1'), + reason='PyQt5 v5.10.1 and older does not package ``opengl32sw.dll``, the ' + 'OpenGL software renderer, which this test requires.') + + +# Parametrize test to run the same basic code on both Python Qt libraries. +QtPyLibs = pytest.mark.parametrize('QtPyLib', ['PyQt5', 'PySide2']) + +# OS X bundles, produced by the ``--windowed`` flag, invoke a unique code path +# that sometimes causes failures in Qt applications. +USE_WINDOWED_KWARG = dict(pyi_args=['--windowed']) if is_darwin else {} + + +# Define a function to remove paths with ``path_to_clean`` in them during a +# test so that PyQt5/PySide2 tests pass. Only remove them in Windows, since +# Mac/Linux Qt libraries don't rely on the path to find libraries. +def path_clean(monkeypatch, path_to_clean): + if is_win: + # Eliminate the other library from the path. + path_to_clean = dict(PyQt5='PySide2', PySide2='PyQt5')[path_to_clean] + new_path = os.pathsep.join( + [x for x in os.environ['PATH'].split(os.pathsep) + if path_to_clean not in x] + ) + monkeypatch.setenv('PATH', new_path) + + +@PYQT5_NEED_OPENGL +@importorskip('PyQt5') +def test_PyQt5_uic(tmpdir, pyi_builder, data_dir, monkeypatch): + path_clean(monkeypatch, 'PyQt5') + # Note that including the data_dir fixture copies files needed by this test. + pyi_builder.test_script('pyi_lib_PyQt5-uic.py') + + +# Produce the source code for QWebEngine tests by inserting the path of an HTML +# page to display. +def get_QWebEngine_html(qt_flavor, data_dir): + return """ + from {0}.QtWidgets import QApplication + from {0}.QtWebEngineWidgets import QWebEngineView + from {0}.QtCore import QUrl, QTimer + + app = QApplication([]) + view = QWebEngineView() + view.load(QUrl.fromLocalFile({1})) + view.show() + view.page().loadFinished.connect( + # Display the web page for one second after it loads. + lambda ok: QTimer.singleShot(1000, app.quit)) + app.exec_() + """.format(qt_flavor, + # Use repr to avoid accidental special characters in Windows + # filenames: ``c:\temp`` is ``cemp``! + repr(data_dir.join('test_web_page.html').strpath)) + + +@xfail(is_linux, reason='See issue #4666') +@pytest.mark.skipif(is_win and not is_64bits, reason="Qt 5.11+ for Windows " + "only provides pre-compiled Qt WebEngine binaries for 64-bit processors.") +@pytest.mark.skipif(is_module_satisfies('PyQt5 == 5.11.3') and is_darwin, + reason='This version of the OS X wheel does not include QWebEngine.') +@importorskip('PyQt5') +def test_PyQt5_QWebEngine(pyi_builder, data_dir, monkeypatch): + path_clean(monkeypatch, 'PyQt5') + if is_darwin: + # This tests running the QWebEngine on OS X. To do so, the test must: + # + # 1. Run only a onedir build -- onefile builds don't work. + if pyi_builder._mode != 'onedir': + pytest.skip('The QWebEngine .app bundle ' + 'only supports onedir mode.') + + # 2. Only test the Mac .app bundle, by modifying the executes this + # fixture runs. + _old_find_executables = pyi_builder._find_executables + # Create a replacement method that selects just the .app bundle. + + def _replacement_find_executables(self, name): + path_to_onedir, path_to_app_bundle = _old_find_executables(name) + return [path_to_app_bundle] + # Use this in the fixture. See https://stackoverflow.com/a/28060251 and + # https://docs.python.org/3/howto/descriptor.html. + pyi_builder._find_executables = \ + _replacement_find_executables.__get__(pyi_builder) + + # 3. Run the test with specific command-line arguments. Otherwise, OS X + # builds fail. Also use this for the Linux and Windows builds, since this is + # a common case. + pyi_builder.test_source(get_QWebEngine_html('PyQt5', data_dir), + **USE_WINDOWED_KWARG) + + +@PYQT5_NEED_OPENGL +@QtPyLibs +def test_Qt5_QtQml(pyi_builder, QtPyLib, monkeypatch): + path_clean(monkeypatch, QtPyLib) + pytest.importorskip(QtPyLib) + + pyi_builder.test_source( + """ + import sys + + from {0}.QtGui import QGuiApplication + from {0}.QtQml import QQmlApplicationEngine + from {0}.QtCore import QTimer, QUrl + + # Select a style via the `command line `_, + # since currently PyQt5 doesn't `support https://riverbankcomputing.com/pipermail/pyqt/2018-March/040180.html>`_ + # ``QQuickStyle``. Using this style with the QML below helps to verify + # that all QML files are packaged; see https://github.com/pyinstaller/pyinstaller/issues/3711. + app = QGuiApplication(sys.argv + ['-style', 'imagine']) + engine = QQmlApplicationEngine() + engine.loadData(b''' + import QtQuick 2.11 + import QtQuick.Controls 2.4 + + ApplicationWindow {{ + visible: true + ProgressBar {{value: 0.6}} + }} + ''', QUrl()) + + if not engine.rootObjects(): + sys.exit(-1) + + # Exit Qt when the main loop becomes idle. + QTimer.singleShot(0, app.exit) + + res = app.exec_() + del engine + sys.exit(res) + """.format(QtPyLib), **USE_WINDOWED_KWARG) + + +@pytest.mark.parametrize('QtPyLib', [ + 'PyQt5', + pytest.param( + 'PySide2', + marks=xfail(is_win, reason='PySide2 SSL hook needs updating.') + ) +]) +def test_Qt5_SSL_support(pyi_builder, monkeypatch, QtPyLib): + path_clean(monkeypatch, QtPyLib) + pytest.importorskip(QtPyLib) + + pyi_builder.test_source( + """ + from PyQt5.QtNetwork import QSslSocket + assert QSslSocket.supportsSsl() + """, **USE_WINDOWED_KWARG) + + +# Test that the ``PyQt5.Qt`` module works by importing something from it. +# +# The Qt Bluetooth API (which any import to ``PyQt5.Qt`` implicitly imports) +# isn't compatible with Windows Server 2012 R2, the OS Appveyor runs. +# Specifically, running on Server 2012 causes the test to display an error in +# `a dialog box `_. +# The alternative of using a newer Appveyor OS `fails `_. +# Therefore, skip this test on Appveyor by testing for one of its `environment +# variables `_. +@skipif(os.environ.get('APPVEYOR') == 'True', + reason='The Appveyor OS is incompatible with PyQt.Qt.') +@importorskip('PyQt5') +@pytest.mark.skipif(is_module_satisfies('PyQt5 == 5.11.3') and is_darwin, + reason='This version of the OS X wheel does not include QWebEngine.') +def test_PyQt5_Qt(pyi_builder, monkeypatch): + path_clean(monkeypatch, 'PyQt5') + pyi_builder.test_source('from PyQt5.Qt import QLibraryInfo', + **USE_WINDOWED_KWARG) + + +@QtPyLibs +def test_Qt5_QTranslate(pyi_builder, monkeypatch, QtPyLib): + path_clean(monkeypatch, QtPyLib) + pytest.importorskip(QtPyLib) + pyi_builder.test_source( + """ + from {0}.QtWidgets import QApplication + from {0}.QtCore import ( + QTranslator, + QLocale, + QLibraryInfo, + ) + + # Initialize Qt default translations + app = QApplication([]) + translator = QTranslator() + locale = QLocale('de_DE') + translation_path = QLibraryInfo.location(QLibraryInfo.TranslationsPath) + + print('Qt locale path: %s' % translation_path) + + if translator.load(locale, "qtbase_", directory=translation_path): + print('Qt locale %s loaded.' % locale.name()) + else: + print('Qt locale %s not found!' % locale.name()) + assert False + """.format(QtPyLib)) + + +@importorskip('PySide2') +def test_PySide2_QWebEngine(pyi_builder, data_dir): + if is_darwin: + # QWebEngine on OS X only works with a onedir build -- onefile builds + # don't work. Skip the test execution for onefile builds. + if pyi_builder._mode != 'onedir': + pytest.skip('The QWebEngine .app bundle ' + 'only supports onedir mode.') + + pyi_builder.test_source(get_QWebEngine_html('PySide2', data_dir), + **USE_WINDOWED_KWARG) + + +@importorskip('zope.interface') +def test_zope_interface(pyi_builder): + # Tests that `nspkg.pth`-based namespace package are bundled properly. + # The `nspkg.pth` file is created by setuptools and thus changes + # frequently. If this test fails most propably + # _SETUPTOOLS_NAMESPACEPKG_PTHs in modulegraph needs to be updated. + pyi_builder.test_source( + """ + # Package 'zope' does not contain __init__.py file. + # Just importing 'zope.interface' is sufficient. + import zope.interface + """) + + +@importorskip('idlelib') +def test_idlelib(pyi_builder): + pyi_builder.test_source( + """ + # This file depends on loading some icons, located based on __file__. + try: + import idlelib.TreeWidget + except: + import idlelib.tree + """) + + +@importorskip('keyring') +@skipif(is_linux, reason="SecretStorage backend on linux requires active " + "D-BUS session and initialized keyring, and may " + "need to unlock the keyring via UI prompt.") +def test_keyring(pyi_builder): + pyi_builder.test_source( + """ + import keyring + keyring.get_password("test", "test") + """) + + +@importorskip('numpy') +def test_numpy(pyi_builder): + pyi_builder.test_source( + """ + import numpy + from numpy.core.numeric import dot + print('dot(3, 4):', dot(3, 4)) + """) + + +@importorskip('pytz') +def test_pytz(pyi_builder): + pyi_builder.test_source( + """ + import pytz + pytz.timezone('US/Eastern') + """) + + +@importorskip('requests') +def test_requests(tmpdir, pyi_builder, data_dir, monkeypatch): + # Note that including the data_dir fixture copies files needed by this test. + # Include the data files. + datas = os.pathsep.join((str(data_dir.join('*')), os.curdir)) + pyi_builder.test_script('pyi_lib_requests.py', + pyi_args=['--add-data', datas]) + + +@importorskip('urllib3.packages.six') +def test_urllib3_six(pyi_builder): + # Test for pre-safe-import urllib3.packages.six.moves. + pyi_builder.test_source(""" + import urllib3.connectionpool + import types + assert isinstance(urllib3.connectionpool.queue, types.ModuleType) + """) + + +@importorskip('sqlite3') +def test_sqlite3(pyi_builder): + pyi_builder.test_source( + """ + # PyInstaller did not included module 'sqlite3.dump'. + import sqlite3 + conn = sqlite3.connect(':memory:') + csr = conn.cursor() + csr.execute('CREATE TABLE Example (id)') + for line in conn.iterdump(): + print(line) + """) + + +# Note that @importorskip('scapy') isn't sufficient; this doesn't ask scapy to +# import its backend dependencies (such as pcapy or dnet). scapy.all does import +# the backends, skipping this test if they aren't installed. +@importorskip('scapy.all') +def test_scapy(pyi_builder): + pyi_builder.test_source( + """ + # Test-cases taken from issue #834 + import scapy.all + scapy.all.IP + + from scapy.all import IP + + # Test-case taken from issue #202. + from scapy.all import * + DHCP # scapy.layers.dhcp.DHCP + BOOTP # scapy.layers.dhcp.BOOTP + DNS # scapy.layers.dns.DNS + ICMP # scapy.layers.inet.ICMP + """) + + +@importorskip('scapy.all') +def test_scapy2(pyi_builder): + pyi_builder.test_source( + """ + # Test the hook to scapy.layers.all + from scapy.layers.all import DHCP + """) + + +@importorskip('scapy.all') +def test_scapy3(pyi_builder): + pyi_builder.test_source( + """ + # Test whether + # a) scapy packet layers are not included if neither scapy.all nor + # scapy.layers.all are imported. + # b) packages are included if imported explicitly + + # This test-case assumes, that layer modules are imported only if + NAME = 'hook-scapy.layers.all' + layer_inet = 'scapy.layers.inet' + + def testit(): + try: + __import__(layer_inet) + raise SystemExit('Self-test of hook %s failed: package module found' + % NAME) + except ImportError, e: + if not e.args[0].endswith(' inet'): + raise SystemExit('Self-test of hook %s failed: package module found' + ' and has import errors: %r' % (NAME, e)) + + import scapy + testit() + import scapy.layers + testit() + # Explicitly import a single layer module. Note: This module MUST NOT + # import inet (neither directly nor indirectly), otherwise the test + # above fails. + import scapy.layers.ir + """) + + +@importorskip('sqlalchemy') +def test_sqlalchemy(pyi_builder): + pyi_builder.test_source( + """ + # The hook behaviour is to include with sqlalchemy all installed database + # backends. + import sqlalchemy + # This import was known to fail with sqlalchemy 0.9.1 + import sqlalchemy.ext.declarative + """) + + +@importorskip('twisted') +def test_twisted(pyi_builder): + pyi_builder.test_source( + """ + # Twisted is an event-driven networking engine. + # + # The 'reactor' is object that starts the eventloop. + # There are different types of platform specific reactors. + # Platform specific reactor is wrapped into twisted.internet.reactor module. + from twisted.internet import reactor + # Applications importing module twisted.internet.reactor might fail + # with error like: + # + # AttributeError: 'module' object has no attribute 'listenTCP' + # + # Ensure default reactor was loaded - it has method 'listenTCP' to start server. + if not hasattr(reactor, 'listenTCP'): + raise SystemExit('Twisted reactor not properly initialized.') + """) + + +@importorskip('pyexcelerate') +def test_pyexcelerate(pyi_builder): + pyi_builder.test_source( + """ + # Requires PyExcelerate 0.6.1 or higher + # Tested on Windows 7 x64 SP1 with CPython 2.7.6 + import pyexcelerate + """) + + +@importorskip('usb') +@pytest.mark.skipif(is_linux, reason='libusb_exit segfaults on some linuxes') +def test_usb(pyi_builder): + # See if the usb package is supported on this platform. + try: + import usb + # This will verify that the backend is present; if not, it will + # skip this test. + usb.core.find() + except (ImportError, usb.core.NoBackendError): + pytest.skip('USB backnd not found.') + + pyi_builder.test_source( + """ + import usb.core + # NoBackendError fails the test if no backends are found. + usb.core.find() + """) + + +@importorskip('zeep') +def test_zeep(pyi_builder): + pyi_builder.test_source( + """ + # Test the hook to zeep + from zeep import utils + utils.get_version() + """) + + +@importorskip('PIL') +#@pytest.mark.xfail(reason="Fails with Pillow 3.0.0") +def test_pil_img_conversion(pyi_builder): + datas = os.pathsep.join((str(_DATA_DIR.join('PIL_images')), '.')) + pyi_builder.test_script( + 'pyi_lib_PIL_img_conversion.py', + pyi_args=['--add-data', datas, + # Use console mode or else on Windows the VS() messageboxes + # will stall pytest. + '--console']) + + +@xfail(is_darwin, reason='Issue #1895.') +@importorskip('PIL') +@importorskip('FixTk') +def test_pil_FixTk(pyi_builder): + # hook-PIL is excluding FixTk, but is must still be included + # since it is imported elsewhere. Also see issue #1584. + pyi_builder.test_source(""" + import tkinter + import FixTk, PIL + """) + +@importorskip('PIL.ImageQt') +@importorskip('PyQt5') +def test_pil_PyQt5(pyi_builder): + # hook-PIL is excluding PyQt5, but is must still be included + # since it is imported elsewhere. Also see issue #1584. + pyi_builder.test_source(""" + import PyQt5 + import PIL + import PIL.ImageQt + """) + + +@importorskip('PIL') +def test_pil_plugins(pyi_builder): + pyi_builder.test_source( + """ + # Verify packaging of PIL.Image. Specifically, the hidden import of FixTk + # importing tkinter is causing some problems. + from PIL.Image import frombytes + print(frombytes) + + # PIL import hook should bundle all available PIL plugins. Verify that plugins + # are bundled. + from PIL import Image + Image.init() + MIN_PLUG_COUNT = 7 # Without all plugins the count is usually 6. + plugins = list(Image.SAVE.keys()) + plugins.sort() + if len(plugins) < MIN_PLUG_COUNT: + raise SystemExit('No PIL image plugins were bundled!') + else: + print('PIL supported image formats: %s' % plugins) + """) + + +@importorskip('pandas') +def test_pandas_extension(pyi_builder): + # Tests that the C extension ``pandas._libs.lib`` is properly bundled. Issue #1580. + # See http://pandas.pydata.org/pandas-docs/stable/whatsnew.html#modules-privacy-has-changed. + pyi_builder.test_source( + """ + from pandas._libs.lib import is_float + assert is_float(1) == 0 + """) + + +@importorskip('win32ctypes') +@pytest.mark.skipif(not is_win, + reason='pywin32-ctypes is supported only on Windows') +@pytest.mark.parametrize('submodule', ['win32api', 'win32cred', 'pywintypes']) +def test_pywin32ctypes(pyi_builder, submodule): + pyi_builder.test_source(""" + from win32ctypes.pywin32 import {0} + """.format(submodule)) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_linux_appimage.py b/3rdparty/pyinstaller-4.3/tests/functional/test_linux_appimage.py new file mode 100644 index 0000000000000000000000000000000000000000..8aad7bfa04ef0b5971f0f8af0ecac1f1c08785e9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_linux_appimage.py @@ -0,0 +1,58 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +GNU/Linux-specific test to check the bootloader from the AppImage. +""" + +# Library imports +# --------------- +import os +import pathlib +import stat +import subprocess +import pytest + + +@pytest.mark.linux +@pytest.mark.parametrize('arch', ['x86_64']) +def test_appimage_loading(tmp_path, pyi_builder_spec, arch): + # Skip the test if appimagetool is not found + appimagetool = pathlib.Path.home() / ('appimagetool-%s.AppImage' % arch) + if appimagetool.is_file(): + pytest.skip('%s not found' % appimagetool) + + # Ensure appimagetool is executable + if not os.access(appimagetool, os.X_OK): + st = appimagetool.stat() + appimagetool.chmod(st.st_mode | stat.S_IXUSR) + + app_name = 'apptest' + app_path = os.path.join(tmp_path, '%s-%s.AppImage' % (app_name, arch)) + + # Freeze the app + pyi_builder_spec.test_source('print("OK")', app_name=app_name, + pyi_args=["--onedir"]) + + # Prepare the dist folder for AppImage compliancy + tools_dir = os.path.join(os.path.dirname(__file__), 'data', 'appimage') + script = os.path.join(tools_dir, 'create.sh') + subprocess.check_call(['bash', script, tools_dir, tmp_path, app_name]) + + # Create the AppImage + app_dir = os.path.join(tmp_path, 'dist', 'AppRun') + subprocess.check_call([appimagetool, "--no-appstream", app_dir, + app_path]) + + # Launch the AppImage + st = os.stat(app_path) + os.chmod(app_path, st.st_mode | stat.S_IXUSR) + subprocess.check_call([app_path]) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_multipackage.py b/3rdparty/pyinstaller-4.3/tests/functional/test_multipackage.py new file mode 100644 index 0000000000000000000000000000000000000000..5a545e03f2fd0dadc1363f37c25678ec8fb8046c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_multipackage.py @@ -0,0 +1,25 @@ +import pytest + +from PyInstaller.utils.tests import importorskip + + +@importorskip('psutil') # Used as test for nested extension +@pytest.mark.parametrize( + "spec_file", + ( + "test_multipackage1.spec", + "test_multipackage2.spec", + "test_multipackage3.spec", + "test_multipackage4.spec", + "test_multipackage5.spec", + ), + ids=( + "onefile_depends_on_onefile", + "onedir_depends_on_onefile", + "onefile_depends_on_onedir", + "onedir_depends_on_onedir", + "onedir_and_onefile_depends_on_onedir", + ) +) +def test_spec_with_multipackage(pyi_builder_spec, spec_file): + pyi_builder_spec.test_spec(spec_file) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_multiprocess.py b/3rdparty/pyinstaller-4.3/tests/functional/test_multiprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..47f9f4047c02ffe2d235a790d9468aefd063a8e3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_multiprocess.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- +# ---------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +# ---------------------------------------------------------------------------- + +# Library imports +# --------------- +import os +import sys +import pytest + +# Local imports +# ------------- +from PyInstaller.compat import is_win +from PyInstaller.utils.tests import importorskip, skipif + + +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess(pyi_builder): + pyi_builder.test_script('pyi_multiprocess.py') + + +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess_forking(pyi_builder): + pyi_builder.test_script('pyi_multiprocess_forking.py') + + +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess_pool(pyi_builder): + pyi_builder.test_script('pyi_multiprocess_pool.py') + + +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess_spawn_semaphore(pyi_builder, capfd): + pyi_builder.test_source(""" + import sys + + from multiprocessing import set_start_method, Process, Semaphore + from multiprocessing import freeze_support + from multiprocessing.util import log_to_stderr + + def test(s): + s.acquire() + print('In subprocess') + s.release() + + if __name__ == '__main__': + log_to_stderr() + freeze_support() + set_start_method('spawn') + + print('In main') + sys.stdout.flush() + s = Semaphore() + s.acquire() + proc = Process(target=test, args = [s]) + proc.start() + s.release() + proc.join() + """) + + out, err = capfd.readouterr() + + # Print the captured output and error so that it will show up in the test output. + sys.stderr.write(err) + sys.stdout.write(out) + + expected = ["In main", "In subprocess"] + + assert os.linesep.join(expected) in out + for substring in expected: + assert out.count(substring) == 1 + + +@skipif(is_win, reason='fork is not available on windows') +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess_fork_semaphore(pyi_builder, capfd): + pyi_builder.test_source(""" + import sys + + from multiprocessing import set_start_method, Process, Semaphore + from multiprocessing import freeze_support + from multiprocessing.util import log_to_stderr + + def test(s): + s.acquire() + print('In subprocess') + s.release() + + if __name__ == '__main__': + log_to_stderr() + freeze_support() + set_start_method('fork') + + print('In main') + sys.stdout.flush() + s = Semaphore() + s.acquire() + proc = Process(target=test, args = [s]) + proc.start() + s.release() + proc.join() + """) + + out, err = capfd.readouterr() + + # Print the captured output and error so that it will show up in the test output. + sys.stderr.write(err) + sys.stdout.write(out) + + expected = ["In main", "In subprocess"] + + assert os.linesep.join(expected) in out + for substring in expected: + assert out.count(substring) == 1 + + + + +@skipif(is_win, reason='forkserver is not available on windows') +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess_forkserver_semaphore(pyi_builder, capfd): + pyi_builder.test_source(""" + import sys + + from multiprocessing import set_start_method, Process, Semaphore + from multiprocessing import freeze_support + from multiprocessing.util import log_to_stderr + + def test(s): + s.acquire() + print('In subprocess') + s.release() + + if __name__ == '__main__': + log_to_stderr() + freeze_support() + set_start_method('forkserver') + + print('In main') + sys.stdout.flush() + s = Semaphore() + s.acquire() + proc = Process(target=test, args = [s]) + proc.start() + s.release() + proc.join() + """) + + out, err = capfd.readouterr() + + # Print the captured output and error so that it will show up in the test output. + sys.stderr.write(err) + sys.stdout.write(out) + + expected = ["In main", "In subprocess"] + + assert os.linesep.join(expected) in out + for substring in expected: + assert out.count(substring) == 1 + + +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess_spawn_process(pyi_builder, capfd): + # Test whether this terminates, see issue #4865 + pyi_builder.test_source(""" + import sys, time + import multiprocessing as mp + + def test(): + time.sleep(1) + print('In subprocess') + + print(sys.argv) + mp.freeze_support() + mp.set_start_method('spawn') + + print('In main') + proc = mp.Process(target=test) + proc.start() + proc.join() + """) + + +@importorskip('multiprocessing') +@pytest.mark.timeout(timeout=60) +def test_multiprocess_spawn_pool(pyi_builder, capfd): + # Test whether this terminates, see issue #4865 + pyi_builder.test_source(""" + import sys, time + import multiprocessing as mp + + def test(s): + time.sleep(1) + print(s) + + print(sys.argv,) + mp.freeze_support() + mp.set_start_method('spawn') + + print('In main') + with mp.Pool() as p: + p.map(test, 'in pool') + """) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_path_encodings.py b/3rdparty/pyinstaller-4.3/tests/functional/test_path_encodings.py new file mode 100644 index 0000000000000000000000000000000000000000..0f5d39ea60246928adaf01e35cc494d8bfa4526e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_path_encodings.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import subprocess + +import pytest + + +def test_ascii_path(pyi_builder): + distdir = pyi_builder._distdir + dd_ascii = distdir.encode('ascii', 'replace').decode('ascii') + if distdir != dd_ascii: + pytest.skip(reason="Default build path not ASCII, skipping...") + + pyi_builder.test_script('pyi_path_encoding.py') + + +@pytest.mark.linux +def test_linux_non_unicode_path(pyi_builder, monkeypatch): + # If we set the locale to 'C', mbstowcs should be completely useless. This + # test verifies that _Py_char2wchar will decode the "undecodable" bytes and + # will decode even filenames that weren't encoded with the locale encoding. + distdir = pyi_builder._distdir + unicode_filename = u'ěšÄřžýáíé日本語' + pyi_builder._distdir = os.path.join(distdir, unicode_filename) + os.makedirs(pyi_builder._distdir) + + tmpdir = os.path.join(str(pyi_builder._tmpdir), unicode_filename + "_TMP") + monkeypatch.setenv('LC_ALL', 'C') + monkeypatch.setenv('TMPDIR', tmpdir) + monkeypatch.setenv('TMP', tmpdir) + + pyi_builder.test_script('pyi_path_encoding.py') + + +@pytest.mark.darwin +@pytest.mark.linux +def test_osx_linux_unicode_path(pyi_builder, monkeypatch): + # Mac and Linux should handle 'unicode' type filenames without problem. + distdir = pyi_builder._distdir + unicode_filename = u'ěšÄřžýáíé日本語' + pyi_builder._distdir = os.path.join(distdir, unicode_filename) + os.makedirs(pyi_builder._distdir) + + tmpdir = os.path.join(str(pyi_builder._tmpdir), unicode_filename + "_TMP") + monkeypatch.setenv('TMPDIR', tmpdir) + monkeypatch.setenv('TMP', tmpdir) + + pyi_builder.test_script('pyi_path_encoding.py') + + +@pytest.mark.win32 +def test_win_codepage_path(pyi_builder, monkeypatch): + distdir = pyi_builder._distdir + # Create some bytes and decode with the current codepage to get a filename that + # is guaranteed to encode with the current codepage. + # Assumes a one-byte codepage, i.e. not cp937 (shift-JIS) which is multibyte + cp_filename = bytes(bytearray(range(0x80, 0x86))).decode('mbcs') + + pyi_builder._distdir = os.path.join(distdir, cp_filename) + os.makedirs(pyi_builder._distdir) + + tmpdir = os.path.join(str(pyi_builder._tmpdir), cp_filename + "_TMP") + monkeypatch.setenv('TMPDIR', tmpdir) + monkeypatch.setenv('TMP', tmpdir) + + pyi_builder.test_script('pyi_path_encoding.py') + + +@pytest.mark.win32 +def test_win_codepage_path_disabled_shortfilename(pyi_builder, monkeypatch): + distdir = pyi_builder._distdir + # Create some bytes and decode with the current codepage to get a filename that + # is guaranteed to encode with the current codepage. + # Assumes a one-byte codepage, i.e. not cp937 (shift-JIS) which is multibyte + cp_filename = bytes(bytearray(range(0x80, 0x86))).decode('mbcs') + + distdir = os.path.join(distdir, cp_filename) + os.makedirs(distdir) + + # Try to remove ShortFileName from this folder using `fsutil` + # Requires admin privileges, so `xfail` if we don't have them. + # `8dot3name strip` only affects subfolders, so pass the folder containing + # our codepage filename + fsutil_distdir = pyi_builder._distdir + + if(subprocess.call(['fsutil', '8dot3name', 'strip', fsutil_distdir])): + pytest.xfail("Administrator privileges required to strip ShortFileName.") + + tmpdir = os.path.join(str(pyi_builder._tmpdir), cp_filename + "_TMP") + monkeypatch.setenv('TMPDIR', tmpdir) + monkeypatch.setenv('TMP', tmpdir) + + pyi_builder._distdir = distdir + pyi_builder.test_script('pyi_path_encoding.py') + + +@pytest.mark.win32 +def test_win_non_codepage_path(pyi_builder, monkeypatch): + distdir = pyi_builder._distdir + # Both eastern European and Japanese characters - no codepage should encode this. + non_cp_filename = u'ěšÄřžýáíé日本語' + + # Codepage encoding would replace some of these chars with "???". + + pyi_builder._distdir = os.path.join(distdir, non_cp_filename) + os.makedirs(pyi_builder._distdir) + + tmpdir = os.path.join(str(pyi_builder._tmpdir), non_cp_filename + "_TMP") + + # To test what happens with a non-ANSI tempdir, we will also need to pass + # the TMP environ as wide chars. + monkeypatch.setenv('TMPDIR', tmpdir) + monkeypatch.setenv('TMP', tmpdir) + + pyi_builder.test_script('pyi_path_encoding.py') + + +@pytest.mark.win32 +def test_win_py3_no_shortpathname(pyi_builder): + pyi_builder.test_script('pyi_win_py3_no_shortpathname.py') + + +@pytest.mark.win32 +def test_win_TEMP_has_shortpathname(pyi_builder, monkeypatch, tmp_path): + """Test if script if pass if $TMP holds a short path name""" + tmp = tmp_path / "longlongfilename" / "xxx" + tmp.mkdir(parents=True, exist_ok=True) + import win32api + tmp = win32api.GetShortPathName(str(tmp)) + monkeypatch.setenv("TMP", tmp) + monkeypatch.setenv("TEMP", tmp) + pyi_builder.test_script('pyi_win_py3_no_shortpathname.py') diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_pkg_resources_provider.py b/3rdparty/pyinstaller-4.3/tests/functional/test_pkg_resources_provider.py new file mode 100644 index 0000000000000000000000000000000000000000..43383191267be2a3d9b37b02300733360568aa9d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_pkg_resources_provider.py @@ -0,0 +1,111 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2020, 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) +#----------------------------------------------------------------------------- +# +# These tests run a test script (scripts/pyi_pkg_resources_provider.py) +# in unfrozen and frozen form, in combination with a custom test package +# (modules/pyi_pkg_resources_provider/package) in either source or +# zipped egg form. +# +# Running the unfrozen test script allows us to verify the behavior of +# DefaultProvider and ZipProvider from pkg_resources and thereby also +# validate the test script itself. Running the frozen test validates +# the behavior of the PyiFrozenProvider. +# +# For details on the structure of the test and the contents of the test +# package, see the top comment in the test script itself. + +# Library imports +# --------------- +import os +import shutil + +# Third-party imports +# ------------------- +import pytest + +# Local imports +# ------------- +from PyInstaller.utils.tests import importorskip +from PyInstaller.compat import exec_python, exec_python_rc + +# Directory with testing modules used in some tests. +_MODULES_DIR = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'modules' +) + + +def __exec_python_script(script_filename, pathex): + # Prepare the environment - default to 'os.environ'... + env = os.environ.copy() + # ... and prepend PYTHONPATH with pathex + if 'PYTHONPATH' in env: + pathex = os.pathsep.join([pathex, env['PYTHONPATH']]) + env['PYTHONPATH'] = pathex + # Run the test script + return exec_python_rc(script_filename, env=env) + + +def __get_test_package_path(package_type, tmpdir, monkeypatch): + # Same test package, in two different formats: source package or + # zipped egg (built on-the-fly) + src_path = os.path.join(_MODULES_DIR, + 'pyi_pkg_resources_provider', + 'package') + # Source package + if package_type == 'pkg': + return src_path + # Copy files to a tmpdir for building the egg. + dest_path = tmpdir.join('src') + shutil.copytree(src_path, dest_path.strpath) + monkeypatch.chdir(dest_path) + # Create an egg from the test package. For debug, show the output of + # the egg build. + print(exec_python('setup.py', 'bdist_egg')) + # Obtain the name of the egg, which depends on the Python version. + dist_path = dest_path.join('dist') + files = os.listdir(dist_path.strpath) + assert len(files) == 1 + egg_name = files[0] + assert egg_name.endswith('.egg') + # Return the full path to the egg file + return dist_path.join(egg_name).strpath + + +@importorskip('pkg_resources') +@pytest.mark.parametrize('package_type', ['pkg', 'egg']) +def test_pkg_resources_provider_source(package_type, tmpdir, script_dir, + monkeypatch): + # Run the test script unfrozen - to validate it is working and to + # verify the behavior of pkg_resources.DefaultProvider / ZipProvider. + pathex = __get_test_package_path(package_type, tmpdir, monkeypatch) + test_script = 'pyi_pkg_resources_provider.py' + test_script = os.path.join(str(script_dir), # not is_py36: str() + test_script) + ret = __exec_python_script(test_script, pathex=pathex) + assert ret == 0, "Test script failed!" + + +@importorskip('pkg_resources') +@pytest.mark.parametrize('package_type', ['pkg', 'egg']) +def test_pkg_resources_provider_frozen(pyi_builder, package_type, tmpdir, + script_dir, monkeypatch): + # Run the test script as a frozen program + pathex = __get_test_package_path(package_type, tmpdir, monkeypatch) + test_script = 'pyi_pkg_resources_provider.py' + hooks_dir = os.path.join(_MODULES_DIR, + 'pyi_pkg_resources_provider', + 'hooks') + pyi_builder.test_script(test_script, pyi_args=[ + '--paths', pathex, + '--hidden-import', 'pyi_pkgres_testpkg', + '--additional-hooks-dir', hooks_dir] + ) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_regression.py b/3rdparty/pyinstaller-4.3/tests/functional/test_regression.py new file mode 100644 index 0000000000000000000000000000000000000000..ea1aebfa91a48b2980619d1f8c72594f12b4b5c0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_regression.py @@ -0,0 +1,104 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 pathlib import Path +from importlib.machinery import EXTENSION_SUFFIXES + +from PyInstaller.depend import analysis, bindepend +from PyInstaller.building.build_main import Analysis +from PyInstaller.building.api import PYZ + +# :todo: find a way to get this from `conftest` or such +# Directory with testing modules used in some tests. +_MODULES_DIR = Path(__file__).absolute().parent / "modules" + +def test_issue_2492(monkeypatch, tmpdir): + # Crash if an extension module has an hidden import to ctypes (e.g. added + # by the hook). + + # Need to set up some values + monkeypatch.setattr('PyInstaller.config.CONF', + {'workpath': str(tmpdir), + 'spec': str(tmpdir), + 'warnfile': str(tmpdir.join('warn.txt')), + 'dot-file': str(tmpdir.join('imports.dot')), + 'xref-file': str(tmpdir.join('imports.xref')), + 'hiddenimports': [], + 'specnm': 'issue_2492_script'}) + # Speedup: avoid analyzing base_library.zip + monkeypatch.setattr(analysis, 'PY3_BASE_MODULES', []) + + script = tmpdir.join('script.py') + script.write('import _struct') + # create a hook + tmpdir.join('hook-_struct.py').write('hiddenimports = ["ctypes"]') + a = Analysis([str(script)], hookspath=[str(tmpdir)], + excludes=['encodings', 'pydoc', 'xml', 'distutils']) + + +def test_issue_5131(monkeypatch, tmpdir): + """ + While fixing the endless recursion when the package's __init__ module is + an extension (see + tests/unit/test_modulegraph_more.py::package_init_is_extension_*), another + error occured: PyInstaller.building._utils._load_code() tried to complote + the source code for extension module - triggered by PYZ.assemble(), which + is collecting all source files - caused by this being marked as "PYMODULE" + in the TOC. + """ + + def getImports(*args, **kwargs): + # Our faked binary does not match the expected file-format for all + # platforms, thus the resp. code might crash. Simply ignore this. + try: + return orig_getImports(*args, **kwargs) + except: # noqa + return [] + + monkeypatch.setattr('PyInstaller.config.CONF', + {'workpath': str(tmpdir), + 'spec': str(tmpdir), + 'warnfile': str(tmpdir.join('warn.txt')), + 'dot-file': str(tmpdir.join('imports.dot')), + 'xref-file': str(tmpdir.join('imports.xref')), + 'hiddenimports': [], + 'specnm': 'issue_5131_script'}) + # Speedup: avoid analyzing base_library.zip + monkeypatch.setattr(analysis, 'PY3_BASE_MODULES', []) + + orig_getImports = bindepend.getImports + monkeypatch.setattr(bindepend, "getImports", getImports) + + pkg = (tmpdir / 'mypkg').mkdir() + init = pkg / ('__init__' + EXTENSION_SUFFIXES[0]) + init.write_binary(b'\0\0\0\0\0\0\0\0\0\0\0\0' * 20) + script = tmpdir.join('script.py') + script.write('import mypkg') + a = Analysis([str(script)], + excludes=['encodings', 'pydoc', 'xml', 'distutils']) + PYZ(a.pure, a.zipped_data) + + +def test_issue_4141(pyi_builder): #script_dir, + extra_path = _MODULES_DIR / 'pyi_issue_4141' + pyi_builder.test_script('pyi_issue_4141.py', + app_name="main", run_from_path=True, + pyi_args=['--path', str(extra_path)]) + + +def test_5734(): + """ + In a regression this will raise a: + FileNotFoundError: [Errno 2] No such file or directory: b'liblibc.a' + on some Linux/gcc combinations. + """ + from PyInstaller.depend.utils import _resolveCtypesImports + _resolveCtypesImports(["libc"]) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_runtime.py b/3rdparty/pyinstaller-4.3/tests/functional/test_runtime.py new file mode 100644 index 0000000000000000000000000000000000000000..78b740989cc40c90e405dfc562e09a3caaa188e5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_runtime.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 pytest + + +def test_ctypes_cdll_unknown_dll(pyi_builder, capfd): + with pytest.raises(pytest.fail.Exception, match="Running exe .* failed"): + pyi_builder.test_source(""" + import ctypes + ctypes.cdll.LoadLibrary('non-existing-2017') + """) + out, err = capfd.readouterr() + assert "Failed to load dynlib/dll" in err diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_signals.py b/3rdparty/pyinstaller-4.3/tests/functional/test_signals.py new file mode 100644 index 0000000000000000000000000000000000000000..38ca7a343dddce05ea3da1a2ce3f25777b90180a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_signals.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +# Library imports +# --------------- +import signal + +# Third-party imports +# ------------------- +import pytest + +signals = sorted([ + key for key in dir(signal) + if key.startswith('SIG') and not key.startswith('SIG_') +]) + + +@pytest.mark.darwin +@pytest.mark.linux +@pytest.mark.parametrize('signame', signals) +@pytest.mark.parametrize('ignore', [True, False]) +def test_signal_handled(pyi_builder, signame, ignore): + # xfail tests for signals that the bootloader does NOT forward + if signame in ['SIGKILL', 'SIGSTOP']: + pytest.skip('{} cannot be caught'.format(signame)) + elif signame in ['SIGCHLD', 'SIGCLD']: + pytest.skip( + 'Messing with {} interferes with bootloader'.format(signame) + ) + elif signame == 'SIGTSTP': + pytest.xfail( + '{} is not caught to allow Ctrl-Z'.format(signame) + ) + + verb = 'ignored' if ignore else 'handled' + app_name = 'test_signal_{}_{}'.format(verb, signame) + pyi_args = ['--bootloader-ignore-signals'] if ignore else [] + + pyi_builder.test_source( + """ + import psutil + import signal + import sys + import time + from signal import {signame} + + def eprint(*args): print(*args, file=sys.stderr) + + p = psutil.Process() + eprint('[test_signal_handled_{signame}] process tree:') + while p: + eprint('-', p.name(), '(%s)' % p.pid) + if p == p.parent(): + break + p = p.parent() + + signalled = False + + def handle(signum, *args): + eprint('handled signal', signum) + global signalled + signalled = True + + signal.signal({signame}, handle) + + ignore = {ignore} + + child = psutil.Process() + parent = child.parent() + + if parent.name() == '{app_name}': + # We are the forked child of the bootloader process. + # Signal our parent process to mimic the behavior + # of an external program signalling the process running + # the executable that pyinstaller produced. + target = parent + elif ignore: + # can't pytest.skip() from inside this process + print('Bootloader did not fork; test is invalid') + sys.exit(0) + else: + target = child + + eprint('signalling', target.name(), '(%s)' % target.pid) + target.send_signal({signame}) + + # sleep a bit to avoid exiting before the signal is delivered + time.sleep(1) + + eprint('ignore:', ignore) + eprint('signalled:', signalled) + if ignore and signalled: + raise Exception('signal {signame} not ignored') + elif not ignore and not signalled: + raise Exception('signal handler not called for {signame}') + + msg = 'ignored' if ignore else 'handled' + eprint('bootloader', msg, 'signal successfully.') + """.format(signame=signame, app_name=app_name, ignore=ignore), + app_name=app_name, + runtime=5, + pyi_args=pyi_args) diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_unbuffered_stdio.py b/3rdparty/pyinstaller-4.3/tests/functional/test_unbuffered_stdio.py new file mode 100644 index 0000000000000000000000000000000000000000..b766838a970f9304a4fb8cac3f65f18f34e7efaf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_unbuffered_stdio.py @@ -0,0 +1,92 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2021, 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) +#----------------------------------------------------------------------------- + +""" +Test for unbuffered stdio (stdout/stderr) mode. +""" + +import os +import asyncio + +import pytest + +from PyInstaller.compat import is_py37, is_win + + +@pytest.mark.skipif(os.environ.get('CI', 'false').lower() == 'true', + reason="The test does not support CI (unexplained " + "occasional errors).") +@pytest.mark.parametrize('stream_mode', ['binary', 'text']) +@pytest.mark.parametrize('output_stream', ['stdout', 'stderr']) +def test_unbuffered_stdio(tmp_path, output_stream, stream_mode, + pyi_builder_spec): + # Unbuffered text layer was introduced in Python 3.7 + if stream_mode == 'text' and not is_py37: + pytest.skip("Unbuffered text layer of stdout and stderr streams " + "requires Python 3.7 or later.") + + # Freeze the test program; test_spec() builds the app and runs it, + # so explicitly set the number of stars to 0 for this run. + pyi_builder_spec.test_spec('pyi_unbuffered_output.spec', + app_args=['--num-stars', '0']) + + # Path to the frozen executable + executable = os.path.join(tmp_path, 'dist', + 'pyi_unbuffered_output', + 'pyi_unbuffered_output') + + # Expected number of stars + EXPECTED_STARS = 5 + + # Run the test program via asyncio.SubprocessProtocol and monitor + # the output + class SubprocessDotCounter(asyncio.SubprocessProtocol): + def __init__(self, loop, output='stdout'): + self.count = 0 + self.loop = loop + # Select stdout vs stderr + assert output in {'stdout', 'stderr'} + self.out_fd = 1 if output == 'stdout' else 2 + + def pipe_data_received(self, fd, data): + if fd == self.out_fd: + # Treat any data batch that does not end with the * + # as irregularity + if not data.endswith(b'*'): + return + self.count += data.count(b'*') + + def connection_lost(self, exc): + self.loop.stop() # end loop.run_forever() + + # Create event loop + if is_win: + loop = asyncio.ProactorEventLoop() # for subprocess' pipes on Windows + else: + loop = asyncio.SelectorEventLoop() + asyncio.set_event_loop(loop) + + counter_proto = SubprocessDotCounter(loop, output=output_stream) + + # Run + try: + proc = loop.subprocess_exec(lambda: counter_proto, + executable, + "--num-stars", str(EXPECTED_STARS), + "--output-stream", output_stream, + "--stream-mode", stream_mode) + loop.run_until_complete(proc) + loop.run_forever() + finally: + loop.close() + + # Check the number of received stars + assert counter_proto.count == EXPECTED_STARS diff --git a/3rdparty/pyinstaller-4.3/tests/functional/test_updating_manifest.py b/3rdparty/pyinstaller-4.3/tests/functional/test_updating_manifest.py new file mode 100644 index 0000000000000000000000000000000000000000..3c41d1541772d7f088a0a12c36b952a377208b71 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/functional/test_updating_manifest.py @@ -0,0 +1,62 @@ +# -*- encoding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +# ----------------------------------------------------------------------------- + +__author__ = 'Suzumizaki-Kimitaka(鈴見咲 å›é«˜)' + +import pytest + +test_manifest_which_uses_non_ascii = \ + r''' + + + + + + + + + + + + + + + + + + + ''' + + +@pytest.mark.win32 +def test_reading_manifest(tmpdir): + """Check not using invalid encoding when reading XML manifest files + + Currently, Python 3.6.5, "open" built-in functions uses the + encoding which locale.getpreferredencoding() returns. But generally, + XML manifest files are written with UTF-8 even localized Windows. + We check here not to use local encoding against UTF-8 manifest file. + """ + # This import only works on Windows. Place it here, protected by the + # `@pytest.mark.win32`` decorator. + from PyInstaller.utils.win32 import winmanifest + + # We create the XML file written with UTF-8 as Microsoft tools do. + tmppath = tmpdir.join('manifest.xml') + with tmppath.open('wt', ensure=True, encoding='utf-8') as write_handle: + write_handle.write(test_manifest_which_uses_non_ascii) + # ... and check the following method always uses UTF-8. + # It will read the file, reformat and overwrite it. + winmanifest.create_manifest(str(tmppath), None, None) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/README.txt b/3rdparty/pyinstaller-4.3/tests/old_suite/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..cfcb8b4789c59af3cb50b83cbc677e9b7de175c7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/README.txt @@ -0,0 +1,41 @@ +======================= +Testing PyInstaller +======================= + +The test workflow is not yet documented, but: + + * If those tests will be interactive (user has to click on a button), + then it should go into `./tests/interactive/`, test for hooks (and + suchlike) go into `./tests/libraries/`. If you are working on a + core function, `./tests/basic/` or `./tests/import/` are + appropriate. + + * If you have more test files, create them with file name prefix + ``test_yourtest_``. + + * To run all tests:: + + cd tests + python runtests.py + + * To run a single test:: + + cd tests + python runtests.py libraries/test_yourtest + + * To run all interactive tests:: + + cd tests + python runtests.py -i + + * Test success depends on zero exit status of created binary. + + +For more information please see +https://github.com/pyinstaller/pyinstaller/wiki/How-to-Contribute. + +.. + Local Variables: + mode: rst + ispell-local-dictionary: "american" + End: diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/hooks1/hook-pkg1.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/hooks1/hook-pkg1.py new file mode 100644 index 0000000000000000000000000000000000000000..a85cadec0272d067277536750d3e79652bc34ecf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/hooks1/hook-pkg1.py @@ -0,0 +1,28 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +attrs = [('notamodule','')] + +# Replace package `pkg1` the code and content of `pkg2`, while the +# name `pkg1` is kept. `pkg2` is not contained in the fozen exe. +# See test_pkg_structures.py for more details. + +def hook(mod): + import os + # TODO This does not work with modulegraph yet, submodules are not + # included. + pkg2_path = os.path.normpath(os.path.join(mod.__path__[0], '../pkg2')) + mod.retarget(os.path.join(pkg2_path, '__init__.py')) + mod.__path__ = [pkg2_path, os.path.join(pkg2_path, 'extra')] + mod.node.packagepath = mod.__path__ + mod.del_import('pkg2') + return mod diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg1/__init__.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg1/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..acf6a7b7886da7f5873e68d974f39717da9df87f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg1/__init__.py @@ -0,0 +1,20 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" pkg1 replaces itself with pkg2""" + + +__all__ = ["a", "b"] +import pkg2 +import sys +sys.modules[__name__] = pkg2 +from pkg2 import * diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg1/a.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg1/a.py new file mode 100644 index 0000000000000000000000000000000000000000..af3d0d6cd45f15d865102a437586c51cb89b5619 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg1/a.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" pkg1.a.py is never imported """ + + +print(" %s" % __doc__) +print(" %s %s" % (__name__, __file__)) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/__init__.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3d0c1ed8615205d3bab569aa881c32d52825bbfe --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/__init__.py @@ -0,0 +1,21 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" pkg2 does various namespace tricks, __path__ append """ + +def notamodule(): + return "notamodule from pkg2.__init__" + +import os +__path__.append(os.path.join( + os.path.dirname(__file__), 'extra')) +__all__ = ["a", "b", "notamodule"] diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/a.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/a.py new file mode 100644 index 0000000000000000000000000000000000000000..8c7753daaeea24381d04bee3883c6ede6776513c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/a.py @@ -0,0 +1,18 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" pkg2.a defines overridden and a_func """ + + +def a_func(): + return "a_func from pkg2.a" +print("pkg2.a imported") diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/a/readme.txt b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/a/readme.txt new file mode 100644 index 0000000000000000000000000000000000000000..b833b12cbd285be0bad43738ec928ae95f1c68a9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/a/readme.txt @@ -0,0 +1 @@ +There is no __init__.py in this directory, so pkg2.a refers to ../a.py, not this. diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/extra/b.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/extra/b.py new file mode 100644 index 0000000000000000000000000000000000000000..fc76b32816d5a8ee9ce059eb1ee7e3f25476763f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/pkg2/extra/b.py @@ -0,0 +1,16 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +""" b.py lives in extra, but shows as pkg2.b (and pkg1.b)""" + +def b_func(): + return "b_func from pkg2.b (pkg2/extra/b.py)" diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_onefile_nestedlaunch0.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_onefile_nestedlaunch0.py new file mode 100644 index 0000000000000000000000000000000000000000..854e71bf2689419587442f67388cf4d8711f5189 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_onefile_nestedlaunch0.py @@ -0,0 +1,17 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 cmath + + +if __name__ == '__main__': + print((dir())) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_onefile_nestedlaunch1.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_onefile_nestedlaunch1.py new file mode 100644 index 0000000000000000000000000000000000000000..c717160824e2e2d6304d77b7e24469e6a674caab --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_onefile_nestedlaunch1.py @@ -0,0 +1,28 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys + + +if __name__ == '__main__': + + filename = os.path.join(os.path.dirname(sys.executable), + 'test_onefile_nestedlaunch0.exe') + + try: + import subprocess + except ImportError: + if os.system(filename) != 0: + raise RuntimeError("os.system failed: %s" % filename) + else: + subprocess.check_call([filename]) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures-version.txt b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures-version.txt new file mode 100644 index 0000000000000000000000000000000000000000..2cf969954da6452764205161caf5fccb2620c070 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures-version.txt @@ -0,0 +1,44 @@ +# UTF-8 +# +# For more details about fixed file info 'ffi' see: +# http://msdn.microsoft.com/en-us/library/ms646997.aspx +VSVersionInfo( + ffi=FixedFileInfo( + # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4) + # Set not needed items to zero 0. + filevers=(96, 12, 19, 1), + prodvers=(4, 1, 2, 1), + # Contains a bitmask that specifies the valid bits 'flags' + mask=0x3f, + # Contains a bitmask that specifies the Boolean attributes of the file. + flags=0x0, + # The operating system for which this file was designed. + # 0x4 - NT and there is no need to change it. + OS=0x4, + # The general type of file. + # 0x1 - the file is an application. + fileType=0x1, + # The function of the file. + # 0x0 - the function is not defined for this fileType + subtype=0x0, + # Creation date and time stamp. + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + u'040904b0', + [StringStruct(u'CompanyName', u'Fuddy Duddies, Inc. 8 Flossie Dr. Arlington, VA 00001'), + StringStruct(u'ProductName', u'SILLINESS'), + StringStruct(u'ProductVersion', u'2, 0, 3, 0'), + StringStruct(u'InternalName', u'SILLINESS'), + StringStruct(u'OriginalFilename', u'silliness.exe'), + StringStruct(u'FileVersion', u'96, 12, 19, 1'), + StringStruct(u'FileDescription', u'Silly stuff'), + StringStruct(u'LegalCopyright', u'Copyright 2001 Fuddy Duddies, Inc.'), + StringStruct(u'LegalTrademarks', u'SILLINESS is a registered trademark of Fuddy Duddies, Inc.'),]) + ]), + VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) + ] +) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures.ico b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures.ico new file mode 100644 index 0000000000000000000000000000000000000000..f68cecdaebd9713049bcad4a11212419fe0ce9ac Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures.ico differ diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures.py b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures.py new file mode 100644 index 0000000000000000000000000000000000000000..da58a1b1776a0c810fd9a01f281e686f63b62230 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/basic/test_pkg_structures.py @@ -0,0 +1,44 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# Tests - hooks, strange pkg structures, version, icon. +# +# In this test, the *whole* package `pkg1` is replaced by the code and +# content of `pkg2`, while the name `pkg1` is kept. `pkg2` is not +# contained in the frozen exe. +# +# Additionally, the code of `pkg2` has a module `pkg2.b`, which +# resides in file:`pkg2/extra/.py`. So this test checks also if path +# extension is working for this very special case. +# +# The magic for all of this is done in hooks1/hook-pkg1.py. +# +# The In PyInstaller 2.1 this was done by simply replacing the +# code-object and filename in the hook. +# TODO: In modulegraph this does not yet work. +# + +e1 = 'a_func from pkg2.a' +e2 = 'b_func from pkg2.b (pkg2/extra/b.py)' +e3 = 'notamodule from pkg2.__init__' + + +from pkg1 import * + +t1 = a.a_func() +assert t1 == e1, 'expected %s, got %s' % (e1, t1) + +t2 = b.b_func() +assert t2 == e2, 'expected %s, got %s' % (e2, t2) + +t3 = notamodule() +assert t3 == e3, 'expected %s, got %s' % (e3, t3) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/hello.qml b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/hello.qml new file mode 100644 index 0000000000000000000000000000000000000000..8b69a1d3e93a87098dadc4a5b5936adde5dc4968 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/hello.qml @@ -0,0 +1,17 @@ +import QtQuick 2.0 + +Rectangle { + width: 360 + height: 360 + Text { + anchors.centerIn: parent + text: "Hello World" + } + MouseArea { + anchors.fill: parent + onClicked: { + Qt.quit(); + } + } +} + diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/pyqt5_qml.qrc b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/pyqt5_qml.qrc new file mode 100644 index 0000000000000000000000000000000000000000..724c5be9eb1f02f5297637244dabe8a171212e58 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/pyqt5_qml.qrc @@ -0,0 +1,5 @@ + + + hello.qml + + diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/pyqt5_qml_qrc.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/pyqt5_qml_qrc.py new file mode 100644 index 0000000000000000000000000000000000000000..3b7cad654d1e4d9c72ad53376b93f4d13c2526be --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/pyqt5_qml_qrc.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created: Wed Sep 4 08:34:31 2013 +# by: The Resource Compiler for PyQt (Qt v5.1.1) +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore + +qt_resource_data = b"\ +\x00\x00\x00\xf9\ +\x69\ +\x6d\x70\x6f\x72\x74\x20\x51\x74\x51\x75\x69\x63\x6b\x20\x32\x2e\ +\x30\x0a\x0a\x52\x65\x63\x74\x61\x6e\x67\x6c\x65\x20\x7b\x0a\x20\ +\x20\x20\x20\x77\x69\x64\x74\x68\x3a\x20\x33\x36\x30\x0a\x20\x20\ +\x20\x20\x68\x65\x69\x67\x68\x74\x3a\x20\x33\x36\x30\x0a\x20\x20\ +\x20\x20\x54\x65\x78\x74\x20\x7b\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x20\x61\x6e\x63\x68\x6f\x72\x73\x2e\x63\x65\x6e\x74\x65\x72\x49\ +\x6e\x3a\x20\x70\x61\x72\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\ +\x20\x20\x74\x65\x78\x74\x3a\x20\x22\x48\x65\x6c\x6c\x6f\x20\x57\ +\x6f\x72\x6c\x64\x22\x0a\x20\x20\x20\x20\x7d\x0a\x20\x20\x20\x20\ +\x4d\x6f\x75\x73\x65\x41\x72\x65\x61\x20\x7b\x0a\x20\x20\x20\x20\ +\x20\x20\x20\x20\x61\x6e\x63\x68\x6f\x72\x73\x2e\x66\x69\x6c\x6c\ +\x3a\x20\x70\x61\x72\x65\x6e\x74\x0a\x20\x20\x20\x20\x20\x20\x20\ +\x20\x6f\x6e\x43\x6c\x69\x63\x6b\x65\x64\x3a\x20\x7b\x0a\x20\x20\ +\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x51\x74\x2e\x71\x75\x69\ +\x74\x28\x29\x3b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x7d\x0a\x20\ +\x20\x20\x20\x7d\x0a\x7d\x0a\x0a\ +" + +qt_resource_name = b"\ +\x00\x09\ +\x03\x32\x8d\xbc\ +\x00\x68\ +\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00\x2e\x00\x71\x00\x6d\x00\x6c\ +" + +qt_resource_struct = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +" + +def qInitResources(): + QtCore.qRegisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(0x01, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_argv_emulation.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_argv_emulation.py new file mode 100644 index 0000000000000000000000000000000000000000..355706474331f5a60f12a25a79b7f59576d91be0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_argv_emulation.py @@ -0,0 +1,80 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys +print("Argv from python:", sys.argv) + + +# Testing the argv capturing capability on the Mac is not that easy, but doable. First, build the app bundle +# with PyInstaller, like this: +# +# python $path_to_your_pyinstaller/pyinstaller.py -w -d test_argv_emulation.py +# +# The result should be test_argv_emulation.app. Then, create a file called Info.plist and place the attached +# text into it, and copy it to test_argv_emulation.app/Contents/. Finally, create a file called "a.foo", and drag/drop that +# file onto the test_argv_emulation.app's icon in the Finder. The app will very briefly run, and should print an output to +# stdout, which is viewable in the Mac's Console app. The output should read something like: +# +# Argv from python: ['/the/path/to/the/app','/the/path/to/the/file/a.foo'] +# +# The Mac's Console app is not terminal. Mac's Console app is a log viewer of system's messages. +# This app can be found in your Applications (icon in the taskbar), then utilities, then Console.app. +# http://en.wikipedia.org/wiki/Console_(OS_X) + + +""" + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + test_argv_emulation + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + foo + + CFBundleTypeNameFoo Test Document + CFBundleTypeRole + Viewer + + + CFBundleExecutable + test_argv_emulation + CFBundleIdentifier + org.pythonmac.unspecified.test_argv_emulation + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + test_argv_emulation + CFBundlePackageType + APPL + CFBundleShortVersionString + 0.0.0 + CFBundleSignature + ???? + CFBundleVersion + 0.0.0 + LSHasLocalizedDisplayName + + NSHumanReadableCopyright + Copyright not specified + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + +""" diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_buffering.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_buffering.py new file mode 100644 index 0000000000000000000000000000000000000000..c4c6f0578e77b1e20e479bba33e5e6b7da1aa6b5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_buffering.py @@ -0,0 +1,37 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys + + +print("""test_buffering - unbufferred + type: 123456 + should see: 12345 + type: + if unbuffered should see: 6 + if NOT unbuffered, should see nothing + type: Q to quit + +input:""") +# Ensure the previous message is fully printed to terminal. +sys.stdout.flush() + + +while True: + data = sys.stdin.read(5) + sys.stdout.write(data) + sys.stdout.flush() + if 'Q' in data or 'q' in data: + break + + +print('test_buffering - done') diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_keyring.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_keyring.py new file mode 100644 index 0000000000000000000000000000000000000000..e31fc0d4ce4f0fd631b2a8d0c7157aa5c4e9bd7a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_keyring.py @@ -0,0 +1,22 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 keyring + + +def main(): + keyring.set_password("pyinstaller", "username", "password") + keyring.get_password("pyinstaller", "username") + + +if __name__ == '__main__': + main() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_matplotlib.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_matplotlib.py new file mode 100644 index 0000000000000000000000000000000000000000..ffb5a8d8d941754557d1214fcb8a891f3c333b85 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_matplotlib.py @@ -0,0 +1,34 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 numpy +from matplotlib import mlab +from matplotlib import pyplot + + +def main(): + # Part of the example at + # http://matplotlib.sourceforge.net/plot_directive/mpl_examples/pylab_examples/contour_demo.py + delta = 0.025 + x = numpy.arange(-3.0, 3.0, delta) + y = numpy.arange(-2.0, 2.0, delta) + X, Y = numpy.meshgrid(x, y) + Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) + Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) + Z = 10.0 * (Z2 - Z1) + pyplot.figure() + CS = pyplot.contour(X, Y, Z) + pyplot.show() + + +if __name__ == "__main__": + main() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_onefile_win32_uac_admin.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_onefile_win32_uac_admin.py new file mode 100644 index 0000000000000000000000000000000000000000..60af252b1295728306241e5b082c936d9c5c4674 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_onefile_win32_uac_admin.py @@ -0,0 +1,26 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2014-2021, 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 test tests on Windows the option --uac-admin with onefile mode. + +1) Upon execution the exe should ask for admin privileges. +2) Only admin user has access to path C:\Windows\Temp and this test + should not fail when accessing this path. + +""" + + +# Accessing directory where only admin has access. +import os +admin_dir = os.path.join(os.environ.get('SystemRoot','C:\\windows'), 'temp') +os.listdir(admin_dir) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pygame.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pygame.py new file mode 100644 index 0000000000000000000000000000000000000000..62fca0a5b9cd5722f0a68bfaabc238911de42b49 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pygame.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 is a copy of +# http://jonasbsb.jo.funpic.de/hendrix/pygame-example.py + + +try: + import sys + import random + import math + import os + import pygame + import time + from pygame.locals import * + +except ImportError as err: + raise SystemExit("Error, couldn't load module. %s" % err) + +if not pygame.mixer: print('Warning, sound disabled') + +NUM_SPRITES = 10 + +### Klassendefinitionen + +class Screen: + def __init__(self, resolution=(640, 480), cmdline=""): + self.color = (0,0,0) + self.resolution = resolution + if "--fullscreen" in cmdline: + self.window = \ + pygame.display.set_mode(self.resolution, pygame.FULLSCREEN) + else: + self.window = pygame.display.set_mode(self.resolution) + # pygame.display.set_mode() verändert die Größe des Fensters + # Über das zweite Argument, pygame.FULLSCREEN, kann das Fenster + # in den Vollbildmodus versetzt werden + + pygame.display.set_caption('A Simple Yet Insightful Pygame Example') + # Verändert die Beschriftung des Fensters + + pygame.mouse.set_visible(0) + # Verhindert, dass die Maus gezeigt wird + + self.screen = pygame.display.get_surface() + # Generiert ein Surface des Fensters + # Siehe: Allgemeines über Surfaces + + self.screen.fill(self.color) + # Füllt das Surface self.screen mit der übergebenen Farbe + # Siehe: Allgemeines über Farben + + self.screen_rect = self.screen.get_rect() + # Rectangle des Fensters + # Siehe: Allgemeines über Rectangles + + def size(self): + return self.screen_rect + + def fill(self): + self.screen.fill(self.color) + # Füllt das Surface self.screen mit der übergebenen Farbe + # Siehe: Allgemeines über Farben + + +class Sprite(pygame.sprite.Sprite): + def __init__(self, screen): + pygame.sprite.Sprite.__init__(self) + # Die Klasse Sprite wird von der pygame-Basisklasse + # pygame.sprite.Sprite abgeleitet + + self.screen= screen + + self.width = 10 + self.height = 10 + # Legt die Höhe und Breite der Objekte fest + + self.x = random.randint(0, screen.resolution[0] + self.width) + self.y = random.randint(0, screen.resolution[1] + self.height) + # Generiert zufällig eine x- und eine y-Koordinate als Startpunkt + + self.direction = random.choice((1,-1)) + self.angle = random.choice((0.45, 2.69)) * self.direction + self.speed = random.randint(5,8) + # Wählt zufällig Werte für die Richtung und Geschwindigkeit aus + + self.image = pygame.Surface([self.width, self.height]) + # Generiert ein Surface des Objektes mit der definierten Größe + # Siehe: Allgemeines über Surfaces + + self.rect = self.image.get_rect() + # Siehe: Allgemeines über Rectangles + + self.rect = self.rect.move(self.x,self.y) + # self.rect.move(x-Wert, y-Wert) berechnet einen + # neuen Punkt und ordnet ihn dem Rectangle des + # Objektes zu + # + # Das Koordinatensystem beginnt am oberen, linken Rand + # des Bildschirms mit (0,0) + + self.area = pygame.display.get_surface().get_rect() + # Rectangle des Fensters + # Siehe: Allgemeines über Rectangles + + def position(self): + return self.rect + + def changeColor(self): + newColor = [] + for i in range(3): + newColor.append(random.randint(0,255)) + self.color = newColor + # Generiert einen zufälligen Farbwert + + self.image.fill(self.color) + # Füllt das Surface des Objektes + # Siehe: Allgemeines über Farben + + def update(self): + dx = self.speed*math.cos(self.angle) + dy = self.speed*math.sin(self.angle) + # Mathematische Grundlage der Bewegung + # siehe: http://de.wikipedia.org/wiki/Sinus + + newpos = self.rect.move(dx,dy) + # berechnet eine neue Position + + if not self.area.contains(newpos): + # Kollisionsberechnung + tl = not self.area.collidepoint(newpos.topleft) + tr = not self.area.collidepoint(newpos.topright) + bl = not self.area.collidepoint(newpos.bottomleft) + br = not self.area.collidepoint(newpos.bottomright) + # Kollisionen mit den Eckpunkten des Fensters werden + # berechnet und als boolescher Wert gespeichert + # (0 keine Kollision, 1 Kollision) + + if tr and tl or (br and bl): + # Falls das Objekt mit dem oberen oder unteren + # Bildschirmrand kollidiert, + self.angle = -self.angle + self.changeColor() + # wird der Winkel (und damit die Richtung) umgekehrt + # und die Farbe verändert + + if tl and bl or (tr and br): + # Falls das Objekt mit dem linken oder rechten + # Bildschirmrand kollidiert, + self.angle = math.pi - self.angle + self.changeColor() + # Wird der Winkel (und damit die Richtung) umgekehrt + # und die Farbe verändert + + self.rect = newpos + # Ordnet dem Rectangle des Objekts die neue Position zu + # Die Veränderung der Position wird erst hier gültig! + + +### Funktionsdefinitionen + +def end(): + sys.exit(0) + +def game(events, screen, sprites): + for event in events: + # Wertet die Event-Warteschleife aus + if event.type == QUIT: + # Beendet das Programm, wenn z.B. das Fenster geschlossen wurde + end() + return + elif event.type == KEYDOWN and event.key == K_ESCAPE: + # Beendet das Programm, wenn die Taste Escape gedrückt wurde + end() + return + elif event.type == KEYDOWN and event.key == K_f: + # Schaltet in den Vollbildmodus, wenn die Taste F gedrückt wurde + pygame.display.toggle_fullscreen() + return + + screen.fill() + # Füllt den Bildschirm + + sprites.update() + # Bewegung und Kollisionserkennung der Sprite-Gruppe + # Die update-Funktion der Instanzen wird automatisch + # für alle 123 Rechtecke aufgerufen + + sprites.draw(screen.screen) + # Zeichnet die Sprite-Instanzen auf den Bildschirm + + pygame.display.update() + # Aktualisiert den Bildschirm + + +def main(): + pygame.init() + + pygame.key.set_repeat(1, 1) + # Legt fest, wie oft Tastendrücke automatisch wiederholt werden + # Das erste Argument gibt an ab wann, das zweite in welchen + # Intervallen der Tastendruck wiederholt wird + + clock = pygame.time.Clock() + # Erstellt einen Zeitnehmer + + screen = Screen(cmdline=sys.argv) + # Erstellt eine Instanz der Klasse Screen() + + movingSprites = [] + for i in range(NUM_SPRITES): + movingSprites.append(Sprite(screen)) + # Die for-Schleife erstellt 123 Instanzen der Klasse Sprite + # und fügt sie der Liste movingSprites hinzu + + sprites = pygame.sprite.RenderPlain((movingSprites)) + # Fasst die erstellen Sprite-Instanzen zu einer Gruppe zusammen + # um das Zeichnen der Sprites zu erleichtern + + while True: + clock.tick(30) + # Verhindert, dass das Spiel zu schnell läuft + + game(pygame.event.get(), screen, sprites) + # Ruft die Funktion game auf und übergibt ihr + # die Event-Warteschleife, die Zeichenfläche und die Objekte + end() + +main() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pyqt5.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pyqt5.py new file mode 100644 index 0000000000000000000000000000000000000000..bf2a112f391f68d579d3ce3a5f58d5f94f46079f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pyqt5.py @@ -0,0 +1,37 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys + +from PyQt5 import Qt +from PyQt5 import QtCore +from PyQt5 import QtGui +from PyQt5 import QtWidgets + + +def main(): + app = QtWidgets.QApplication(sys.argv) + read_formats = ', '.join([str(format).lower() \ + for format in QtGui.QImageReader.supportedImageFormats()]) + print(("Qt5 plugin paths: " + str(list(app.libraryPaths())))) + print(("Qt5 image read support: " + read_formats)) + print(('Qt5 Libraries path: ' + \ + str(QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.LibrariesPath)))) + label = QtWidgets.QLabel("Hello World from PyQt5", None) + label.setWindowTitle("Hello World from PyQt5") + label.resize(300, 300) + label.show() + app.exec_() + + +if __name__ == "__main__": + main() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pyqt5_qml.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pyqt5_qml.py new file mode 100644 index 0000000000000000000000000000000000000000..beaa22292dc6acced22742ba8fd0d63988f95239 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_pyqt5_qml.py @@ -0,0 +1,49 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 sys +import os + +from PyQt5 import QtCore +from PyQt5 import QtWidgets +from PyQt5 import QtQuick + +# The hello.qml file is put in a resource so that the packaged app can access +# it. To rebuild it use: +# > pyrcc5 pyqt5_qml.qrc > pyqt5_qml_qrc.py +import pyqt5_qml_qrc + +def main(): + # This is required so that app.quit can be invoked when the quickview + # is closed. If it is not present then the app does not exit. It is + # possibly a bug in PyQt or Qt. + global app + + app = QtWidgets.QApplication(sys.argv) + quickview = QtQuick.QQuickView() + if getattr(sys, 'frozen', None): + basedir = sys._MEIPASS + else: + basedir = os.path.dirname(__file__) + + # The app dir is in the default import path but we can't put the QtQuick + # import lib dirs there because of a name clash (on OSX) with the QtQuick + # dll. + print("Qt5 Qml import paths:", list(quickview.engine().importPathList())) + quickview.setSource(QtCore.QUrl('qrc:/hello.qml')) + quickview.engine().quit.connect(app.quit) + quickview.show() + + app.exec_() + +if __name__ == "__main__": + main() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_tix.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_tix.py new file mode 100644 index 0000000000000000000000000000000000000000..459cf11bcbb55cc9adfed58875999f603fa18ab2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_tix.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 Tix as tix + +root = tix.Tk() +root.title("Test for TiX") + +tix.Label(text="Press to exit").pack() +tix.DirList(root).pack() +tix.Button(root, text="Close", command=root.destroy).pack() +root.bind("", lambda x: root.destroy()) + +tix.mainloop() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_tkinter.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_tkinter.py new file mode 100644 index 0000000000000000000000000000000000000000..0cb97a2a042e73536bb4123f9fe3f19a3151a76d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_tkinter.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# In Python 3 module name is 'tkinter' +try: + from tkinter import * +except ImportError: + from Tkinter import * + + +root = Tk() +root.title("Test for tkinter") +root.bind("", lambda x: root.destroy()) + +Label(text="Press to exit. Some non ascii chars: řĚěíáŘ").pack() +Button(root, text="Close", command=root.destroy).pack() + +root.mainloop() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_wx.py b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_wx.py new file mode 100644 index 0000000000000000000000000000000000000000..20a8ca1a5c5ad8c1f3ec6b758ccc3c739907be96 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/interactive/test_wx.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 wx + + +def main(): + + def onKeyDown(event): + if event.GetKeyCode() == wx.WXK_ESCAPE: + frame.Close() + + app = wx.App(0) + frame = wx.Frame(None, title="Hello World from wxPython") + panel = wx.Panel(frame) + label = wx.StaticText(panel, -1, + u"Press to exit. Some non-ascii chars: řĚěíáŘ") + panel.Bind(wx.EVT_KEY_DOWN, onKeyDown) + panel.SetFocus() + frame.Show() + app.MainLoop() + + +if __name__ == "__main__": + main() diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/libraries/test_gst.py b/3rdparty/pyinstaller-4.3/tests/old_suite/libraries/test_gst.py new file mode 100644 index 0000000000000000000000000000000000000000..3aef6cacf8d3ba03256ed1375807ec8caa010d00 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/libraries/test_gst.py @@ -0,0 +1,31 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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) +#----------------------------------------------------------------------------- + + +# Test for GStreamer Python bindings. + + +# gst module is trying to load some plugins +# and loading plugins should fail when they are not bundled. + + +import sys +import gst + + +reg = gst.registry_get_default() +plug = reg.find_plugin('coreelements') +path = plug.get_filename() +print('coreelements plugin: %s' % path) + +if not path.startswith(sys._MEIPASS): + raise SystemExit('GStreamer coreelements plugin not loaded from ' + 'MEIPASS/gst_plugins, but from %r.' % path) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/libraries/test_wx.py b/3rdparty/pyinstaller-4.3/tests/old_suite/libraries/test_wx.py new file mode 100644 index 0000000000000000000000000000000000000000..c6d08f6b3f177ca449a55efcc79ea2dbe639bd8f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/libraries/test_wx.py @@ -0,0 +1,15 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 wx +app = wx.App(0) +frame = wx.Frame(None, title="Hello World from wxPython", size=(320, 240)) diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/runtests.py b/3rdparty/pyinstaller-4.3/tests/old_suite/runtests.py new file mode 100644 index 0000000000000000000000000000000000000000..a8587ca8d428f53a93115ce76f1d17f4e7f77636 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/runtests.py @@ -0,0 +1,738 @@ +#! /usr/bin/env python +#----------------------------------------------------------------------------- +# Copyright (c) 2013-2021, 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 program will execute any file with name test*.py. If your test +# need an aditional dependency name it test*.py to be ignored +# by this program but be recognizable by any one as a dependency of that +# particular test. + +import glob +import optparse +import os +import re +import shutil +import subprocess +import sys +import unittest + +# ignore some warnings which only confuse when running tests +import warnings + +warnings.filterwarnings('ignore', + "Parent module '.*' not found while handling absolute import") + + +# Expand PYTHONPATH with PyInstaller package to support running without +# installation -- only if not running in a virtualenv. +#if not (hasattr(sys, 'real_prefix') or sys.prefix != sys.base_prefix): +pyi_home = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..') +sys.path.insert(0, pyi_home) + +# Unbuffered sys.stdout, so we can follow stdout continuously when +# running the test-cases, esp. the generated executables. +#sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) + + +import PyInstaller +from PyInstaller import compat, configure +from PyInstaller import __main__ as pyi_main +from PyInstaller.compat import is_win, is_darwin, modname_tkinter, text_read_mode +from PyInstaller.utils import misc +import PyInstaller.utils.hooks as hookutils +from PyInstaller.utils.win32 import winutils +from PyInstaller.utils.cliutils import archive_viewer +import PyInstaller.log as logging + +# HACK +PyInstaller.building.makespec._EXENAME_FORCED_SUFFIX_ = '.exe' + +VERBOSE = False +REPORT = False +PYI_CONFIG = {} +# Directory with this script (runtests.py). +BASEDIR = os.path.dirname(os.path.abspath(__file__)) + + +class SkipChecker(object): + """ + Check conditions if a test case should be skipped. + """ + def __init__(self): + # Required Python or OS version for some tests. + self.MIN_VERSION_OR_OS = { + 'import/test_onefile_pkgutil.get_data__main__': False, + 'interactive/test_onefile_win32_uac_admin': is_win, + 'libraries/test_enchant': is_win, + } + + # Test-cases failing for a known reason and the reason + self.KNOWN_TO_FAIL = { + 'import/test_onefile_pkgutil-get_data__main__': 'Our import mechanism returns the wrong loader-class for __main__.', + 'import/test_nspkg3': 'due to missing support for extendpath in modulegraph', + 'import/test_nspkg3-bbb-zzz': 'due to missing support for extendpath in modulegraph', + 'import/test_nspkg3-empty': 'due to missing support for extendpath in modulegraph', + } + + # The dependencies for Windows and Mac differ from Linux dependencies + # for scapy. The following analysis of scapy's dependencies is based on + # examining scapy v2.3.1, where the version is determined by pip show + # scapy. + # + # In Linux, scapy.arch.linux relies on the command-line utility tcpdump + # and doesn't by default import from scapy.arch.pcapdnet. In contrast, + # scapy.arch.windows sets conf.use_pcap = 1 and conf.use_dnet = 1, then + # imports scapy.arch.pcapdnet, which depends on external packages (pcap + # or pcapy, along with dnet). Since pcap isn't maintained (per + # https://github.com/dugsong/pypcap, the last commit was in 2010), + # choose pcapy (https://pypi.python.org/pypi/pcapy shows recent + # activity). Likewise, scapy.arch.bsd (how scapy classifies Mac, per + # scapy.arch) imports scapy.arch.unix, which sets conf.use_pcap = 1 and + # conf.use_dnet = 1, producing the same dependencies as Windows. + scapy_modules = ['scapy'] + if is_win or is_darwin: + scapy_modules += ['pcapy', 'dnet'] + + # Required Python modules for some tests. + self.MODULES = { + 'basic/test_onefile_nestedlaunch1': ['ctypes'], + 'basic/test_pkg_structures': ['pkg_resources'], + + 'libraries/test_gst': ['gst'], + 'libraries/test_wx': ['wx'], + + 'interactive/test_ipython': ['IPython'], + 'interactive/test_keyring': ['keyring'], + 'interactive/test_matplotlib': ['matplotlib'], + 'interactive/test_pygame': ['pygame'], + 'interactive/test_qt5': ['PyQt5'], + 'interactive/test_tix': ['Tix'], + 'interactive/test_tkinter': [modname_tkinter], + 'interactive/test_wx': ['wx'], + } + + # Other dependencies of some tests. + self.DEPENDENCIES = { + } + + def _check_known_fail(self, test_name): + """ + Return error message (the reason) when a test is known to fail. + Return None otherwise. + """ + return self.KNOWN_TO_FAIL.get(test_name, None) + + def _check_python_and_os(self, test_name): + """ + Return True if test name is not in the list or Python or OS + version is not met. + """ + if (test_name in self.MIN_VERSION_OR_OS and + not self.MIN_VERSION_OR_OS[test_name]): + return False + return True + + def _check_modules(self, test_name): + """ + Return name of missing required module, if any. None means + no module is missing. + """ + if test_name in self.MODULES: + for mod_name in self.MODULES[test_name]: + # STDOUT and STDERR are discarded (devnull) to hide + # import exceptions. + with open(os.devnull) as trash: + retcode = compat.exec_python_rc('-c', "import %s" % mod_name, + stdout=trash, stderr=trash) + if retcode != 0: + return mod_name + return None + + def _check_dependencies(self, test_name): + """ + Return error message when a requirement is not met, None otherwise. + """ + if test_name in self.DEPENDENCIES: + for dep in self.DEPENDENCIES[test_name]: + if dep is not None: + return dep + return None + + def check(self, test_name, run_known_to_fail): + """ + Check test requirements if they are any specified. + + Return tupple (True/False, 'Reason for skipping.'). + True if all requirements are met. Then test case may + be executed. + """ + if not run_known_to_fail: + reason = self._check_known_fail(test_name) + if reason: + return (False, 'Known to fail, reason: ' + reason) + + if not self._check_python_and_os(test_name): + return (False, 'Required another Python version or OS.') + + required_module = self._check_modules(test_name) + if required_module is not None: + return (False, "Module %s is missing." % required_module) + + dependency = self._check_dependencies(test_name) + if dependency is not None: + return (False, dependency) + + return (True, 'Requirements met.') + + +SPEC_FILE = set([ + 'basic/test_pkg_structures', + 'interactive/test_matplotlib', # TODO .spec for this test contain win32 specific manifest code. Do we still need it? + 'interactive/test_onefile_win32_uac_admin', + 'multipackage/test_multipackage1', + 'multipackage/test_multipackage2', + 'multipackage/test_multipackage3', + 'multipackage/test_multipackage4', + 'multipackage/test_multipackage5', +]) + + +class BuildTestRunner(object): + + def __init__(self, test_name, verbose=False, report=False, with_crypto=False): + # Use path separator '/' even on windows for test_name name. + self.test_name = test_name.replace('\\', '/') + self.verbose = verbose + self.test_dir, self.test_file = os.path.split(self.test_name) + runtests_basedir = compat.getenv('PYINSTALLER_RUNTESTS_WORKDIR') + if runtests_basedir: + runtests_basedir = os.path.join(runtests_basedir, self.test_dir) + if not os.path.exists(runtests_basedir): + os.makedirs(runtests_basedir) + else: + runtests_basedir = os.getcwd() + self._specdir = runtests_basedir + self._distdir = os.path.join(runtests_basedir, 'dist') + self._builddir = os.path.join(runtests_basedir, 'build') + # For junit xml report some behavior is changed. + # Especially redirecting sys.stdout. + self.report = report + # Build the test executable with bytecode encryption enabled. + self.with_crypto = with_crypto + + def _msg(self, text): + """ + Important text. Print it to console only in verbose mode. + """ + if self.verbose: + # This allows to redirect stdout to junit xml report. + sys.stdout.write('\n' + 10 * '#' + ' ' + text + ' ' + 10 * '#' + '\n\n') + sys.stdout.flush() + + def _plain_msg(self, text, newline=True): + """ + Print text to console only in verbose mode. + """ + if self.verbose: + if newline: + sys.stdout.write(text + '\n') + else: + sys.stdout.write(text) + sys.stdout.flush() + + def _find_exepath(self, test): + """ + Search for all executables generated by the testcase. + + If the test-case is called e.g. 'test_multipackage1', this is + searching for each of 'test_multipackage1.exe' and + 'multipackage1_?.exe' in both one-file- and one-dir-mode. + """ + assert test.startswith('test_') + name = test[5:] + '_?' + parent_dir = self._distdir + patterns = [ + # one-file deploy pattern + os.path.join(parent_dir, test+'.exe'), + # one-dir deploy pattern + os.path.join(parent_dir, test, test+'.exe'), + # search for e.g. `multipackage2_B`, too: + os.path.join(parent_dir, name+'.exe'), + os.path.join(parent_dir, name, name+'.exe'), + ] + for pattern in patterns: + for prog in glob.glob(pattern): + if os.path.isfile(prog): + yield prog + + def _run_created_exe(self, prog): + """ + Run executable created by PyInstaller. + """ + # Run the test in a clean environment to make sure they're + # really self-contained + path = compat.getenv('PATH') + compat.unsetenv('PATH') + # For Windows we need to keep minimal PATH for sucessful running of some tests. + if is_win: + # Minimum Windows PATH is in most cases: C:\Windows\system32;C:\Windows + compat.setenv('PATH', os.pathsep.join(winutils.get_system_path())) + + self._plain_msg("RUNNING: " + prog) + old_wd = os.getcwd() + os.chdir(os.path.dirname(prog)) + # Run executable. + prog = os.path.join(os.curdir, os.path.basename(prog)) + proc = subprocess.Popen([prog], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # Prints stdout of subprocess continuously. + self._msg('STDOUT %s' % self.test_name) + while proc.poll() is None: + # We need to read a line, not single bytes. Otherwise decoding + # would ail. + line = proc.stdout.readline() + self._plain_msg(line.decode('utf-8'), newline=False) + # Print any stdout that wasn't read before the process terminated. + # See the conversation in https://github.com/pyinstaller/pyinstaller/pull/1092 + # for examples of why this is necessary. + self._plain_msg(proc.stdout.read().decode('utf-8'), newline=False) + # Print possible stderr at the end. + stderr = proc.stderr.read().decode('utf-8') + self._msg('STDERR %s' % self.test_name) + self._plain_msg(stderr) + compat.setenv("PATH", path) + # Restore current working directory + os.chdir(old_wd) + return proc.returncode, stderr + + + def test_exists(self): + """ + Return True if test file exists. + """ + return os.path.exists(os.path.join(BASEDIR, self.test_name + '.py')) + + def test_building(self): + """ + Run building of test script. + + Return True if build succeded False otherwise. + """ + OPTS = ['--debug', '--noupx', + '--specpath', self._specdir, + '--distpath', self._distdir, + '--workpath', self._builddir] + + if self.verbose: + OPTS.extend(['--debug', '--log-level=INFO']) + else: + OPTS.append('--log-level=ERROR') + + # Build executable in onefile mode. + if self.test_file.startswith('test_onefile'): + OPTS.append('--onefile') + else: + OPTS.append('--onedir') + + if self.with_crypto or '_crypto' in self.test_file: + print('NOTE: Bytecode encryption is enabled for this test.', end="") + OPTS.append('--key=test_key') + + self._msg("BUILDING TEST " + self.test_name) + + # Use pyinstaller.py for building test_name. + testfile_spec = self.test_file + '.spec' + if not os.path.exists(self.test_file + '.spec'): + # .spec file does not exist and it has to be generated + # for main script. + testfile_spec = self.test_file + '.py' + + #pyinst_script = os.path.join(HOMEPATH, 'pyinstaller.py') + + # TODO Fix redirecting stdout/stderr + # In report mode is stdout and sys.stderr redirected. + #if self.report: + ## Write output from subprocess to stdout/err. + #retcode, out, err = compat.exec_python_all(pyinst_script, + #testfile_spec, *OPTS) + #sys.stdout.write(out) + #sys.stdout.write(err) + #else: + #retcode = compat.exec_python_rc(pyinst_script, + #testfile_spec, *OPTS) + # abspath is required due to makespec.make_path_spec_relative() + testfile_spec = os.path.abspath(testfile_spec) + pyi_args = [testfile_spec] + OPTS + # TODO fix return code in running PyInstaller programatically + pyi_main.run(pyi_args, PYI_CONFIG) + retcode = 0 + + return retcode == 0 + + def test_exe(self): + """ + Test running of all created executables. + + multipackage-tests generate more than one exe-file and all of + them have to be run. + """ + self._msg('EXECUTING TEST ' + self.test_name) + found = False + retcode = 0 + stderr = '' + for exe in self._find_exepath(self.test_file): + found = True + rc, err = self._run_created_exe(exe) + retcode = retcode or rc + if rc != 0: + stderr = '\n'.join((stderr, '--- %s ---' % exe, err)) + if not found: + self._plain_msg('ERROR: no file generated by PyInstaller found!') + return 1, list(self._find_exepath(self.test_file)) + return retcode, stderr.strip() + + + def test_logs(self): + """ + Compare log files (now used only by multipackage test_name). + + Return True if .toc files match or when .toc patters + are not defined. + """ + logsfn = glob.glob(self.test_file + '.toc') + # Other main scripts do not start with 'test_'. + assert self.test_file.startswith('test_') + logsfn += glob.glob(self.test_file[5:] + '_?.toc') + # generate a mapping basename -> pathname + progs = dict((os.path.splitext(os.path.basename(nm))[0], nm) + for nm in self._find_exepath(self.test_file)) + for logfn in logsfn: + self._msg("EXECUTING MATCHING " + logfn) + tmpname = os.path.splitext(logfn)[0] + prog = progs.get(tmpname) + if not prog: + return False, 'Executable for %s missing' % logfn + fname_list = archive_viewer.get_archive_content(prog) + fname_list = [fn for fn in fname_list] + with open(logfn, text_read_mode) as fp: + pattern_list = eval(fp.read()) + # Alphabetical order of patterns. + pattern_list.sort() + missing = [] + for pattern in pattern_list: + for fname in fname_list: + if re.match(pattern, fname): + self._plain_msg('MATCH: %s --> %s' % (pattern, fname)) + break + else: + # no matching entry found + missing.append(pattern) + self._plain_msg('MISSING: %s' % pattern) + + # Not all modules matched. + # Stop comparing other .toc files and fail the test. + if missing: + msg = '\n'.join('Missing %s in %s' % (m, prog) + for m in missing) + return False, msg + + return True, '' + + +class GenericTestCase(unittest.TestCase): + def __init__(self, func_name, test_dir=None, with_crypto=False, + run_known_fails=False): + """ + func_name Name of test function to create. + """ + if test_dir is not None: + self.test_dir = test_dir + self.test_name = self.test_dir + '/' + func_name + self.run_known_fails = run_known_fails + + # Create new test fuction. This has to be done before super(). + setattr(self, func_name, self._generic_test_function) + super(GenericTestCase, self).__init__(func_name) + + # For tests current working directory has to be changed temporaly. + self.curr_workdir = os.getcwd() + + # Whether to enable bytecode encryption for test executable + self.with_crypto = with_crypto + + def setUp(self): + testdir = os.path.dirname(self.test_name) + os.chdir(os.path.join(BASEDIR, testdir)) # go to testdir + # Clean variables that could be set by PyInstaller import hooks. + # We need to clean it because some tests might fails. + # Like 'wx_pubsub' tests'. + hookutils.hook_variables = {} + + def tearDown(self): + os.chdir(self.curr_workdir) # go back from testdir + + def _generic_test_function(self): + # Skip test case if test requirement are not met. + s = SkipChecker() + req_met, msg = s.check(self.test_name, self.run_known_fails) + if not req_met: + raise unittest.SkipTest(msg) + # Create a build and test it. + b = BuildTestRunner(self.test_name, verbose=VERBOSE, report=REPORT, with_crypto=self.with_crypto) + self.assertTrue(b.test_exists(), + msg='Test %s not found.' % self.test_name) + self.assertTrue(b.test_building(), + msg='Build of %s failed.' % self.test_name) + + okay, msg = b.test_logs() + if not okay: + self.fail('Matching .toc of %s failed.\n\n%s' % + (self.test_name, msg)) + + retcode, stderr = b.test_exe() + if retcode != 0: + self.fail('Running exe of %s failed with return-code %s.\n\n%s' % + (self.test_name, retcode, stderr)) + + +class BasicTestCase(GenericTestCase): + test_dir = 'basic' + + +class CryptoTestCase(GenericTestCase): + test_dir = 'crypto' + + def __init__(self, func_name, with_crypto=False, run_known_fails=False): + # Crypto tests MUST NOT run 'with' crypto enabled. + super(CryptoTestCase, self).__init__(func_name, with_crypto=False, + run_known_fails=run_known_fails) + + +class ImportTestCase(GenericTestCase): + test_dir = 'import' + + +class LibrariesTestCase(GenericTestCase): + test_dir = 'libraries' + + +class MultipackageTestCase(GenericTestCase): + test_dir = 'multipackage' + + +class InteractiveTestCase(GenericTestCase): + """ + Interactive tests require user interaction mostly GUI. + + Interactive tests have to be run directly by user. + They can't be run by any continuous integration system. + """ + test_dir = 'interactive' + + +class TestCaseGenerator(object): + """ + Generate test cases. + """ + def _detect_tests(self, directory): + files = glob.glob(os.path.join(directory, 'test_*.py')) + # Test name is a file name without extension. + tests = [os.path.splitext(os.path.basename(x))[0] for x in files] + tests.sort() + return tests + + def create_suite(self, test_types, with_crypto=False, + run_known_fails=False): + """ + Create test suite and add test cases to it. + + test_types Test classes to create test cases from. + + Return test suite with tests. + """ + suite = unittest.TestSuite() + + for _type in test_types: + tests = self._detect_tests(_type.test_dir) + # Create test cases for a specific type. + for test_name in tests: + suite.addTest(_type(test_name, with_crypto=with_crypto, + run_known_fails=run_known_fails)) + + return suite + + +def clean(): + """ + Remove temporary files created while running tests. + """ + # Files/globs to clean up. + patterns = """python_exe.build + logdict*.log + disttest* + buildtest* + warn*.txt + *.py[co] + */*.py[co] + */*/*.py[co] + build/ + dist/ + */*.dll + */*.lib + */*.obj + */*.exp + */*.so + */*.dylib + """.split() + + # By some directories we do not need to clean files. + # E.g. for unit tests. + IGNORE_DIRS = set([ + 'eggs4testing', + 'unit', + ]) + + # Remove temporary files in all subdirectories. + for directory in os.listdir(BASEDIR): + if not os.path.isdir(directory): + continue + if directory in IGNORE_DIRS: + continue + for pattern in patterns: + file_list = glob.glob(os.path.join(directory, pattern)) + for pth in file_list: + try: + if os.path.isdir(pth): + shutil.rmtree(pth) + else: + os.remove(pth) + except OSError as e: + print(e) + # Delete *.spec files for tests without spec file. + for pth in glob.glob(os.path.join(directory, '*.spec')): + test_name = directory + '/' + os.path.splitext(os.path.basename(pth))[0] + if not test_name in SPEC_FILE: + if os.path.exists(pth): + os.remove(pth) + + +def run_tests(test_suite): + """ + Run test suite. + """ + return unittest.TextTestRunner(verbosity=2).run(test_suite) + + +def main(): + try: + parser = optparse.OptionParser(usage='%prog [options] [TEST-NAME ...]', + epilog='TEST-NAME can be the name of the .py-file, ' + 'the .spec-file or only the basename.') + except TypeError: + parser = optparse.OptionParser(usage='%prog [options] [TEST-NAME ...]') + + parser.add_option('-a', '--all-with-crypto', action='store_true', + help='Run the whole test suite with bytecode encryption enabled.') + parser.add_option('-c', '--clean', action='store_true', + help='Clean up generated files') + parser.add_option('-i', '--interactive-tests', action='store_true', + help='Run interactive tests (default: run normal tests)') + parser.add_option('-v', '--verbose', + action='store_true', + default=False, + help='Verbose mode (default: %default)') + parser.add_option('--known-fails', action='store_true', + dest='run_known_fails', + help='Run tests known to fail, too.') + + opts, args = parser.parse_args() + + # Do only cleanup. + if opts.clean: + clean() + raise SystemExit() # Exit code is 0 in this case. + + # Run only specified tests. + if args: + if opts.interactive_tests: + parser.error('Must not specify -i/--interactive-tests when passing test names.') + suite = unittest.TestSuite() + for arg in args: + test_list = glob.glob(arg) + if not test_list: + test_list = [arg] + else: + test_list = [x for x in test_list + if os.path.splitext(x)[1] in (".py", ".spec")] + # Sort tests aplhabetically. For example test + # basic/test_nested_launch1 depends on the executable from + # basic/test_nested_launch0, which it runs. + test_list.sort() + for t in test_list: + test_dir = os.path.dirname(t) + test_script = os.path.basename(os.path.splitext(t)[0]) + suite.addTest(GenericTestCase(test_script, test_dir=test_dir, + run_known_fails=opts.run_known_fails)) + print('Running test: ', (test_dir + '/' + test_script)) + + # Run all tests or all interactive tests. + else: + if opts.interactive_tests: + print('Running interactive tests...') + test_classes = [InteractiveTestCase] + elif opts.all_with_crypto: + print('Running normal tests with bytecode encryption...') + # Make sure to exclude CryptoTestCase here since we are building + # everything else with crypto enabled. + test_classes = [BasicTestCase, ImportTestCase, + LibrariesTestCase, MultipackageTestCase] + else: + print('Running normal tests (-i for interactive tests)...') + test_classes = [BasicTestCase, CryptoTestCase, ImportTestCase, + LibrariesTestCase, MultipackageTestCase] + + # Create test suite. + generator = TestCaseGenerator() + suite = generator.create_suite(test_classes, opts.all_with_crypto, + opts.run_known_fails) + + # Set global options + global VERBOSE, REPORT, PYI_CONFIG + VERBOSE = opts.verbose + # The function called by configure.get_config uses logging. So, we need to + # set a logging level now, in addition to passing it as a "log-level=XXXX" + # option in test_building (see the OPTS dict), in order for the logging + # level to be consistent. Otherwise, the default logging level of INFO will + # produce messages, then be overriden by runtest's default (when the -v / + # --verbose option isn't specificed on runtest's command line) of ERROR + # (again, see the OPTS dict in test_building). + if VERBOSE: + # logging's default of INFO is fine. + pass + else: + # Set the logging level to ERROR. + logger = logging.getLogger('PyInstaller') + logger.setLevel(logging.ERROR) + PYI_CONFIG = configure.get_config(upx_dir=None) # Run configure phase only once. + + + # Run created test suite. + clean() + + result = run_tests(suite) + + sys.exit(int(bool(result.failures or result.errors))) + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print('... aborted by user-request ...') diff --git a/3rdparty/pyinstaller-4.3/tests/old_suite/test-requirements.txt b/3rdparty/pyinstaller-4.3/tests/old_suite/test-requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..a25022dada81b2af4c0b5c4acb03d864906585b4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/old_suite/test-requirements.txt @@ -0,0 +1,47 @@ +# pure python modules +setuptools +docutils +jinja2 +sphinx +pyusb +pytz +pyenchant +IPython + +# uses ctypes, but requires speech lib to be installed +pyttsx + +# extension modules which do not require external headers +simplejson +SQLAlchemy +multiprocessing + +# Extension modules which require additional external headers. +# Most probably you want to install them using your operating systems +# package-management. +twisted +PIL +MySQL-python +psycopg2 +# gst +matplotlib +numpy +scipy +pycrypto +pygame +#pyodbc +wxPython # was installed for windows only + +### Others from old setupenv_unix.py and setupenv_windows.py +keyring +markdown +pycparser +pyexcelerate +pylint +# pyodbc # was installed for unix only +qt4reactor +requests +scapy +# pywin32 -- windows only + +six # was installed for unix only diff --git a/3rdparty/pyinstaller-4.3/tests/requirements-developer.txt b/3rdparty/pyinstaller-4.3/tests/requirements-developer.txt new file mode 100644 index 0000000000000000000000000000000000000000..5f82f23b2cd7744e741798ac047bca3ffc8f5b10 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/requirements-developer.txt @@ -0,0 +1,37 @@ +# This is the pip requirements file for developers and release managers. This +# will install development tools and release helpers as well as the +# requirements for running the PyInstaller base test-suite. +# +# Example (assuming current dir is PyInstaller's top-level source dir):: +# +# python -m pip install -r tests/requirements-developer.txt +# python -m pip install -r tests/requirements-libraries.txt # extensive + +# include PyInstaller's requirements and for base testing +-r ../requirements.txt +-r requirements-tools.txt + +### Helpers for development + +ipython # Better interactive Python shell. +pyreadline ; sys_platform == 'win32' # Colors in IPython, Windows-only package. +pycmd # Contains 'py.cleanup' that removes all .pyc files and similar. + +### Helpers for releases + +wheel>0.24.0 # For creating .whl packages +twine # For secure upload of tar.gz to PYPI. + +towncrier>=19.2.0 # For creating the change-log file. + +zest.releaser>=6.18 # Makes releasing easier +# Addons for zest.releaser: +check-manifest # Checks MANIFEST.in +pyroma # Checks if package follows best practices of Python packaging. +chardet # character encoding detector. +readme-renderer # Check PyPI description is valid reStructuredText +zestreleaser.towncrier # integrate towncrier into zest.releaser + +sphinx +sphinx_rtd_theme +sphinx-autodoc-typehints diff --git a/3rdparty/pyinstaller-4.3/tests/requirements-libraries.txt b/3rdparty/pyinstaller-4.3/tests/requirements-libraries.txt new file mode 100644 index 0000000000000000000000000000000000000000..e3244d5f3013f66082b9d4089b3d1e0f6400410a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/requirements-libraries.txt @@ -0,0 +1,72 @@ +# This is the pip requirements file for extensive +# PyInstaller testing. +# +# Example (assuming current dir is PyInstaller's top-level source dir):: +# +# python -m pip install -r tests/requirements-libraries.txt # extensive + +# include requirements for base testing +-r requirements-tools.txt + + +# Needs work +# ---------- +# These packages, if updated, produce test failures. Work needs to be done on +# these hooks. Any requirement in this list should be followed by the +# `# pyup: ignore `_ comment. +# +# - v. 2.2 and above fails. +Django==2.1.8 # pyup: ignore +# - v 21.1.0 fails; earlier versions not tested. +keyring==19.2.0 # pyup: ignore + + +# Working +# ------- +# These packages work with no (known) issues. +babel==2.9.0 +future==0.18.2 +gevent==21.1.2 +pygments==2.8.1 +pyside2==5.15.1 +pyqt5==5.15.1 +pyqtwebengine==5.15.1 +python-dateutil==2.8.1 +pytz==2021.1 +requests==2.25.1 +# simplejson is used for text_c_extension +simplejson==3.17.2 +sphinx==2.4.4 # pyup: ignore +# Required for test_namespace_package +sqlalchemy==1.4.7 +zope.interface==5.3.0 +Pillow==8.2.0 + + +# Python versions not supported / supported for older package versions +# ------------------------------------------------------- + +# iPython 7.17 dropped support for python 3.6 +# https://ipython.readthedocs.io/en/stable/whatsnew/version7.html#ipython-7-17 +ipython==7.22.0; python_version > '3.6' +ipython==7.16.1; python_version <= '3.6' # pyup: ignore + +# pandas also dropped support +pandas==1.2.3; python_version > '3.6' +pandas==1.1.5; python_version <= '3.6' # pyup: ignore + +# so did numpy +numpy==1.20.2; python_version > '3.6' +numpy==1.19.4; python_version <= '3.6' # pyup: ignore + +# scipy too +scipy==1.6.2; python_version > '3.6' +scipy==1.5.4; python_version <= '3.6' # pyup: ignore + +# and matplotlib +matplotlib==3.4.1; python_version > '3.6' +matplotlib==3.3.4; python_version <= '3.6' # pyup: ignore + +# Install PyInstaller Hook Sample, to ensure that tests declared in +# entry-points are discovered. +https://github.com/pyinstaller/hooksample/archive/v4.0rc1.zip # pyup: ignore \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/requirements-tools.txt b/3rdparty/pyinstaller-4.3/tests/requirements-tools.txt new file mode 100644 index 0000000000000000000000000000000000000000..4ed96f3ce138c1739cdc3015b5c926673f83ca21 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/requirements-tools.txt @@ -0,0 +1,43 @@ +# This is the pip requirements file for running the +# PyInstaller test-suite. +# +# For extensive testing you will also need to install what is defined in +# requirements-libraries.txt. +# +# Example (assuming current dir is PyInstaller's top-level source dir):: +# +# python -m pip install -r tests/requirements-tools.txt +# python -m pip install -r tests/requirements-libraries.txt # extensive + +# include PyInstaller's requirements +-r ../requirements.txt + +# Work-around for a bug in execnet 1.4.1 +execnet >= 1.5.0 + +# Testing framework. +pytest >= 2.7.3 + +# Plugin allowing running tests in parallel. +pytest-xdist + +# Plugin to abort hanging tests. +pytest-timeout +# allows specifying order without duplicates +pytest-drop-dup-tests + +# Better subprocess alternative with implemented timeout. +psutil + +# Check new flake8 violations on pull requests +flake8 + +pywin32; sys_platform == 'win32' + +lxml + +# crypto support (`--key` option) +tinyaes ~= 1.0 + +# Ability to retry a failed test +flaky diff --git a/3rdparty/pyinstaller-4.3/tests/speed/speed_pefile.py b/3rdparty/pyinstaller-4.3/tests/speed/speed_pefile.py new file mode 100644 index 0000000000000000000000000000000000000000..d150487cba129cdaecf4d23ccd83d2dc598f3946 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/speed/speed_pefile.py @@ -0,0 +1,61 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2015-2021, 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) +#----------------------------------------------------------------------------- +""" + speed_pefile +""" +import os +import shutil +from PyInstaller import log +from PyInstaller.building.build_main import Analysis +from PyInstaller.config import CONF + +import time + +from os.path import join +from tempfile import mkdtemp +logger = log.getLogger(__name__) + + +def speed_pefile(): + log.logging.basicConfig(level=log.DEBUG) + + tempdir = mkdtemp("speed_pefile") + workdir = join(tempdir, "build") + distdir = join(tempdir, "dist") + script = join(tempdir, 'speed_pefile_script.py') + warnfile = join(workdir, 'warn.txt') + os.makedirs(workdir) + os.makedirs(distdir) + + with open(script, 'w') as f: + f.write(''' +from PySide2 import QtCore +from PySide2 import QtGui +''') + + CONF['workpath'] = workdir + CONF['distpath'] = distdir + CONF['warnfile'] = warnfile + CONF['hiddenimports'] = [] + CONF['spec'] = join(tempdir, 'speed_pefile_script.spec') + + CONF['specpath'] = tempdir + CONF['specnm'] = 'speed_pefile_script' + + start = time.time() + a = Analysis([script]) + duration = time.time() - start + + logger.warn("Analysis duration: %s", duration) + shutil.rmtree(tempdir, ignore_errors=True) + +if __name__ == '__main__': + speed_pefile() \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/dynamiclib.dll b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/dynamiclib.dll new file mode 100644 index 0000000000000000000000000000000000000000..8fa2136f688510d6f4367b1c79a06907d4ca8885 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/dynamiclib.dll @@ -0,0 +1,2 @@ +# + diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/dynamiclib.dylib b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/dynamiclib.dylib new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/dynamiclib.dylib @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/init__.py b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/nine.dat b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/nine.dat new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/nine.dat @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/data/eleven.dat b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/data/eleven.dat new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/data/eleven.dat @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/one.py b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/one.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/one.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/sub_pkg/init__.py b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/sub_pkg/init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/sub_pkg/init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/sub_pkg/three.py b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/sub_pkg/three.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/sub_pkg/three.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/ten.dat b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/ten.dat new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/py_files_not_in_package/ten.dat @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/pyextension.pyd b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/pyextension.pyd new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/pyextension.pyd @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/pyextension.so b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/pyextension.so new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/pyextension.so @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/init__.py b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/thirteen.txt b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/thirteen.txt new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/thirteen.txt @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/twelve.py b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/twelve.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/subpkg/twelve.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/two.py b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/two.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/Tree_files/two.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/conftest.py b/3rdparty/pyinstaller-4.3/tests/unit/conftest.py new file mode 100644 index 0000000000000000000000000000000000000000..ed90c786f281c6c16e3e8a15ee186f843aad6b69 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/conftest.py @@ -0,0 +1,22 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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.path +import sys + + +# Expand sys.path with PyInstaller source. +_ROOT_DIR = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..')) +sys.path.append(_ROOT_DIR) + +# Bring all fixtures into this file. +from PyInstaller.utils.conftest import * # noqa diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/MANIFEST.in b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..65d0dc7aef96cab22b9449bc5af4f34e2c1c0ef0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/__init__.py @@ -0,0 +1,5 @@ +import os + + +def get_hook_dirs(): + return [os.path.abspath(os.path.dirname(__file__))] diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/hook-pyi_example_package.py b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/hook-pyi_example_package.py new file mode 100644 index 0000000000000000000000000000000000000000..1376475cb270e7c07239cd2847004c0fc296ee7b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/hook-pyi_example_package.py @@ -0,0 +1 @@ +raise RuntimeError('I shouldn\'t be running') diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/rthooks.dat b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/rthooks.dat new file mode 100644 index 0000000000000000000000000000000000000000..f1329b9aead019b9b827ca3d89b4cccc2bfac795 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/rthooks.dat @@ -0,0 +1,3 @@ +{ + 'pyi_example_package': ['pyi_rth_naughty.py'] +} \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/rthooks/pyi_rth_naughty.py b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/rthooks/pyi_rth_naughty.py new file mode 100644 index 0000000000000000000000000000000000000000..2efd6e24d9fdcb837273450513af0f4ecced6e2a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/pyi_example_package/rthooks/pyi_rth_naughty.py @@ -0,0 +1 @@ +raise RuntimeError('I should not be running') diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/rthooks.dat b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/rthooks.dat new file mode 100644 index 0000000000000000000000000000000000000000..d99be230511ff7e555d4e38f0c572e0cc74c16da --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/rthooks.dat @@ -0,0 +1,3 @@ +{ + 'pyi_example_package': ['pyi_rth_good.py'] +} \ No newline at end of file diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/rthooks/pyi_rth_good.py b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/rthooks/pyi_rth_good.py new file mode 100644 index 0000000000000000000000000000000000000000..9b233d867a8648254b9ae0fc220634e4bb8f81f5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/rthooks/pyi_rth_good.py @@ -0,0 +1,10 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/setup.py b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..5881a571752ca0d65f9840772cfc6b9a03ebb0e8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hook_order_hooks/setup.py @@ -0,0 +1,13 @@ +from setuptools import setup + +setup( + name='pyi_example_package', + setup_requires="setuptools >= 40.0.0", + packages=['pyi_example_package'], + author='PyInstaller development team', + entry_points={ + 'pyinstaller40': [ + 'hooks = pyi_example_package.__init__:get_hook_dirs' + ] + } +) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/dynamiclib.dll b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/dynamiclib.dll new file mode 100644 index 0000000000000000000000000000000000000000..8fa2136f688510d6f4367b1c79a06907d4ca8885 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/dynamiclib.dll @@ -0,0 +1,2 @@ +# + diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/dynamiclib.dylib b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/dynamiclib.dylib new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/dynamiclib.dylib @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/nine.dat b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/nine.dat new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/nine.dat @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/data/eleven.dat b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/data/eleven.dat new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/data/eleven.dat @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/one.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/one.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/one.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/sub_pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/sub_pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/sub_pkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/sub_pkg/three.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/sub_pkg/three.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/sub_pkg/three.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/ten.dat b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/ten.dat new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/py_files_not_in_package/ten.dat @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/pyextension.pyd b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/pyextension.pyd new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/pyextension.pyd @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/pyextension.so b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/pyextension.so new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/pyextension.so @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/thirteen.txt b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/thirteen.txt new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/thirteen.txt @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/twelve.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/twelve.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/subpkg/twelve.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/two.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/two.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/hookutils_package/two.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/setup.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..6461b94c47ac5248640bfdc38682c840e05b2d84 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files/setup.py @@ -0,0 +1,23 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 assists in creating a ``.egg`` package for use with testing +# ``collect_submodules``. To do so, execute ``python setup.py bdist_egg``. +from setuptools import setup, find_packages + +setup( + name='hookutils_egg', + zip_safe=True, + packages=find_packages(), + # Manually include the fake extension modules for testing. They aren't + # automatically included. + package_data={'hookutils_package' : ['pyextension.*']}, +) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files2/foo/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files2/foo/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files2/foo/bar/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files2/foo/bar/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4b53fe93f10bf69a997e78052d5cfacae12800a9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/hookutils_files2/foo/bar/__init__.py @@ -0,0 +1,2 @@ +# Print something to stdout +print("Hello world!") diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_TOC.py b/3rdparty/pyinstaller-4.3/tests/unit/test_TOC.py new file mode 100644 index 0000000000000000000000000000000000000000..654a3ce446d5009fc1b5e9e7b8b16041bce971a9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_TOC.py @@ -0,0 +1,329 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 contains tests for the class:``TOC``, see +# https://pyinstaller.readthedocs.io/en/latest/advanced-topics.html#the-toc-and-tree-classes + +import pytest + +from PyInstaller.building.datastruct import TOC + + +ELEMS1 = ( + ('encodings', '/usr/lib/python2.7/encodings/__init__.py','PYMODULE'), + ('_random', '/usr/lib/python2.7/lib-dynload/_random.so', 'EXTENSION'), + ('libreadline.so.6', '/lib64/libreadline.so.6', 'BINARY'), +) + +ELEMS2 = ( + ('li-la-lu', '/home/myself/li-la-su', 'SOMETHING'), + ('schubidu', '/home/otherguy/schibidu', 'PKG'), +) + +ELEMS3 = ( + ('PIL.Image.py', '/usr/lib/python2.7/encodings/__init__.py','PYMODULE'), +) + + +def test_init_empty(): + toc = TOC() + assert len(toc) == 0 + + +def test_init(): + toc = TOC(ELEMS1) + assert len(toc) == 3 + assert toc == list(ELEMS1) + + +def test_append(): + toc = TOC(ELEMS1) + toc.append(('li-la-lu', '/home/myself/li-la-su', 'SOMETHING')) + expected = list(ELEMS1) + expected.append(('li-la-lu', '/home/myself/li-la-su', 'SOMETHING')) + assert toc == expected + + +def test_append_existing(): + toc = TOC(ELEMS1) + toc.append(ELEMS1[-1]) + expected = list(ELEMS1) + assert toc == expected + + +def test_append_keep_filename(): + # name in TOC should be the same as the one added + toc = TOC() + entry = ('EnCodIngs', '/usr/lib/python2.7/encodings.py', 'BINARY') + toc.append(entry) + assert toc[0][0] == entry[0] + + +def test_insert(): + toc = TOC(ELEMS1) + toc.insert(1, ('li-la-lu', '/home/myself/li-la-su', 'SOMETHING')) + expected = list(ELEMS1) + expected.insert(1, ('li-la-lu', '/home/myself/li-la-su', 'SOMETHING')) + assert toc == expected + + +def test_insert_existing(): + toc = TOC(ELEMS1) + toc.insert(0, ELEMS1[-1]) + toc.insert(1, ELEMS1[-1]) + expected = list(ELEMS1) + assert toc == expected + + +def test_insert_keep_filename(): + # name in TOC should be the same as the one added + toc = TOC() + entry = ('EnCodIngs', '/usr/lib/python2.7/encodings.py', 'BINARY') + toc.insert(1, entry) + assert toc[0][0] == entry[0] + + +def test_extend(): + toc = TOC(ELEMS1) + toc.extend(ELEMS2) + expected = list(ELEMS1) + expected.extend(ELEMS2) + assert toc == expected + + +def test_extend_existing(): + toc = TOC(ELEMS1) + toc.extend(ELEMS1) + expected = list(ELEMS1) + assert toc == expected + + +def test_add_list(): + toc = TOC(ELEMS1) + other = list(ELEMS2) + result = toc + other + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + list(ELEMS2) + assert result == expected + + +def test_add_tuple(): + toc = TOC(ELEMS1) + other = ELEMS2 + result = toc + other + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + list(ELEMS2) + assert result == expected + + +def test_add_toc(): + toc = TOC(ELEMS1) + other = TOC(ELEMS2) + result = toc + other + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + list(ELEMS2) + assert result == expected + + +def test_radd_list(): + toc = TOC(ELEMS1) + other = list(ELEMS2) + result = other + toc + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS2) + list(ELEMS1) + assert result == expected + + +def test_radd_tuple(): + toc = TOC(ELEMS1) + other = ELEMS2 + result = other + toc + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS2) + list(ELEMS1) + assert result == expected + + +def test_radd_toc(): + toc = TOC(ELEMS1) + other = TOC(ELEMS2) + result = other + toc + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS2) + list(ELEMS1) + assert result == expected + + +def test_sub_list(): + toc = TOC(ELEMS1) + ELEMS2 + other = list(ELEMS2) + result = toc - other + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + assert result == expected + + +def test_sub_tuple(): + toc = TOC(ELEMS1) + ELEMS2 + other = ELEMS2 + result = toc - other + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + assert result == expected + + +def test_sub_toc(): + toc = TOC(ELEMS1) + ELEMS2 + other = TOC(ELEMS2) + result = toc - other + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + assert result == expected + + +def test_sub_non_existing(): + toc = TOC(ELEMS1) + other = ELEMS3 + result = toc - other + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + assert result == expected + + +def test_rsub_list(): + toc = TOC(ELEMS1) + other = list(ELEMS1) + list(ELEMS2) + result = other - toc + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS2) + assert result == expected + + +def test_rsub_tuple(): + toc = TOC(ELEMS1) + other = tuple(list(ELEMS1) + list(ELEMS2)) + result = other - toc + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS2) + assert result == expected + + +def test_rsub_toc(): + toc = TOC(ELEMS1) + other = TOC(ELEMS1) + ELEMS2 + result = other - toc + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS2) + assert result == expected + + +def test_rsub_non_existing(): + toc = TOC(ELEMS3) + other = ELEMS1 + result = other - toc + assert result is not toc + assert result is not other + assert isinstance(result, TOC) + expected = list(ELEMS1) + assert result == expected + + +# The following tests verify that case-insensitive comparisons are used on Windows +# and only for appropriate TOC entry types + +@pytest.mark.win32 +def test_append_other_case_mixed(): + # If a binary file is added with the same filename as an existing pymodule, + # it should not be added. + + toc = TOC(ELEMS1) + elem = ('EnCodIngs', '/usr/lib/python2.7/encodings.py', 'BINARY') + toc.append(elem) + expected = list(ELEMS1) + assert toc == expected + + +@pytest.mark.win32 +def test_append_other_case_pymodule(): + # python modules should not use C-I comparisons. Both 'encodings' and + # 'EnCodIngs' should be added. + toc = TOC(ELEMS1) + elem = ('EnCodIngs', '/usr/lib/python2.7/encodings.py', 'PYMODULE') + toc.append(elem) + expected = list(ELEMS1) + expected.append(elem) + assert toc == expected + + +@pytest.mark.win32 +def test_append_other_case_binary(): + # binary files should use C-I comparisons. 'LiBrEADlInE.so.6' should not be added. + toc = TOC(ELEMS1) + toc.append(('LiBrEADlInE.so.6', '/lib64/libreadline.so.6', 'BINARY')) + expected = list(ELEMS1) + assert toc == expected + + +@pytest.mark.win32 +def test_insert_other_case_mixed(): + # If a binary file is added with the same filename as an existing pymodule, + # it should not be added + + toc = TOC(ELEMS1) + elem = ('EnCodIngs', '/usr/lib/python2.7/encodings.py', 'BINARY') + toc.insert(1, elem) + expected = list(ELEMS1) + assert toc == expected + + +@pytest.mark.win32 +def test_insert_other_case_pymodule(): + # python modules should not use C-I comparisons. Both 'encodings' and + # 'EnCodIngs' should be added. + toc = TOC(ELEMS1) + elem = ('EnCodIngs', '/usr/lib/python2.7/encodings.py', 'PYMODULE') + toc.insert(1, elem) + expected = list(ELEMS1) + expected.insert(1, elem) + assert toc == expected + + +@pytest.mark.win32 +def test_insert_other_case_binary(): + # binary files should use C-I comparisons. 'LiBrEADlInE.so.6' should not be added. + toc = TOC(ELEMS1) + toc.insert(1, ('LiBrEADlInE.so.6', '/lib64/libreadline.so.6', 'BINARY')) + expected = list(ELEMS1) + assert toc == expected diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_Tree.py b/3rdparty/pyinstaller-4.3/tests/unit/test_Tree.py new file mode 100644 index 0000000000000000000000000000000000000000..0b5948083035229c2c45548158d84c8395dc1745 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_Tree.py @@ -0,0 +1,74 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 contains tests for the class:``Tree``, see +# https://pyinstaller.readthedocs.io/en/latest/advanced-topics.html#the-tree-class + +import os +import pytest + +from os.path import join +import PyInstaller.building.datastruct + + +class Tree(PyInstaller.building.datastruct.Tree): + # A stripped-down version of PyInstaller.building.datastruct.Tree, + # not checking guts, but only the `assemble()` step + def __postinit__(self): + self.assemble() + + +TEST_MOD = 'Tree_files' +_DATA_BASEPATH = join(os.path.dirname(os.path.abspath(__file__)), TEST_MOD) + +_TEST_FILES = sorted([ + join('subpkg', 'twelve.py'), + join('subpkg', 'thirteen.txt'), + join('subpkg', 'init__.py'), + 'two.py', + 'dynamiclib.dylib', + join('py_files_not_in_package', 'sub_pkg', 'three.py'), + join('py_files_not_in_package', 'sub_pkg', 'init__.py'), + join('py_files_not_in_package', 'one.py'), + join('py_files_not_in_package', 'data', 'eleven.dat'), + join('py_files_not_in_package', 'ten.dat'), + 'dynamiclib.dll', + 'pyextension.pyd', + 'nine.dat', + 'init__.py', + 'pyextension.so', +]) + +_PARAMETERS = ( + (None, None, _TEST_FILES), + ('abc', None, [join('abc', f) for f in _TEST_FILES]), + (None, ['*.py'], + [f for f in _TEST_FILES if not f.endswith('.py')]), + (None, ['*.py', '*.pyd'], + [f for f in _TEST_FILES if not f.endswith(('.py', '.pyd'))]), + (None, ['subpkg'], + [f for f in _TEST_FILES + if not f.startswith('subpkg')]), + (None, ['subpkg', 'sub_pkg'], + [f for f in _TEST_FILES + if not (f.startswith('subpkg') or os.sep+'sub_pkg'+os.sep in f)]), + ('klm', ['subpkg', 'sub_pkg', '*.py', '*.pyd'], + [join('klm', f) for f in _TEST_FILES + if not (f.startswith('subpkg') or os.sep+'sub_pkg'+os.sep in f or + f.endswith(('.py', '.pyd')))]), +) + +@pytest.mark.parametrize("prefix,excludes,result", _PARAMETERS) +def test_Tree(monkeypatch, prefix, excludes, result): + monkeypatch.setattr('PyInstaller.config.CONF', {'workpath': '.'}) + tree = Tree(_DATA_BASEPATH, prefix=prefix, excludes=excludes) + files = sorted(f[0] for f in tree) + assert files == sorted(result) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_additionalfilescache.py b/3rdparty/pyinstaller-4.3/tests/unit/test_additionalfilescache.py new file mode 100644 index 0000000000000000000000000000000000000000..289673e16eaec9bacab2806ec00abb7ee335f8eb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_additionalfilescache.py @@ -0,0 +1,33 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.depend.imphook import AdditionalFilesCache + + +def test_binaries_and_datas(): + datas, binaries, modnames = [ + [('source', 'dest'), ('src', 'dst')], + [('abc', 'def'), ('ghi', 'jkl')], + ['testmodule1', 'testmodule2'] + ] + + cache = AdditionalFilesCache() + for modname in modnames: + cache.add( + modname, binaries, datas + ) + assert cache.datas(modname) == datas + cache.add( + modname, binaries, datas + ) + # This should be extended. Therefore it should be binaries*2 + assert cache.binaries(modname) != binaries + assert cache.binaries(modname) == binaries * 2 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..9c035d19b12fb88c431565d7052f032a3f622dfb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/__init__.py @@ -0,0 +1 @@ +""" altgraph tests """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_altgraph.py b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_altgraph.py new file mode 100644 index 0000000000000000000000000000000000000000..be1d24a0cce1be5c18eac334305f2aad74974781 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_altgraph.py @@ -0,0 +1,45 @@ +#!/usr/bin/env py.test +import os +import sys + +from altgraph import Graph, GraphAlgo +import unittest + +class BasicTests (unittest.TestCase): + def setUp(self): + self.edges = [ + (1,2), (2,4), (1,3), (2,4), (3,4), (4,5), (6,5), (6,14), (14,15), + (6, 15), (5,7), (7, 8), (7,13), (12,8), (8,13), (11,12), (11,9), + (13,11), (9,13), (13,10) + ] + + # these are the edges + self.store = {} + self.g = Graph.Graph() + for head, tail in self.edges: + self.store[head] = self.store[tail] = None + self.g.add_edge(head, tail) + + def test_num_edges(self): + # check the parameters + self.assertEqual(self.g.number_of_nodes(), len(self.store)) + self.assertEqual(self.g.number_of_edges(), len(self.edges)) + + def test_forw_bfs(self): + # do a forward bfs + self.assertEqual( self.g.forw_bfs(1), + [1, 2, 3, 4, 5, 7, 8, 13, 11, 10, 12, 9]) + + + def test_get_hops(self): + # diplay the hops and hop numbers between nodes + self.assertEqual(self.g.get_hops(1, 8), + [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]) + + def test_shortest_path(self): + self.assertEqual(GraphAlgo.shortest_path(self.g, 1, 12), + [1, 2, 4, 5, 7, 13, 11, 12]) + + +if __name__ == "__main__": # pragma: no cover + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_dot.py b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_dot.py new file mode 100644 index 0000000000000000000000000000000000000000..71da58ba04b3b4aa976ff7dc8667b15b4a88156b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_dot.py @@ -0,0 +1,368 @@ +import unittest +import os + +from altgraph import Dot +from altgraph import Graph +from altgraph import GraphError + + +class TestDot (unittest.TestCase): + + def test_constructor(self): + g = Graph.Graph([ + (1,2), + (1,3), + (1,4), + (2,4), + (2,6), + (2,7), + (7,4), + (6,1), + ] + ) + + dot = Dot.Dot(g) + + self.assertEqual(dot.name, 'G') + self.assertEqual(dot.attr, {}) + self.assertEqual(dot.temp_dot, 'tmp_dot.dot') + self.assertEqual(dot.temp_neo, 'tmp_neo.dot') + self.assertEqual(dot.dot, 'dot') + self.assertEqual(dot.dotty, 'dotty') + self.assertEqual(dot.neato, 'neato') + self.assertEqual(dot.type, 'digraph') + + self.assertEqual(dot.nodes, dict([(x, {}) for x in g])) + + edges = {} + for head in g: + edges[head] = {} + for tail in g.out_nbrs(head): + edges[head][tail] = {} + + self.assertEqual(dot.edges[1], edges[1]) + self.assertEqual(dot.edges, edges) + + + dot = Dot.Dot(g, nodes=[1,2], + edgefn=lambda node: list(sorted(g.out_nbrs(node)))[:-1], + nodevisitor=lambda node: {'label': node}, + edgevisitor=lambda head, tail: {'label': (head, tail) }, + name="testgraph", + dot='/usr/local/bin/dot', + dotty='/usr/local/bin/dotty', + neato='/usr/local/bin/neato', + graphtype="graph") + + self.assertEqual(dot.name, 'testgraph') + self.assertEqual(dot.attr, {}) + self.assertEqual(dot.temp_dot, 'tmp_dot.dot') + self.assertEqual(dot.temp_neo, 'tmp_neo.dot') + self.assertEqual(dot.dot, '/usr/local/bin/dot') + self.assertEqual(dot.dotty, '/usr/local/bin/dotty') + self.assertEqual(dot.neato, '/usr/local/bin/neato') + self.assertEqual(dot.type, 'graph') + + self.assertEqual(dot.nodes, dict([(x, {'label': x}) for x in [1,2]])) + + edges = {} + for head in [1,2]: + edges[head] = {} + for tail in list(sorted(g.out_nbrs(head)))[:-1]: + if tail not in [1,2]: continue + edges[head][tail] = {'label': (head, tail) } + + self.assertEqual(dot.edges[1], edges[1]) + self.assertEqual(dot.edges, edges) + + self.assertRaises(GraphError, Dot.Dot, g, nodes=[1,2, 9]) + + def test_style(self): + g = Graph.Graph([]) + + dot = Dot.Dot(g) + + self.assertEqual(dot.attr, {}) + + dot.style(key='value') + self.assertEqual(dot.attr, {'key': 'value'}) + + dot.style(key2='value2') + self.assertEqual(dot.attr, {'key2': 'value2'}) + + def test_node_style(self): + g = Graph.Graph([ + (1,2), + (1,3), + (1,4), + (2,4), + (2,6), + (2,7), + (7,4), + (6,1), + ] + ) + + dot = Dot.Dot(g) + + self.assertEqual(dot.nodes[1], {}) + + dot.node_style(1, key='value') + self.assertEqual(dot.nodes[1], {'key': 'value'}) + + dot.node_style(1, key2='value2') + self.assertEqual(dot.nodes[1], {'key2': 'value2'}) + self.assertEqual(dot.nodes[2], {}) + + dot.all_node_style(key3='value3') + for n in g: + self.assertEqual(dot.nodes[n], {'key3': 'value3'}) + + self.assertTrue(9 not in dot.nodes) + dot.node_style(9, key='value') + self.assertEqual(dot.nodes[9], {'key': 'value'}) + + def test_edge_style(self): + g = Graph.Graph([ + (1,2), + (1,3), + (1,4), + (2,4), + (2,6), + (2,7), + (7,4), + (6,1), + ] + ) + + dot = Dot.Dot(g) + + self.assertEqual(dot.edges[1][2], {}) + dot.edge_style(1,2, foo='bar') + self.assertEqual(dot.edges[1][2], {'foo': 'bar'}) + + dot.edge_style(1,2, foo2='2bar') + self.assertEqual(dot.edges[1][2], {'foo2': '2bar'}) + + self.assertEqual(dot.edges[1][3], {}) + + self.assertFalse(6 in dot.edges[1]) + dot.edge_style(1,6, foo2='2bar') + self.assertEqual(dot.edges[1][6], {'foo2': '2bar'}) + + self.assertRaises(GraphError, dot.edge_style, 1, 9, a=1) + self.assertRaises(GraphError, dot.edge_style, 9, 1, a=1) + + + def test_iter(self): + g = Graph.Graph([ + (1,2), + (1,3), + (1,4), + (2,4), + (2,6), + (2,7), + (7,4), + (6,1), + ] + ) + + dot = Dot.Dot(g) + dot.style(graph="foobar") + dot.node_style(1, key='value') + dot.node_style(2, key='another', key2='world') + dot.edge_style(1,4, key1='value1', key2='value2') + dot.edge_style(2,4, key1='valueA') + + self.assertEqual(list(iter(dot)), list(dot.iterdot())) + + for item in dot.iterdot(): + self.assertTrue(isinstance(item, str)) + + first = list(dot.iterdot())[0] + self.assertEqual(first, "digraph %s {\n"%(dot.name,)) + + dot.type = 'graph' + first = list(dot.iterdot())[0] + self.assertEqual(first, "graph %s {\n"%(dot.name,)) + + dot.type = 'foo' + self.assertRaises(GraphError, list, dot.iterdot()) + dot.type = 'digraph' + + self.assertEqual(list(dot), [ + 'digraph G {\n', + 'graph="foobar";', + '\n', + + '\t"1" [', + 'key="value",', + '];\n', + + '\t"2" [', + 'key="another",', + 'key2="world",', + '];\n', + + '\t"3" [', + '];\n', + + '\t"4" [', + '];\n', + + '\t"6" [', + '];\n', + + '\t"7" [', + '];\n', + + '\t"1" -> "2" [', + '];\n', + + '\t"1" -> "3" [', + '];\n', + + '\t"1" -> "4" [', + 'key1="value1",', + 'key2="value2",', + '];\n', + + '\t"2" -> "4" [', + 'key1="valueA",', + '];\n', + + '\t"2" -> "6" [', + '];\n', + + '\t"2" -> "7" [', + '];\n', + + '\t"6" -> "1" [', + '];\n', + + '\t"7" -> "4" [', + '];\n', + '}\n']) + + + def test_save(self): + g = Graph.Graph([ + (1,2), + (1,3), + (1,4), + (2,4), + (2,6), + (2,7), + (7,4), + (6,1), + ] + ) + + dot = Dot.Dot(g) + dot.style(graph="foobar") + dot.node_style(1, key='value') + dot.node_style(2, key='another', key2='world') + dot.edge_style(1,4, key1='value1', key2='value2') + dot.edge_style(2,4, key1='valueA') + + fn = 'test_dot.dot' + self.assertTrue(not os.path.exists(fn)) + + try: + dot.save_dot(fn) + + with open(fn, 'r') as fp: + self.assertEqual(fp.read(), ''.join(dot)) + + finally: + if os.path.exists(fn): + os.unlink(fn) + + + def test_img(self): + g = Graph.Graph([ + (1,2), + (1,3), + (1,4), + (2,4), + (2,6), + (2,7), + (7,4), + (6,1), + ] + ) + + dot = Dot.Dot(g, dot='/usr/local/bin/!!dot', dotty='/usr/local/bin/!!dotty', neato='/usr/local/bin/!!neato') + dot.style(size='10,10', rankdir='RL', page='5, 5' , ranksep=0.75) + dot.node_style(1, label='BASE_NODE',shape='box', color='blue') + dot.node_style(2, style='filled', fillcolor='red') + dot.edge_style(1,4, style='dotted') + dot.edge_style(2,4, arrowhead='dot', label='binds', labelangle='90') + + system_cmds = [] + def fake_system(cmd): + system_cmds.append(cmd) + return None + + try: + real_system = os.system + os.system = fake_system + + system_cmds = [] + dot.save_img('foo') + self.assertEqual(system_cmds, ['/usr/local/bin/!!dot -Tgif tmp_dot.dot -o foo.gif']) + + system_cmds = [] + dot.save_img('foo', file_type='jpg') + self.assertEqual(system_cmds, ['/usr/local/bin/!!dot -Tjpg tmp_dot.dot -o foo.jpg']) + + system_cmds = [] + dot.save_img('bar', file_type='jpg', mode='neato') + self.assertEqual(system_cmds, [ + '/usr/local/bin/!!neato -o tmp_dot.dot tmp_neo.dot', + '/usr/local/bin/!!dot -Tjpg tmp_dot.dot -o bar.jpg', + ]) + + system_cmds = [] + dot.display() + self.assertEqual(system_cmds, [ + '/usr/local/bin/!!dotty tmp_dot.dot' + ]) + + system_cmds = [] + dot.display(mode='neato') + self.assertEqual(system_cmds, [ + '/usr/local/bin/!!neato -o tmp_dot.dot tmp_neo.dot', + '/usr/local/bin/!!dotty tmp_dot.dot' + ]) + + finally: + if os.path.exists(dot.temp_dot): + os.unlink(dot.temp_dot) + if os.path.exists(dot.temp_neo): + os.unlink(dot.temp_neo) + os.system = real_system + + if os.path.exists('/usr/local/bin/dot') and os.path.exists('/usr/local/bin/neato'): + try: + dot.dot='/usr/local/bin/dot' + dot.neato='/usr/local/bin/neato' + self.assertFalse(os.path.exists('foo.gif')) + dot.save_img('foo') + self.assertTrue(os.path.exists('foo.gif')) + os.unlink('foo.gif') + + self.assertFalse(os.path.exists('foo.gif')) + dot.save_img('foo', mode='neato') + self.assertTrue(os.path.exists('foo.gif')) + os.unlink('foo.gif') + + finally: + if os.path.exists(dot.temp_dot): + os.unlink(dot.temp_dot) + if os.path.exists(dot.temp_neo): + os.unlink(dot.temp_neo) + + +if __name__ == "__main__": # pragma: no cover + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graph.py b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graph.py new file mode 100644 index 0000000000000000000000000000000000000000..d28164948e6789f470a2a8085189e2fc7ca8ab83 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graph.py @@ -0,0 +1,661 @@ +import unittest + +from altgraph import GraphError +from altgraph.Graph import Graph + +class TestGraph (unittest.TestCase): + + def test_nodes(self): + graph = Graph() + + self.assertEqual(graph.node_list(), []) + + o1 = object() + o1b = object() + o2 = object() + graph.add_node(1, o1) + graph.add_node(1, o1b) + graph.add_node(2, o2) + graph.add_node(3) + + self.assertRaises(TypeError, graph.add_node, []) + + self.assertTrue(graph.node_data(1) is o1) + self.assertTrue(graph.node_data(2) is o2) + self.assertTrue(graph.node_data(3) is None) + + self.assertTrue(1 in graph) + self.assertTrue(2 in graph) + self.assertTrue(3 in graph) + + self.assertEqual(graph.number_of_nodes(), 3) + self.assertEqual(graph.number_of_hidden_nodes(), 0) + self.assertEqual(graph.hidden_node_list(), []) + self.assertEqual(list(sorted(graph)), [1, 2, 3]) + + graph.hide_node(1) + graph.hide_node(2) + graph.hide_node(3) + + + self.assertEqual(graph.number_of_nodes(), 0) + self.assertEqual(graph.number_of_hidden_nodes(), 3) + self.assertEqual(list(sorted(graph.hidden_node_list())), [1, 2, 3]) + + self.assertFalse(1 in graph) + self.assertFalse(2 in graph) + self.assertFalse(3 in graph) + + graph.add_node(1) + self.assertFalse(1 in graph) + + graph.restore_node(1) + self.assertTrue(1 in graph) + self.assertFalse(2 in graph) + self.assertFalse(3 in graph) + + graph.restore_all_nodes() + self.assertTrue(1 in graph) + self.assertTrue(2 in graph) + self.assertTrue(3 in graph) + + self.assertEqual(list(sorted(graph.node_list())), [1, 2, 3]) + + v = graph.describe_node(1) + self.assertEqual(v, (1, o1, [], [])) + + def test_edges(self): + graph = Graph() + graph.add_node(1) + graph.add_node(2) + graph.add_node(3) + graph.add_node(4) + graph.add_node(5) + + self.assertTrue(isinstance(graph.edge_list(), list)) + + graph.add_edge(1, 2) + graph.add_edge(4, 5, 'a') + + self.assertRaises(GraphError, graph.add_edge, 'a', 'b', create_nodes=False) + + self.assertEqual(graph.number_of_hidden_edges(), 0) + self.assertEqual(graph.number_of_edges(), 2) + e = graph.edge_by_node(1, 2) + self.assertTrue(isinstance(e, int)) + graph.hide_edge(e) + self.assertEqual(graph.number_of_hidden_edges(), 1) + self.assertEqual(graph.number_of_edges(), 1) + e2 = graph.edge_by_node(1, 2) + self.assertTrue(e2 is None) + + graph.restore_edge(e) + e2 = graph.edge_by_node(1, 2) + self.assertEqual(e, e2) + self.assertEqual(graph.number_of_hidden_edges(), 0) + + self.assertEqual(graph.number_of_edges(), 2) + + e1 = graph.edge_by_node(1, 2) + e2 = graph.edge_by_node(4, 5) + graph.hide_edge(e1) + graph.hide_edge(e2) + + self.assertEqual(graph.number_of_edges(), 0) + graph.restore_all_edges() + self.assertEqual(graph.number_of_edges(), 2) + + self.assertEqual(graph.edge_by_id(e1), (1,2)) + self.assertRaises(GraphError, graph.edge_by_id, (e1+1)*(e2+1)+1) + + self.assertEqual(list(sorted(graph.edge_list())), [e1, e2]) + + self.assertEqual(graph.describe_edge(e1), (e1, 1, 1, 2)) + self.assertEqual(graph.describe_edge(e2), (e2, 'a', 4, 5)) + + self.assertEqual(graph.edge_data(e1), 1) + self.assertEqual(graph.edge_data(e2), 'a') + + self.assertEqual(graph.head(e2), 4) + self.assertEqual(graph.tail(e2), 5) + + graph.add_edge(1, 3) + graph.add_edge(1, 5) + graph.add_edge(4, 1) + + self.assertEqual(list(sorted(graph.out_nbrs(1))), [2, 3, 5]) + self.assertEqual(list(sorted(graph.inc_nbrs(1))), [4]) + self.assertEqual(list(sorted(graph.inc_nbrs(5))), [1, 4]) + self.assertEqual(list(sorted(graph.all_nbrs(1))), [2, 3, 4, 5]) + + graph.add_edge(5, 1) + self.assertEqual(list(sorted(graph.all_nbrs(5))), [1, 4]) + + self.assertEqual(graph.out_degree(1), 3) + self.assertEqual(graph.inc_degree(2), 1) + self.assertEqual(graph.inc_degree(5), 2) + self.assertEqual(graph.all_degree(5), 3) + + v = graph.out_edges(4) + self.assertTrue(isinstance(v, list)) + self.assertEqual(graph.edge_by_id(v[0]), (4, 5)) + + v = graph.out_edges(1) + for e in v: + self.assertEqual(graph.edge_by_id(e)[0], 1) + + v = graph.inc_edges(1) + self.assertTrue(isinstance(v, list)) + self.assertEqual(graph.edge_by_id(v[0]), (4, 1)) + + v = graph.inc_edges(5) + for e in v: + self.assertEqual(graph.edge_by_id(e)[1], 5) + + v = graph.all_edges(5) + for e in v: + self.assertTrue(graph.edge_by_id(e)[1] == 5 or graph.edge_by_id(e)[0] == 5) + + e1 = graph.edge_by_node(1, 2) + self.assertTrue(isinstance(e1, int)) + graph.hide_node(1) + self.assertRaises(GraphError, graph.edge_by_node, 1, 2) + graph.restore_node(1) + e2 = graph.edge_by_node(1, 2) + self.assertEqual(e1, e2) + + + + def test_toposort(self): + graph = Graph() + graph.add_node(1) + graph.add_node(2) + graph.add_node(3) + graph.add_node(4) + graph.add_node(5) + + graph.add_edge(1, 2) + graph.add_edge(1, 3) + graph.add_edge(2, 4) + graph.add_edge(3, 5) + + ok, result = graph.forw_topo_sort() + self.assertTrue(ok) + for idx in range(1, 6): + self.assertTrue(idx in result) + + self.assertTrue(result.index(1) < result.index(2)) + self.assertTrue(result.index(1) < result.index(3)) + self.assertTrue(result.index(2) < result.index(4)) + self.assertTrue(result.index(3) < result.index(5)) + + ok, result = graph.back_topo_sort() + self.assertTrue(ok) + for idx in range(1, 6): + self.assertTrue(idx in result) + self.assertTrue(result.index(2) < result.index(1)) + self.assertTrue(result.index(3) < result.index(1)) + self.assertTrue(result.index(4) < result.index(2)) + self.assertTrue(result.index(5) < result.index(3)) + + + # Same graph as before, but with edges + # reversed, which means we should get + # the same results as before if using + # back_topo_sort rather than forw_topo_sort + # (and v.v.) + + graph = Graph() + graph.add_node(1) + graph.add_node(2) + graph.add_node(3) + graph.add_node(4) + graph.add_node(5) + + graph.add_edge(2, 1) + graph.add_edge(3, 1) + graph.add_edge(4, 2) + graph.add_edge(5, 3) + + ok, result = graph.back_topo_sort() + self.assertTrue(ok) + for idx in range(1, 6): + self.assertTrue(idx in result) + + self.assertTrue(result.index(1) < result.index(2)) + self.assertTrue(result.index(1) < result.index(3)) + self.assertTrue(result.index(2) < result.index(4)) + self.assertTrue(result.index(3) < result.index(5)) + + ok, result = graph.forw_topo_sort() + self.assertTrue(ok) + for idx in range(1, 6): + self.assertTrue(idx in result) + self.assertTrue(result.index(2) < result.index(1)) + self.assertTrue(result.index(3) < result.index(1)) + self.assertTrue(result.index(4) < result.index(2)) + self.assertTrue(result.index(5) < result.index(3)) + + + # Create a cycle + graph.add_edge(1, 5) + ok, result = graph.forw_topo_sort() + self.assertFalse(ok) + ok, result = graph.back_topo_sort() + self.assertFalse(ok) + + def test_bfs_subgraph(self): + graph = Graph() + graph.add_edge(1, 2) + graph.add_edge(1, 4) + graph.add_edge(2, 4) + graph.add_edge(4, 8) + graph.add_edge(4, 9) + graph.add_edge(4, 10) + graph.add_edge(8, 10) + + subgraph = graph.forw_bfs_subgraph(10) + self.assertTrue(isinstance(subgraph, Graph)) + self.assertEqual(subgraph.number_of_nodes(), 1) + self.assertTrue(10 in subgraph) + self.assertEqual(subgraph.number_of_edges(), 0) + + subgraph = graph.forw_bfs_subgraph(4) + self.assertTrue(isinstance(subgraph, Graph)) + self.assertEqual(subgraph.number_of_nodes(), 4) + self.assertTrue(4 in subgraph) + self.assertTrue(8 in subgraph) + self.assertTrue(9 in subgraph) + self.assertTrue(10 in subgraph) + self.assertEqual(subgraph.number_of_edges(), 4) + e = subgraph.edge_by_node(4, 8) + e = subgraph.edge_by_node(4, 9) + e = subgraph.edge_by_node(4, 10) + e = subgraph.edge_by_node(8, 10) + + # same graph as before, but switch around + # edges. This results in the same test results + # but now for back_bfs_subgraph rather than + # forw_bfs_subgraph + + graph = Graph() + graph.add_edge(2, 1) + graph.add_edge(4, 1) + graph.add_edge(4, 2) + graph.add_edge(8, 4) + graph.add_edge(9, 4) + graph.add_edge(10, 4) + graph.add_edge(10, 8) + + subgraph = graph.back_bfs_subgraph(10) + self.assertTrue(isinstance(subgraph, Graph)) + self.assertEqual(subgraph.number_of_nodes(), 1) + self.assertTrue(10 in subgraph) + self.assertEqual(subgraph.number_of_edges(), 0) + + subgraph = graph.back_bfs_subgraph(4) + self.assertTrue(isinstance(subgraph, Graph)) + self.assertEqual(subgraph.number_of_nodes(), 4) + self.assertTrue(4 in subgraph) + self.assertTrue(8 in subgraph) + self.assertTrue(9 in subgraph) + self.assertTrue(10 in subgraph) + self.assertEqual(subgraph.number_of_edges(), 4) + e = subgraph.edge_by_node(4, 8) + e = subgraph.edge_by_node(4, 9) + e = subgraph.edge_by_node(4, 10) + e = subgraph.edge_by_node(8, 10) + + def test_bfs_subgraph_does_not_reverse_egde_direction(self): + graph = Graph() + graph.add_node('A') + graph.add_node('B') + graph.add_node('C') + graph.add_edge('A', 'B') + graph.add_edge('B', 'C') + + whole_graph = graph.forw_topo_sort() + subgraph_backward = graph.back_bfs_subgraph('C') + subgraph_backward = subgraph_backward.forw_topo_sort() + self.assertEquals(whole_graph, subgraph_backward) + + subgraph_forward = graph.forw_bfs_subgraph('A') + subgraph_forward = subgraph_forward.forw_topo_sort() + self.assertEquals(whole_graph, subgraph_forward) + + def test_iterdfs(self): + graph = Graph() + graph.add_edge("1", "1.1") + graph.add_edge("1", "1.2") + graph.add_edge("1", "1.3") + graph.add_edge("1.1", "1.1.1") + graph.add_edge("1.1", "1.1.2") + graph.add_edge("1.2", "1.2.1") + graph.add_edge("1.2", "1.2.2") + graph.add_edge("1.2.2", "1.2.2.1") + graph.add_edge("1.2.2", "1.2.2.2") + graph.add_edge("1.2.2", "1.2.2.3") + + result = list(graph.iterdfs("1")) + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1' + ]) + result = list(graph.iterdfs("1", "1.2.1")) + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1' + ]) + + result = graph.forw_dfs("1") + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1' + ]) + result = graph.forw_dfs("1", "1.2.1") + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1' + ]) + + graph = Graph() + graph.add_edge("1.1", "1") + graph.add_edge("1.2", "1") + graph.add_edge("1.3", "1") + graph.add_edge("1.1.1", "1.1") + graph.add_edge("1.1.2", "1.1") + graph.add_edge("1.2.1", "1.2") + graph.add_edge("1.2.2", "1.2") + graph.add_edge("1.2.2.1", "1.2.2") + graph.add_edge("1.2.2.2", "1.2.2") + graph.add_edge("1.2.2.3", "1.2.2") + + result = list(graph.iterdfs("1", forward=False)) + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1' + ]) + result = list(graph.iterdfs("1", "1.2.1", forward=False)) + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1' + ]) + result = graph.back_dfs("1") + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1' + ]) + result = graph.back_dfs("1", "1.2.1") + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1' + ]) + + + # Introduce cyle: + graph.add_edge("1", "1.2") + result = list(graph.iterdfs("1", forward=False)) + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1' + ]) + + result = graph.back_dfs("1") + self.assertEqual(result, [ + '1', '1.3', '1.2', '1.2.2', '1.2.2.3', '1.2.2.2', + '1.2.2.1', '1.2.1', '1.1', '1.1.2', '1.1.1' + ]) + + + def test_iterdata(self): + graph = Graph() + graph.add_node("1", "I") + graph.add_node("1.1", "I.I") + graph.add_node("1.2", "I.II") + graph.add_node("1.3", "I.III") + graph.add_node("1.1.1", "I.I.I") + graph.add_node("1.1.2", "I.I.II") + graph.add_node("1.2.1", "I.II.I") + graph.add_node("1.2.2", "I.II.II") + graph.add_node("1.2.2.1", "I.II.II.I") + graph.add_node("1.2.2.2", "I.II.II.II") + graph.add_node("1.2.2.3", "I.II.II.III") + + graph.add_edge("1", "1.1") + graph.add_edge("1", "1.2") + graph.add_edge("1", "1.3") + graph.add_edge("1.1", "1.1.1") + graph.add_edge("1.1", "1.1.2") + graph.add_edge("1.2", "1.2.1") + graph.add_edge("1.2", "1.2.2") + graph.add_edge("1.2.2", "1.2.2.1") + graph.add_edge("1.2.2", "1.2.2.2") + graph.add_edge("1.2.2", "1.2.2.3") + + result = list(graph.iterdata("1", forward=True)) + self.assertEqual(result, [ + 'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II', + 'I.II.II.I', 'I.II.I', 'I.I', 'I.I.II', 'I.I.I' + ]) + + result = list(graph.iterdata("1", end="1.2.1", forward=True)) + self.assertEqual(result, [ + 'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II', + 'I.II.II.I', 'I.II.I' + ]) + + result = list(graph.iterdata("1", condition=lambda n: len(n) < 6, forward=True)) + self.assertEqual(result, [ + 'I', 'I.III', 'I.II', + 'I.I', 'I.I.I' + ]) + + + # And the revese option: + graph = Graph() + graph.add_node("1", "I") + graph.add_node("1.1", "I.I") + graph.add_node("1.2", "I.II") + graph.add_node("1.3", "I.III") + graph.add_node("1.1.1", "I.I.I") + graph.add_node("1.1.2", "I.I.II") + graph.add_node("1.2.1", "I.II.I") + graph.add_node("1.2.2", "I.II.II") + graph.add_node("1.2.2.1", "I.II.II.I") + graph.add_node("1.2.2.2", "I.II.II.II") + graph.add_node("1.2.2.3", "I.II.II.III") + + graph.add_edge("1.1", "1") + graph.add_edge("1.2", "1") + graph.add_edge("1.3", "1") + graph.add_edge("1.1.1", "1.1") + graph.add_edge("1.1.2", "1.1") + graph.add_edge("1.2.1", "1.2") + graph.add_edge("1.2.2", "1.2") + graph.add_edge("1.2.2.1", "1.2.2") + graph.add_edge("1.2.2.2", "1.2.2") + graph.add_edge("1.2.2.3", "1.2.2") + + result = list(graph.iterdata("1", forward=False)) + self.assertEqual(result, [ + 'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II', + 'I.II.II.I', 'I.II.I', 'I.I', 'I.I.II', 'I.I.I' + ]) + + result = list(graph.iterdata("1", end="1.2.1", forward=False)) + self.assertEqual(result, [ + 'I', 'I.III', 'I.II', 'I.II.II', 'I.II.II.III', 'I.II.II.II', + 'I.II.II.I', 'I.II.I' + ]) + + result = list(graph.iterdata("1", condition=lambda n: len(n) < 6, forward=False)) + self.assertEqual(result, [ + 'I', 'I.III', 'I.II', + 'I.I', 'I.I.I' + ]) + + def test_bfs(self): + graph = Graph() + graph.add_edge("1", "1.1") + graph.add_edge("1.1", "1.1.1") + graph.add_edge("1.1", "1.1.2") + graph.add_edge("1.1.2", "1.1.2.1") + graph.add_edge("1.1.2", "1.1.2.2") + graph.add_edge("1", "1.2") + graph.add_edge("1", "1.3") + graph.add_edge("1.2", "1.2.1") + + self.assertEqual(graph.forw_bfs("1"), + ['1', '1.1', '1.2', '1.3', '1.1.1', '1.1.2', '1.2.1', '1.1.2.1', '1.1.2.2']) + self.assertEqual(graph.forw_bfs("1", "1.1.1"), + ['1', '1.1', '1.2', '1.3', '1.1.1']) + + + # And the "reverse" graph + graph = Graph() + graph.add_edge("1.1", "1") + graph.add_edge("1.1.1", "1.1") + graph.add_edge("1.1.2", "1.1") + graph.add_edge("1.1.2.1", "1.1.2") + graph.add_edge("1.1.2.2", "1.1.2") + graph.add_edge("1.2", "1") + graph.add_edge("1.3", "1") + graph.add_edge("1.2.1", "1.2") + + self.assertEqual(graph.back_bfs("1"), + ['1', '1.1', '1.2', '1.3', '1.1.1', '1.1.2', '1.2.1', '1.1.2.1', '1.1.2.2']) + self.assertEqual(graph.back_bfs("1", "1.1.1"), + ['1', '1.1', '1.2', '1.3', '1.1.1']) + + + + # check cycle handling + graph.add_edge("1", "1.2.1") + self.assertEqual(graph.back_bfs("1"), + ['1', '1.1', '1.2', '1.3', '1.1.1', '1.1.2', '1.2.1', '1.1.2.1', '1.1.2.2']) + + + def test_connected(self): + graph = Graph() + graph.add_node(1) + graph.add_node(2) + graph.add_node(3) + graph.add_node(4) + + self.assertFalse(graph.connected()) + + graph.add_edge(1, 2) + graph.add_edge(3, 4) + self.assertFalse(graph.connected()) + + graph.add_edge(2, 3) + graph.add_edge(4, 1) + self.assertTrue(graph.connected()) + + def test_edges_complex(self): + g = Graph() + g.add_edge(1, 2) + e = g.edge_by_node(1,2) + g.hide_edge(e) + g.hide_node(2) + self.assertRaises(GraphError, g.restore_edge, e) + + g.restore_all_edges() + self.assertRaises(GraphError, g.edge_by_id, e) + + def test_clust_coef(self): + g = Graph() + g.add_edge(1, 2) + g.add_edge(1, 3) + g.add_edge(1, 4) + self.assertEqual(g.clust_coef(1), 0) + + g.add_edge(2, 5) + g.add_edge(3, 5) + g.add_edge(4, 5) + self.assertEqual(g.clust_coef(1), 0) + + g.add_edge(2, 3) + self.assertEqual(g.clust_coef(1), 1./6) + g.add_edge(2, 4) + self.assertEqual(g.clust_coef(1), 2./6) + g.add_edge(4, 2) + self.assertEqual(g.clust_coef(1), 3./6) + + g.add_edge(2, 3) + g.add_edge(2, 4) + g.add_edge(3, 4) + g.add_edge(3, 2) + g.add_edge(4, 2) + g.add_edge(4, 3) + self.assertEqual(g.clust_coef(1), 1) + + + def test_get_hops(self): + graph = Graph() + graph.add_edge(1, 2) + graph.add_edge(1, 3) + graph.add_edge(2, 4) + graph.add_edge(4, 5) + graph.add_edge(5, 7) + graph.add_edge(7, 8) + + self.assertEqual(graph.get_hops(1), + [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]) + + self.assertEqual(graph.get_hops(1, 5), + [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3)]) + + graph.add_edge(5, 1) + graph.add_edge(7, 1) + graph.add_edge(7, 4) + + self.assertEqual(graph.get_hops(1), + [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]) + + # And the reverse graph + graph = Graph() + graph.add_edge(2, 1) + graph.add_edge(3, 1) + graph.add_edge(4, 2) + graph.add_edge(5, 4) + graph.add_edge(7, 5) + graph.add_edge(8, 7) + + self.assertEqual(graph.get_hops(1, forward=False), + [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]) + + self.assertEqual(graph.get_hops(1, 5, forward=False), + [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3)]) + + graph.add_edge(1, 5) + graph.add_edge(1, 7) + graph.add_edge(4, 7) + + self.assertEqual(graph.get_hops(1, forward=False), + [(1, 0), (2, 1), (3, 1), (4, 2), (5, 3), (7, 4), (8, 5)]) + + + def test_constructor(self): + graph = Graph(iter([ + (1, 2), + (2, 3, 'a'), + (1, 3), + (3, 4), + ])) + self.assertEqual(graph.number_of_nodes(), 4) + self.assertEqual(graph.number_of_edges(), 4) + try: + graph.edge_by_node(1,2) + graph.edge_by_node(2,3) + graph.edge_by_node(1,3) + graph.edge_by_node(3,4) + except GraphError: + self.fail("Incorrect graph") + + self.assertEqual(graph.edge_data(graph.edge_by_node(2, 3)), 'a') + + self.assertRaises(GraphError, Graph, [(1,2,3,4)]) + +if __name__ == "__main__": # pragma: no cover + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graphstat.py b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graphstat.py new file mode 100644 index 0000000000000000000000000000000000000000..bfff701fcf59b24a0e11ceaf192f2c508998eb99 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graphstat.py @@ -0,0 +1,70 @@ +import unittest + +from altgraph import GraphStat +from altgraph import Graph +import sys + +class TestDegreesDist (unittest.TestCase): + + def test_simple(self): + a = Graph.Graph() + self.assertEqual(GraphStat.degree_dist(a), []) + + a.add_node(1) + a.add_node(2) + a.add_node(3) + + self.assertEqual(GraphStat.degree_dist(a), GraphStat._binning([0, 0, 0])) + + for x in range(100): + a.add_node(x) + + for x in range(1, 100): + for y in range(1, 50): + if x % y == 0: + a.add_edge(x, y) + + counts_inc = [] + counts_out = [] + for n in a: + counts_inc.append(a.inc_degree(n)) + counts_out.append(a.out_degree(n)) + + self.assertEqual(GraphStat.degree_dist(a), GraphStat._binning(counts_out)) + self.assertEqual(GraphStat.degree_dist(a, mode='inc'), GraphStat._binning(counts_inc)) + +class TestBinning (unittest.TestCase): + def test_simple(self): + + # Binning [0, 100) into 10 bins + a = list(range(100)) + out = GraphStat._binning(a, limits=(0, 100), bin_num=10) + + self.assertEqual(out, + [ (x*1.0, 10) for x in range(5, 100, 10) ]) + + + # Check that outliers are ignored. + a = list(range(100)) + out = GraphStat._binning(a, limits=(0, 90), bin_num=9) + + self.assertEqual(out, + [ (x*1.0, 10) for x in range(5, 90, 10) ]) + + + out = GraphStat._binning(a, limits=(0, 100), bin_num=15) + binSize = 100 / 15.0 + result = [0]*15 + for i in range(100): + bin = int(i/binSize) + try: + result[bin] += 1 + except IndexError: + pass + + result = [ (i * binSize + binSize/2, result[i]) for i in range(len(result))] + + self.assertEqual(result, out) + +if __name__ == "__main__": # pragma: no cover + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graphutil.py b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graphutil.py new file mode 100644 index 0000000000000000000000000000000000000000..2088aa15408da074b730e9614ee62a14e2a278a7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_graphutil.py @@ -0,0 +1,140 @@ +import unittest +from altgraph import GraphUtil +from altgraph import Graph, GraphError + +class TestGraphUtil (unittest.TestCase): + + def test_generate_random(self): + g = GraphUtil.generate_random_graph(10, 50) + self.assertEqual(g.number_of_nodes(), 10) + self.assertEqual(g.number_of_edges(), 50) + + seen = set() + + for e in g.edge_list(): + h, t = g.edge_by_id(e) + self.assertFalse(h == t) + self.assertTrue((h, t) not in seen) + seen.add((h, t)) + + g = GraphUtil.generate_random_graph(5, 30, multi_edges=True) + self.assertEqual(g.number_of_nodes(), 5) + self.assertEqual(g.number_of_edges(), 30) + + seen = set() + + for e in g.edge_list(): + h, t = g.edge_by_id(e) + self.assertFalse(h == t) + if (h, t) in seen: + break + seen.add((h, t)) + + else: + self.fail("no duplicates?") + + g = GraphUtil.generate_random_graph(5, 21, self_loops=True) + self.assertEqual(g.number_of_nodes(), 5) + self.assertEqual(g.number_of_edges(), 21) + + seen = set() + + for e in g.edge_list(): + h, t = g.edge_by_id(e) + self.assertFalse((h, t) in seen) + if h == t: + break + seen.add((h, t)) + + else: + self.fail("no self loops?") + + self.assertRaises(GraphError, GraphUtil.generate_random_graph, 5, 21) + g = GraphUtil.generate_random_graph(5, 21, True) + self.assertRaises(GraphError, GraphUtil.generate_random_graph, 5, 26, True) + + def test_generate_scale_free(self): + graph = GraphUtil.generate_scale_free_graph(50, 10) + self.assertEqual(graph.number_of_nodes(), 500) + + counts = {} + for node in graph: + degree = graph.inc_degree(node) + try: + counts[degree] += 1 + except KeyError: + counts[degree] = 1 + + total_counts = sum(counts.values()) + P = {} + for degree, count in counts.items(): + P[degree] = count * 1.0 / total_counts + + # XXX: use algoritm + # to check if P[degree] ~ degree ** G (for some G) + + #print sorted(P.items()) + + #print sorted([(count, degree) for degree, count in counts.items()]) + + #self.fail("missing tests for GraphUtil.generate_scale_free_graph") + + def test_filter_stack(self): + g = Graph.Graph() + g.add_node("1", "N.1") + g.add_node("1.1", "N.1.1") + g.add_node("1.1.1", "N.1.1.1") + g.add_node("1.1.2", "N.1.1.2") + g.add_node("1.1.3", "N.1.1.3") + g.add_node("1.1.1.1", "N.1.1.1.1") + g.add_node("1.1.1.2", "N.1.1.1.2") + g.add_node("1.1.2.1", "N.1.1.2.1") + g.add_node("1.1.2.2", "N.1.1.2.2") + g.add_node("1.1.2.3", "N.1.1.2.3") + g.add_node("2", "N.2") + + g.add_edge("1", "1.1") + g.add_edge("1.1", "1.1.1") + g.add_edge("1.1", "1.1.2") + g.add_edge("1.1", "1.1.3") + g.add_edge("1.1.1", "1.1.1.1") + g.add_edge("1.1.1", "1.1.1.2") + g.add_edge("1.1.2", "1.1.2.1") + g.add_edge("1.1.2", "1.1.2.2") + g.add_edge("1.1.2", "1.1.2.3") + + v, r, o = GraphUtil.filter_stack(g, "1", [ + lambda n: n != "N.1.1.1", lambda n: n != "N.1.1.2.3" ]) + + self.assertEqual(v, + set(["1", "1.1", "1.1.1", "1.1.2", "1.1.3", + "1.1.1.1", "1.1.1.2", "1.1.2.1", "1.1.2.2", + "1.1.2.3"])) + self.assertEqual(r, set([ + "1.1.1", "1.1.2.3"])) + + o.sort() + self.assertEqual(o, + [ + ("1.1", "1.1.1.1"), + ("1.1", "1.1.1.2") + ]) + + v, r, o = GraphUtil.filter_stack(g, "1", [ + lambda n: n != "N.1.1.1", lambda n: n != "N.1.1.1.2" ]) + + self.assertEqual(v, + set(["1", "1.1", "1.1.1", "1.1.2", "1.1.3", + "1.1.1.1", "1.1.1.2", "1.1.2.1", "1.1.2.2", + "1.1.2.3"])) + self.assertEqual(r, set([ + "1.1.1", "1.1.1.2"])) + + self.assertEqual(o, + [ + ("1.1", "1.1.1.1"), + ]) + + +if __name__ == "__main__": # pragma: no cover + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_object_graph.py b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_object_graph.py new file mode 100644 index 0000000000000000000000000000000000000000..55efa3c969438489b3eb38c61f17488df1a34ad2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_altgraph/test_object_graph.py @@ -0,0 +1,349 @@ +import unittest +import sys +from altgraph.ObjectGraph import ObjectGraph +from altgraph.Graph import Graph + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + + +class Node (object): + def __init__(self, graphident): + self.graphident = graphident + +class SubNode (Node): + pass + +class ArgNode (object): + def __init__(self, graphident, *args, **kwds): + self.graphident = graphident + self.args = args + self.kwds = kwds + + def __repr__(self): + return ''%(self.graphident,) + +class TestObjectGraph (unittest.TestCase): + + def test_constructor(self): + graph = ObjectGraph() + self.assertTrue(isinstance(graph, ObjectGraph)) + + g = Graph() + graph = ObjectGraph(g) + self.assertTrue(graph.graph is g) + self.assertEqual(graph.debug, 0) + self.assertEqual(graph.indent, 0) + + graph = ObjectGraph(debug=5) + self.assertEqual(graph.debug, 5) + + def test_repr(self): + graph = ObjectGraph() + self.assertEqual(repr(graph), '') + + + def testNodes(self): + graph = ObjectGraph() + n1 = Node("n1") + n2 = Node("n2") + n3 = Node("n3") + n4 = Node("n4") + + n1b = Node("n1") + + self.assertTrue(graph.getIdent(graph) is graph) + self.assertTrue(graph.getRawIdent(graph) is graph) + + graph.addNode(n1) + graph.addNode(n2) + graph.addNode(n3) + + self.assertTrue(n1 in graph) + self.assertFalse(n4 in graph) + self.assertTrue("n1" in graph) + self.assertFalse("n4" in graph) + + self.assertTrue(graph.findNode(n1) is n1) + self.assertTrue(graph.findNode(n1b) is n1) + self.assertTrue(graph.findNode(n2) is n2) + self.assertTrue(graph.findNode(n4) is None) + self.assertTrue(graph.findNode("n1") is n1) + self.assertTrue(graph.findNode("n2") is n2) + self.assertTrue(graph.findNode("n4") is None) + + self.assertEqual(graph.getRawIdent(n1), "n1") + self.assertEqual(graph.getRawIdent(n1b), "n1") + self.assertEqual(graph.getRawIdent(n4), "n4") + self.assertEqual(graph.getRawIdent("n1"), None) + + self.assertEqual(graph.getIdent(n1), "n1") + self.assertEqual(graph.getIdent(n1b), "n1") + self.assertEqual(graph.getIdent(n4), "n4") + self.assertEqual(graph.getIdent("n1"), "n1") + + self.assertTrue(n3 in graph) + graph.removeNode(n3) + self.assertTrue(n3 not in graph) + graph.addNode(n3) + self.assertTrue(n3 in graph) + + n = graph.createNode(SubNode, "n1") + self.assertTrue(n is n1) + + n = graph.createNode(SubNode, "n8") + self.assertTrue(isinstance(n, SubNode)) + self.assertTrue(n in graph) + self.assertTrue(graph.findNode("n8") is n) + + n = graph.createNode(ArgNode, "args", 1, 2, 3, a='a', b='b') + self.assertTrue(isinstance(n, ArgNode)) + self.assertTrue(n in graph) + self.assertTrue(graph.findNode("args") is n) + self.assertEqual(n.args, (1, 2, 3)) + self.assertEqual(n.kwds, {'a':'a', 'b':'b'}) + + def testEdges(self): + graph = ObjectGraph() + n1 = graph.createNode(ArgNode, "n1", 1) + n2 = graph.createNode(ArgNode, "n2", 1) + n3 = graph.createNode(ArgNode, "n3", 1) + n4 = graph.createNode(ArgNode, "n4", 1) + + graph.createReference(n1, n2, "n1-n2") + graph.createReference("n1", "n3", "n1-n3") + graph.createReference("n2", n3) + + g = graph.graph + e = g.edge_by_node("n1", "n2") + self.assertTrue(e is not None) + self.assertEqual(g.edge_data(e), "n1-n2") + + e = g.edge_by_node("n1", "n3") + self.assertTrue(e is not None) + self.assertEqual(g.edge_data(e), "n1-n3") + + e = g.edge_by_node("n2", "n3") + self.assertTrue(e is not None) + self.assertEqual(g.edge_data(e), None) + + e = g.edge_by_node("n1", "n4") + self.assertTrue(e is None) + + graph.removeReference(n1, n2) + e = g.edge_by_node("n1", "n2") + self.assertTrue(e is None) + + graph.removeReference("n1", "n3") + e = g.edge_by_node("n1", "n3") + self.assertTrue(e is None) + + graph.createReference(n1, n2, "foo") + e = g.edge_by_node("n1", "n2") + self.assertTrue(e is not None) + self.assertEqual(g.edge_data(e), "foo") + + + def test_flatten(self): + graph = ObjectGraph() + n1 = graph.createNode(ArgNode, "n1", 1) + n2 = graph.createNode(ArgNode, "n2", 2) + n3 = graph.createNode(ArgNode, "n3", 3) + n4 = graph.createNode(ArgNode, "n4", 4) + n5 = graph.createNode(ArgNode, "n5", 5) + n6 = graph.createNode(ArgNode, "n6", 6) + n7 = graph.createNode(ArgNode, "n7", 7) + n8 = graph.createNode(ArgNode, "n8", 8) + + graph.createReference(graph, n1) + graph.createReference(graph, n7) + graph.createReference(n1, n2) + graph.createReference(n1, n4) + graph.createReference(n2, n3) + graph.createReference(n2, n5) + graph.createReference(n5, n6) + graph.createReference(n4, n6) + graph.createReference(n4, n2) + + self.assertFalse(isinstance(graph.flatten(), list)) + + fl = list(graph.flatten()) + self.assertTrue(n1 in fl) + self.assertTrue(n2 in fl) + self.assertTrue(n3 in fl) + self.assertTrue(n4 in fl) + self.assertTrue(n5 in fl) + self.assertTrue(n6 in fl) + self.assertTrue(n7 in fl) + self.assertFalse(n8 in fl) + + fl = list(graph.flatten(start=n2)) + self.assertFalse(n1 in fl) + self.assertTrue(n2 in fl) + self.assertTrue(n3 in fl) + self.assertFalse(n4 in fl) + self.assertTrue(n5 in fl) + self.assertTrue(n6 in fl) + self.assertFalse(n7 in fl) + self.assertFalse(n8 in fl) + + graph.createReference(n1, n5) + fl = list(graph.flatten(lambda n: n.args[0] % 2 != 0)) + self.assertTrue(n1 in fl) + self.assertFalse(n2 in fl) + self.assertFalse(n3 in fl) + self.assertFalse(n4 in fl) + self.assertTrue(n5 in fl) + self.assertFalse(n6 in fl) + self.assertTrue(n7 in fl) + self.assertFalse(n8 in fl) + + def test_iter_nodes(self): + graph = ObjectGraph() + n1 = graph.createNode(ArgNode, "n1", 1) + n2 = graph.createNode(ArgNode, "n2", 2) + n3 = graph.createNode(ArgNode, "n3", 3) + n4 = graph.createNode(ArgNode, "n4", 4) + n5 = graph.createNode(ArgNode, "n5", 5) + n6 = graph.createNode(ArgNode, "n6", 5) + + nodes = graph.nodes() + if sys.version[0] == '2': + self.assertTrue(hasattr(nodes, 'next')) + else: + self.assertTrue(hasattr(nodes, '__next__')) + self.assertTrue(hasattr(nodes, '__iter__')) + + nodes = list(nodes) + self.assertEqual(len(nodes), 6) + self.assertTrue(n1 in nodes) + self.assertTrue(n2 in nodes) + self.assertTrue(n3 in nodes) + self.assertTrue(n4 in nodes) + self.assertTrue(n5 in nodes) + self.assertTrue(n6 in nodes) + + def test_get_edges(self): + graph = ObjectGraph() + n1 = graph.createNode(ArgNode, "n1", 1) + n2 = graph.createNode(ArgNode, "n2", 2) + n3 = graph.createNode(ArgNode, "n3", 3) + n4 = graph.createNode(ArgNode, "n4", 4) + n5 = graph.createNode(ArgNode, "n5", 5) + n6 = graph.createNode(ArgNode, "n6", 5) + + graph.createReference(n1, n2) + graph.createReference(n1, n3) + graph.createReference(n3, n1) + graph.createReference(n5, n1) + graph.createReference(n2, n4) + graph.createReference(n2, n5) + graph.createReference(n6, n2) + + outs, ins = graph.get_edges(n1) + + self.assertFalse(isinstance(outs, list)) + self.assertFalse(isinstance(ins, list)) + + ins = list(ins) + outs = list(outs) + + + self.assertTrue(n1 not in outs) + self.assertTrue(n2 in outs) + self.assertTrue(n3 in outs) + self.assertTrue(n4 not in outs) + self.assertTrue(n5 not in outs) + self.assertTrue(n6 not in outs) + + self.assertTrue(n1 not in ins) + self.assertTrue(n2 not in ins) + self.assertTrue(n3 in ins) + self.assertTrue(n4 not in ins) + self.assertTrue(n5 in ins) + self.assertTrue(n6 not in ins) + + def test_filterStack(self): + graph = ObjectGraph() + n1 = graph.createNode(ArgNode, "n1", 0) + n11 = graph.createNode(ArgNode, "n1.1", 1) + n12 = graph.createNode(ArgNode, "n1.2", 0) + n111 = graph.createNode(ArgNode, "n1.1.1", 0) + n112 = graph.createNode(ArgNode, "n1.1.2",2) + n2 = graph.createNode(ArgNode, "n2", 0) + n3 = graph.createNode(ArgNode, "n2", 0) + + graph.createReference(None, n1) + graph.createReference(None, n2) + graph.createReference(n1, n11) + graph.createReference(n1, n12) + graph.createReference(n11, n111) + graph.createReference(n11, n112) + + self.assertTrue(n1 in graph) + self.assertTrue(n2 in graph) + self.assertTrue(n11 in graph) + self.assertTrue(n12 in graph) + self.assertTrue(n111 in graph) + self.assertTrue(n112 in graph) + self.assertTrue(n2 in graph) + self.assertTrue(n3 in graph) + + visited, removes, orphans = graph.filterStack( + [lambda n: n.args[0] != 1, lambda n: n.args[0] != 2]) + + self.assertEqual(visited, 6) + self.assertEqual(removes, 2) + self.assertEqual(orphans, 1) + + e = graph.graph.edge_by_node(n1.graphident, n111.graphident) + self.assertEqual(graph.graph.edge_data(e), "orphan") + + self.assertTrue(n1 in graph) + self.assertTrue(n2 in graph) + self.assertTrue(n11 not in graph) + self.assertTrue(n12 in graph) + self.assertTrue(n111 in graph) + self.assertTrue(n112 not in graph) + self.assertTrue(n2 in graph) + self.assertTrue(n3 in graph) + + +class TestObjectGraphIO (unittest.TestCase): + def setUp(self): + self._stdout = sys.stdout + + def tearDown(self): + sys.stdout = self._stdout + + def test_msg(self): + graph = ObjectGraph() + + sys.stdout = fp = StringIO() + graph.msg(0, "foo") + self.assertEqual(fp.getvalue(), "foo \n") + + sys.stdout = fp = StringIO() + graph.msg(5, "foo") + self.assertEqual(fp.getvalue(), "") + + sys.stdout = fp = StringIO() + graph.debug = 10 + graph.msg(5, "foo") + self.assertEqual(fp.getvalue(), "foo \n") + + sys.stdout = fp = StringIO() + graph.msg(0, "foo", 1, "a") + self.assertEqual(fp.getvalue(), "foo 1 'a'\n") + + sys.stdout = fp = StringIO() + graph.msgin(0, "hello", "world") + graph.msg(0, "test me") + graph.msgout(0, "bye bye") + self.assertEqual(fp.getvalue(), "hello 'world'\n test me \nbye bye \n") + + +if __name__ == "__main__": # pragma: no cover + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_building_utils.py b/3rdparty/pyinstaller-4.3/tests/unit/test_building_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..d88231a7149478fd844fb08d818d8ec301b5ffb4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_building_utils.py @@ -0,0 +1,95 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 pytest +import os +from importlib.machinery import EXTENSION_SUFFIXES + +from PyInstaller.building import utils + +def test_format_binaries_and_datas_not_found_raises_error(tmpdir): + datas = [('non-existing.txt', '.')] + tmpdir.join('existing.txt').ensure() + # TODO Tighten test when introducing PyInstaller.exceptions + with pytest.raises(SystemExit) as context: + utils.format_binaries_and_datas(datas, str(tmpdir)) + + +def test_format_binaries_and_datas_1(tmpdir): + + def _(path): return os.path.join(*path.split('/')) + + datas = [(_('existing.txt'), '.'), + (_('other.txt'), 'foo'), + (_('*.log'), 'logs'), + (_('a/*.log'), 'lll'), + (_('a/here.tex'), '.'), + (_('b/[abc].tex'), 'tex')] + + expected = set() + for dest, src in ( + ('existing.txt', 'existing.txt'), + ('foo/other.txt', 'other.txt'), + ('logs/aaa.log', 'aaa.log'), + ('logs/bbb.log', 'bbb.log'), + ('lll/xxx.log', 'a/xxx.log'), + ('lll/yyy.log', 'a/yyy.log'), + ('here.tex', 'a/here.tex'), + ('tex/a.tex', 'b/a.tex'), + ('tex/b.tex', 'b/b.tex'), + ): + src = tmpdir.join(_(src)).ensure() + expected.add((_(dest), str(src))) + + # add some files which are not included + tmpdir.join(_('not.txt')).ensure() + tmpdir.join(_('a/not.txt')).ensure() + tmpdir.join(_('b/not.txt')).ensure() + + res = utils.format_binaries_and_datas(datas, str(tmpdir)) + assert res == expected + + +def test_format_binaries_and_datas_with_bracket(tmpdir): + # See issue #2314: the filename contains brackets which are + # interpreted by glob(). + + def _(path): return os.path.join(*path.split('/')) + + datas = [(_('b/[abc].tex'), 'tex')] + + expected = set() + for dest, src in ( + ('tex/[abc].tex', 'b/[abc].tex'), + ): + src = tmpdir.join(_(src)).ensure() + expected.add((_(dest), str(src))) + + # add some files which are not included + tmpdir.join(_('tex/not.txt')).ensure() + + res = utils.format_binaries_and_datas(datas, str(tmpdir)) + assert res == expected + + +def test_add_to_suffix__extension(): + toc = [ + ('mypkg', + 'lib38/site-packages/mypkg' + EXTENSION_SUFFIXES[0], + 'EXTENSION'), + ] + toc = utils.add_suffix_to_extensions(toc) + assert toc == [ + ('mypkg' + EXTENSION_SUFFIXES[0], + 'lib38/site-packages/mypkg' + EXTENSION_SUFFIXES[0], + 'EXTENSION'), + ] diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_compat.py b/3rdparty/pyinstaller-4.3/tests/unit/test_compat.py new file mode 100644 index 0000000000000000000000000000000000000000..ba6ebb1c9ab92357f17e21935476b2aa4c053afc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_compat.py @@ -0,0 +1,28 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 PyInstaller.utils.tests import skipif + +import pytest + +from PyInstaller import compat + + +def test_exec_command_subprocess_wrong_encoding_reports_nicely(capsys): + # Ensure a nice error message is printed if decoding the output of the + # subprocess fails. + # Actually `exec_python()` is used for running the progam, so we can use a + # small Python script. + prog = ("""import sys; sys.stdout.buffer.write(b'dfadfadf\\xa0:::::')""") + with pytest.raises(UnicodeDecodeError): + res = compat.exec_python('-c', prog) + out, err = capsys.readouterr() + assert 'bytes around the offending' in err diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_depend_utils.py b/3rdparty/pyinstaller-4.3/tests/unit/test_depend_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..cc48e6e9dd4a0132fb9a8dd297c8e0f17e7b6719 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_depend_utils.py @@ -0,0 +1,95 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 pytest +import textwrap + +from PyInstaller.depend import utils +from PyInstaller.compat import is_unix, is_win + + +CTYPES_CLASSNAMES = ( + 'CDLL', 'ctypes.CDLL', + 'WinDLL', 'ctypes.WinDLL', + 'OleDLL', 'ctypes.OleDLL', + 'PyDLL', 'ctypes.PyDLL') + + +def __scan_code_for_ctypes(code, monkeypatch): + # _resolveCtypesImports would filter our some of our names + monkeypatch.setattr(utils, '_resolveCtypesImports', + lambda cbinaries: cbinaries) + code = textwrap.dedent(code) + co = compile(code, 'dummy', 'exec') + #import pdb ; pdb.set_trace() + return utils.scan_code_for_ctypes(co) + + +@pytest.mark.parametrize('classname', CTYPES_CLASSNAMES) +def test_ctypes_CDLL_call(monkeypatch, classname): + code = "%s('somelib.xxx')" % classname + res = __scan_code_for_ctypes(code, monkeypatch) + assert res == set(['somelib.xxx']) + + +@pytest.mark.parametrize('classname', CTYPES_CLASSNAMES) +def test_ctypes_LibraryLoader(monkeypatch, classname): + # This type of useage is only valif on Windows and the lib-name will + # always get `.dll` appended. + code = "%s.somelib" % classname.lower() + res = __scan_code_for_ctypes(code, monkeypatch) + assert res == set(['somelib.dll']) + + +@pytest.mark.parametrize('classname', CTYPES_CLASSNAMES) +def test_ctypes_LibraryLoader_LoadLibrary(monkeypatch, classname): + code = "%s.LoadLibrary('somelib.xxx')" % classname.lower() + res = __scan_code_for_ctypes(code, monkeypatch) + assert res == set(['somelib.xxx']) + + +def test_ctypes_util_find_library(monkeypatch): + # for lind_library() we need a lib actually existing on the system + if is_win: + libname = "KERNEL32" + else: + libname = "c" + code = "ctypes.util.find_library('%s')" % libname + res = __scan_code_for_ctypes(code, monkeypatch) + assert res + + +def test_ctypes_util_find_library_as_default_argument(): + # Test-case for fix: + # commit 55b542f135340c612a861cfcce0f86c4e5a968df + # Author: Hartmut Goebel + # Date: Thu Nov 19 14:45:30 2015 +0100 + code = """ + def locate_library(loader=ctypes.util.find_library): + pass + """ + code = textwrap.dedent(code) + co = compile(code, '', 'exec') + utils.scan_code_for_ctypes(co) + + +@pytest.mark.linux +def test_ldconfig_cache(): + utils.load_ldconfig_cache() + libpath = None + for soname in utils.LDCONFIG_CACHE: + if soname.startswith('libc.so.'): + libpath = utils.LDCONFIG_CACHE[soname] + break + assert libpath, 'libc.so not found' + assert os.path.isfile(libpath) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_hook_order.py b/3rdparty/pyinstaller-4.3/tests/unit/test_hook_order.py new file mode 100644 index 0000000000000000000000000000000000000000..b38b191bc3a458b0f72f6370d7e141105a45ce56 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_hook_order.py @@ -0,0 +1,46 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 sys +import subprocess +import atexit +from PyInstaller.utils.tests import skip + + +@skip +def test_hook_order(pyi_builder): + + subprocess.run( + [ + sys.executable, '-m', 'pip', 'install', '-e', + os.path.join(os.path.dirname(__file__), 'hook_order_hooks') + ] + ) + + atexit.register(lambda: subprocess.run( + [ + sys.executable, '-m', 'pip', 'uninstall', 'pyi_example_package', + '--yes', '-q', '-q', '-q' + ] + )) + + pyi_builder.test_source( + ''' + try: + import pyi_example_package + except: + pass + ''', + pyi_args=[ + '--additional-hooks-dir={}'.format( + os.path.join(os.path.dirname(__file__), 'hook_order_hooks') + ) + ]) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_hookutils.py b/3rdparty/pyinstaller-4.3/tests/unit/test_hookutils.py new file mode 100644 index 0000000000000000000000000000000000000000..c75d524351fb5cad464039c3ac4307787f11adfc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_hookutils.py @@ -0,0 +1,324 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 pytest +import shutil +from os.path import join + +from PyInstaller.utils.hooks import collect_data_files, collect_submodules, \ + get_module_file_attribute, remove_prefix, remove_suffix, \ + remove_file_extension, is_module_or_submodule, \ + is_module_satisfies +from PyInstaller.compat import exec_python, ALL_SUFFIXES, is_win + + +class TestRemovePrefix(object): + # Verify that removing a prefix from an empty string is OK. + def test_empty_string(self): + assert '' == remove_prefix('', 'prefix') + + # An empty prefix should pass the string through unmodified. + def test_emptystr_unmodif(self): + assert 'test' == remove_prefix('test', '') + + # If the string is the prefix, it should be empty at exit. + def test_string_prefix(self): + assert '' == remove_prefix('test', 'test') + + # Just the prefix should be removed. + def test_just_prefix(self): + assert 'ing' == remove_prefix('testing', 'test') + + # A matching string not as prefix should produce no modifications + def test_no_modific(self): + assert 'atest' == remove_prefix('atest', 'test') + + +class TestRemoveSuffix(object): + # Verify that removing a suffix from an empty string is OK. + def test_empty_string(self): + assert '' == remove_suffix('', 'suffix') + + # An empty suffix should pass the string through unmodified. + def test_emptystr_unmodif(self): + assert 'test' == remove_suffix('test', '') + + # If the string is the suffix, it should be empty at exit. + def test_string_suffix(self): + assert '' == remove_suffix('test', 'test') + + # Just the suffix should be removed. + def test_just_suffix(self): + assert 'test' == remove_suffix('testing', 'ing') + + # A matching string not as suffix should produce no modifications + def test_no_modific(self): + assert 'testa' == remove_suffix('testa', 'test') + + +class TestRemoveExtension(object): + # Removing a suffix from a filename with no extension returns the filename. + def test_no_extension(self): + assert 'file' == remove_file_extension('file') + + # A filename with two extensions should have only the first removed. + def test_two_extensions(self): + assert 'file.1' == remove_file_extension('file.1.2') + + # Standard case - remove an extension + def test_remove_ext(self): + assert 'file' == remove_file_extension('file.1') + + # Unix-style .files are not treated as extensions + def test_unixstyle_not_ext(self): + assert '.file' == remove_file_extension('.file') + + # Unix-style .file.ext works + def test_unixstyle_ext(self): + assert '.file' == remove_file_extension('.file.1') + + # Unix-style .file.ext works + def test_unixstyle_path(self): + assert '/a/b/c' == remove_file_extension('/a/b/c') + assert '/a/b/c' == remove_file_extension('/a/b/c.1') + + # Windows-style .file.ext works + def test_win32style_path(self): + assert 'C:\\a\\b\\c' == remove_file_extension('C:\\a\\b\\c') + assert 'C:\\a\\b\\c' == remove_file_extension('C:\\a\\b\\c.1') + + +# The name of the hookutils test files directory +TEST_MOD = 'hookutils_package' +# The path to this directory. +TEST_MOD_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'hookutils_files') + + +@pytest.fixture +def mod_list(monkeypatch): + # Add 'hookutils_files' to sys.path (so ``is_package`` can find it) and to + # ``pathex`` (so code run in a subprocess can find it). + monkeypatch.setattr('PyInstaller.config.CONF', {'pathex': [TEST_MOD_PATH]}) + monkeypatch.syspath_prepend(TEST_MOD_PATH) + # Use the hookutils_test_files package for testing. + return collect_submodules(TEST_MOD) + +class TestCollectSubmodules(object): + # An error should be thrown if a module, not a package, was passed. + def test_collect_submod_module(self): + # os is a module, not a package. + with pytest.raises(TypeError): + collect_submodules(__import__('os')) + + # The package name itself should be in the returned list. + def test_collect_submod_itself(self, mod_list): + assert TEST_MOD in mod_list + + # Python extension is included in the list. + def test_collect_submod_pyextension(self, mod_list): + assert TEST_MOD + '.pyextension' in mod_list + + # Check that all packages get included + def test_collect_submod_all_included(self, mod_list): + mod_list.sort() + assert mod_list == [TEST_MOD, + # Python extensions on Windows ends with '.pyd' and + # '.so' on Linux, Mac OS X and other operating systems. + TEST_MOD + '.pyextension', + TEST_MOD + '.subpkg', + TEST_MOD + '.subpkg.twelve', + TEST_MOD + '.two'] + + # Dynamic libraries (.dll, .dylib) are not included in the list. + def test_collect_submod_no_dynamiclib(self, mod_list): + assert TEST_MOD + '.dynamiclib' not in mod_list + + # Subpackages without an __init__.py should not be included. + def test_collect_submod_subpkg_init(self, mod_list): + assert TEST_MOD + '.py_files_not_in_package.sub_pkg.three' not in mod_list + + # Test with a subpackage. + def test_collect_submod_subpkg(self, mod_list): + # Note: Even though mod_list is overwritten, it's still needed as a + # fixture, so that the path to the TEST_MOD will be set correctly. + mod_list = collect_submodules(TEST_MOD + '.subpkg') + mod_list.sort() + assert mod_list == [TEST_MOD + '.subpkg', + TEST_MOD + '.subpkg.twelve'] + + # Test in an ``.egg`` file. + def test_collect_submod_egg(self, tmpdir, monkeypatch): + # Copy files to a tmpdir for egg building. + dest_path = tmpdir.join('hookutils_package') + shutil.copytree(TEST_MOD_PATH, dest_path.strpath) + monkeypatch.chdir(dest_path) + + # Create an egg from the test package. For debug, show the output of + # the egg build. + print(exec_python('setup.py', 'bdist_egg')) + + # Obtain the name of the egg, which depends on the Python version. + dist_path = dest_path.join('dist') + fl = os.listdir(dist_path.strpath) + assert len(fl) == 1 + egg_name = fl[0] + assert egg_name.endswith('.egg') + + # Add the egg to Python's path. + pth = dist_path.join(egg_name).strpath + monkeypatch.setattr('PyInstaller.config.CONF', {'pathex': [pth]}) + monkeypatch.syspath_prepend(pth) + + # Verify its contents. + ml = collect_submodules(TEST_MOD) + self.test_collect_submod_all_included(ml) + + # Messages printed to stdout by modules during collect_submodules() + # should not affect the collected modules list. + def test_collect_submod_stdout_interference(self, monkeypatch): + TEST_MOD = 'foo' + TEST_MOD_PATH = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'hookutils_files2' + ) + + monkeypatch.setattr('PyInstaller.config.CONF', + {'pathex': [TEST_MOD_PATH]}) + monkeypatch.syspath_prepend(TEST_MOD_PATH) + + ml = collect_submodules(TEST_MOD) + ml = sorted(ml) + + assert ml == ['foo', 'foo.bar'] + + +def test_is_module_or_submodule(): + assert is_module_or_submodule('foo.bar', 'foo.bar') + assert is_module_or_submodule('foo.bar.baz', 'foo.bar') + assert not is_module_or_submodule('foo.bard', 'foo.bar') + assert not is_module_or_submodule('foo', 'foo.bar') + + +def test_is_module_satisfies_package_not_installed(): + assert is_module_satisfies('pytest') + assert not is_module_satisfies('magnumopus-no-package-test-case') + + +# An error should be thrown if a module, not a package, was passed. +def test_collect_data_module(): + # 'os' is a module, not a package. + with pytest.raises(TypeError): + collect_data_files(__import__('os')) + + +# This fixtures runs ``collect_data_files`` through the test cases in +# ``_DATA_PARAMS``. +@pytest.fixture( + params=[ + # This is used to invoke ``collect_data_files(*args, **kwargs)``, then + # provide the expected results to verify correctness. The order is: + ## args, kwargs, expected_results_sequence + ([TEST_MOD], {}, ('dynamiclib.dll', + 'dynamiclib.dylib', + 'nine.dat', + join('py_files_not_in_package', 'data', 'eleven.dat'), + join('py_files_not_in_package', 'ten.dat'), + # Not backwards! On Windows, ``.so`` files are + # just data and vice versa. + 'pyextension.so' if is_win else 'pyextension.pyd', + join('subpkg', 'thirteen.txt'), + )), + # Test collecting from a subpackage. + ([TEST_MOD + '.subpkg'], {}, ( + join('subpkg', 'thirteen.txt'), + )), + ([TEST_MOD], dict(include_py_files=True, excludes=['**/__pycache__']), ( + '__init__.py', + 'dynamiclib.dll', + 'dynamiclib.dylib', + 'nine.dat', + join('py_files_not_in_package', 'data', 'eleven.dat'), + join('py_files_not_in_package', 'one.py'), + join('py_files_not_in_package', 'sub_pkg', '__init__.py'), + join('py_files_not_in_package', 'sub_pkg', 'three.py'), + join('py_files_not_in_package', 'ten.dat'), + 'pyextension.pyd', + 'pyextension.so', + join('subpkg', '__init__.py'), + join('subpkg', 'thirteen.txt'), + join('subpkg', 'twelve.py'), + 'two.py', + )), + ([TEST_MOD], dict(excludes=['py_files_not_in_package', + '**/__pycache__']), ( + 'dynamiclib.dll', + 'dynamiclib.dylib', + 'nine.dat', + 'pyextension.so' if is_win else 'pyextension.pyd', + join('subpkg', 'thirteen.txt'), + )), + ([TEST_MOD], dict(includes=['**/*.dat', '**/*.txt']), ( + 'nine.dat', + join('py_files_not_in_package', 'data', 'eleven.dat'), + join('py_files_not_in_package', 'ten.dat'), + join('subpkg', 'thirteen.txt'), + )), + ([TEST_MOD], dict(includes=['*.dat']), ( + 'nine.dat', + )), + ([TEST_MOD], dict(subdir="py_files_not_in_package", + excludes=['**/__pycache__']), ( + join('py_files_not_in_package', 'data', 'eleven.dat'), + join('py_files_not_in_package', 'ten.dat'), + )), + ], + ids=['package', 'subpackage', 'package with py files', 'excludes', + '** includes', 'includes', 'subdir'] +) +def data_lists(monkeypatch, request): + def _sort(sequence): + l = list(sequence) + l.sort() + return tuple(l) + # Add path with 'hookutils_files' module to ``sys.path`` so tests + # could find this module - useful for subprocesses. + monkeypatch.syspath_prepend(TEST_MOD_PATH) + # Use the hookutils_test_files package for testing. + args, kwargs, subfiles = request.param + data = collect_data_files(*args, **kwargs) + # Break list of (source, dest) into source and dest lists. + src = [item[0] for item in data] + dst = [item[1] for item in data] + + return subfiles, _sort(src), _sort(dst) + + +# Make sure the correct files are found. +def test_collect_data_all_included(data_lists): + subfiles, src, dst = data_lists + # Check the source and dest lists against the correct values in + # subfiles. + src_compare = tuple([join(TEST_MOD_PATH, TEST_MOD, subpath) + for subpath in subfiles]) + dst_compare = [os.path.dirname(join(TEST_MOD, subpath)) + for subpath in subfiles] + dst_compare.sort() + dst_compare = tuple(dst_compare) + assert src == src_compare + assert dst == dst_compare + + +# An Import error should be thrown if a module is not found. +def test_get_module_file_attribute_non_exist_module(): + with pytest.raises(ImportError): + get_module_file_attribute('pyinst_nonexisting_module_name') diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_loader.py b/3rdparty/pyinstaller-4.3/tests/unit/test_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..54c57a1ac5df193e2b3888782d53b920987994f5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_loader.py @@ -0,0 +1,62 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 threading import Thread +from queue import Queue + +from PyInstaller.loader.pyimod02_archive import ArchiveFile + + +def test_threading_import(tmpdir): + """ + On Python 3.3+, PyInstaller doesn't acquire a lock when performing an + import. Therefore, two thread could both be reading the .pyz archive at the + same time. At the core, the ArchiveFile class performs these reads. This + test verifies that multi-threaded reads work. + + For more information, see https://github.com/pyinstaller/pyinstaller/pull/2010. + """ + + # Create a temporary file and use the ArchiveReader on it. + tmp_file = tmpdir.join('test.txt') + tmp_file.write('Testing') + ar = ArchiveFile(tmp_file.strpath, 'r') + + # Use queues to synchronize threads. + q1 = Queue() + q2 = Queue() + + # This function, which is run in a separate thread, works to ensure that + # both threads open a file at the same time. + def foo(): + with ar: + # Wait until both threads have opened the file. + q1.put(1) + assert q2.get() == 2 + # Wait until the other thread has closed the file before closing it + # here. + assert q2.get() == 3 + + thread = Thread(target=foo) + thread.start() + + # This code works with ``foo`` above to open the same file from two threads. + with ar: + # Wait until both threads have opened the file. + q2.put(2) + assert q1.get() == 1 + # Make the other thread wait until this thread has closed the file. + q2.put(3) + + # Wait for the other thread to finish. + thread.join() + diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_makespec.py b/3rdparty/pyinstaller-4.3/tests/unit/test_makespec.py new file mode 100644 index 0000000000000000000000000000000000000000..f2a571e4931a848a0a0325ff823f83c0238e442b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_makespec.py @@ -0,0 +1,50 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 + +from PyInstaller.building import makespec + +def test_make_variable_path(): + p = os.path.join(makespec.HOMEPATH, "aaa", "bbb", "ccc") + assert (makespec.make_variable_path(p) == + ("HOMEPATH", os.path.join("aaa", "bbb", "ccc"))) + + +def test_make_variable_path_regression(): + p = os.path.join(makespec.HOMEPATH + "aaa", "bbb", "ccc") + assert makespec.make_variable_path(p) == (None, p) + + +def test_Path_constructor(): + p = makespec.Path("aaa", "bbb", "ccc") + assert p.path == os.path.join("aaa", "bbb", "ccc") + + +def test_Path_repr(): + p = makespec.Path(makespec.HOMEPATH, "aaa", "bbb", "ccc") + assert p.path == os.path.join(makespec.HOMEPATH, "aaa", "bbb", "ccc") + assert (repr(p) == + "os.path.join(HOMEPATH,%r)" % os.path.join("aaa", "bbb", "ccc")) + + +def test_Path_repr_relative(): + p = makespec.Path("aaa", "bbb", "ccc.py") + assert p.path == os.path.join("aaa", "bbb", "ccc.py") + assert repr(p) == "%r" % os.path.join("aaa", "bbb", "ccc.py") + + +def test_Path_regression(): + p = makespec.Path(makespec.HOMEPATH + "-aaa", "bbb", "ccc") + assert p.path == os.path.join(makespec.HOMEPATH + "-aaa", "bbb", "ccc") + assert (repr(p) == + repr(os.path.join(makespec.HOMEPATH + "-aaa", "bbb", "ccc"))) + diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_miscutils.py b/3rdparty/pyinstaller-4.3/tests/unit/test_miscutils.py new file mode 100644 index 0000000000000000000000000000000000000000..45a8b4fcd6b598ae891185b45bc214f66a0c43cb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_miscutils.py @@ -0,0 +1,50 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2020, 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 pytest + +from PyInstaller.utils.misc import load_py_data_struct, save_py_data_struct + + +@pytest.mark.win32 +def test_versioninfo(tmp_path): + from PyInstaller.utils.win32.versioninfo import VSVersionInfo, \ + FixedFileInfo, StringFileInfo, StringTable, StringStruct, \ + VarFileInfo, VarStruct + + vsinfo = VSVersionInfo( + ffi=FixedFileInfo( + filevers=(1, 2, 3, 4), + prodvers=(5, 6, 7, 8), + mask=0x3f, + flags=0x1, + OS=0x40004, + fileType=0x42, + subtype=0x42, + date=(0, 0) + ), + kids=[ + StringFileInfo( + [ + StringTable( + '040904b0', + [StringStruct('FileDescription', + 'versioninfo test')]) + ]), + VarFileInfo([VarStruct('Translation', [1033, 1200])]) + ] + ) + + file = str(tmp_path / 'versioninfo') + save_py_data_struct(file, vsinfo) + + assert vsinfo == load_py_data_struct(file) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..22b9679cb423776816d9576ece4d1f8fe265e083 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/__init__.py @@ -0,0 +1 @@ +""" modulegraph tests """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_basic.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_basic.py new file mode 100644 index 0000000000000000000000000000000000000000..34b22536185d72a8404550c2dd52485688d98043 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_basic.py @@ -0,0 +1,43 @@ +import unittest + +import os, shutil + +from PyInstaller.lib.modulegraph import modulegraph + +class DummyModule(object): + packagepath = None + def __init__(self, ppath): + self.packagepath = ppath + +class FindAllSubmodulesTestCase(unittest.TestCase): + def testNone(self): + mg = modulegraph.ModuleGraph() + # empty packagepath + m = DummyModule(None) + sub_ms = [] + for sm in mg._find_all_submodules(m): + sub_ms.append(sm) + self.assertEqual(sub_ms, []) + + def testSimple(self): + mg = modulegraph.ModuleGraph() + # a string does not break anything although it is split into its characters + # BUG: "/hi/there" will read "/" + m = DummyModule("xyz") + sub_ms = [] + for sm in mg._find_all_submodules(m): + sub_ms.append(sm) + self.assertEqual(sub_ms, []) + + def testSlashes(self): + # a string does not break anything although it is split into its characters + # BUG: "/xyz" will read "/" so this one already triggers missing itertools + mg = modulegraph.ModuleGraph() + m = DummyModule("/xyz") + sub_ms = [] + for sm in mg._find_all_submodules(m): + sub_ms.append(sm) + self.assertEqual(sub_ms, []) + +if __name__ == '__main__': + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_compiled_modules.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_compiled_modules.py new file mode 100644 index 0000000000000000000000000000000000000000..7e356455056ded8e899b6c5af3967a6195e72eb0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_compiled_modules.py @@ -0,0 +1,63 @@ +import unittest + +import os, py_compile, sys + +from PyInstaller.lib.modulegraph import modulegraph + +class CompiledModuleTests(unittest.TestCase): + def setUp(self): + self.base_dir = os.path.join( + os.path.dirname(__file__), + 'testpkg-compiled') + self.compiled_dir = os.path.join( + self.base_dir, 'compiled') + self.source_dir = os.path.join( + self.base_dir, 'source') + + for fn in os.listdir(self.source_dir): + if not fn.endswith('.py'): continue + + py_compile.compile( + os.path.join(self.source_dir, fn), + os.path.join(self.compiled_dir, fn + 'c')) + + def tearDown(self): + for fn in os.listdir(self.compiled_dir): + if fn.endswith('.pyc'): + os.unlink(os.path.join(self.compiled_dir, fn)) + + def testCompiledModules(self): + mf = modulegraph.ModuleGraph(path=[self.compiled_dir] + sys.path) + #self.mf.debug = 999 + mf.run_script(os.path.join(self.compiled_dir, 'script.py')) + + o = mf.findNode('mod1') + self.assertIsInstance(o, modulegraph.CompiledModule) + self.assertEqual(o._global_attr_names, { 'mod2', 'mod3', 'foo' }) + self.assertEqual(o._starimported_ignored_module_names, set()) + + o = mf.findNode('mod2') + self.assertIsInstance(o, modulegraph.CompiledModule) + self.assertEqual(o._global_attr_names, + { 'mod1', 'sys', 'testme', 'bar' }) + self.assertEqual(o._starimported_ignored_module_names, set()) + + o = mf.findNode('mod3') + self.assertIsInstance(o, modulegraph.CompiledModule) + self.assertEqual(o._global_attr_names, { 'os', 'path'}) + self.assertEqual(o._starimported_ignored_module_names, set()) + + o = mf.findNode('mod4') + other = mf.findNode('zipfile') + self.assertIsInstance(o, modulegraph.CompiledModule) + self.assertEqual(o._global_attr_names, other._global_attr_names) + self.assertEqual(o._starimported_ignored_module_names, {'math'}) + + o = mf.findNode('mod5') + self.assertIs(o, None) + + +if __name__ == '__main__': + #import profile + #profile.run('unittest.main()', sort=2) + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_edge_data.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_edge_data.py new file mode 100644 index 0000000000000000000000000000000000000000..7b1a4039734d425704feb5096c75b398ebc20c00 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_edge_data.py @@ -0,0 +1,422 @@ +import os +import sys +if sys.version_info[:2] <= (2,6): + import unittest2 as unittest +else: + import unittest + +from PyInstaller.lib.modulegraph import modulegraph + + +# XXX: Todo: simular tests with bytecompiled modules + + +class TestEdgeData (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def test_regular_import(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-edgedata') + mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + script_name = os.path.join(root, 'script.py') + mf.run_script(script_name) + + script_node = mf.findNode(script_name) + self.assertIsInstance(script_node, modulegraph.Script) + + + node = mf.findNode('toplevel_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False)) + + node = mf.findNode('toplevel_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False)) + + node = mf.findNode('toplevel_class_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False)) + + node = mf.findNode('toplevel_class_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False)) + + node = mf.findNode('toplevel_conditional_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=False)) + + node = mf.findNode('toplevel_conditional_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=False)) + + node = mf.findNode('toplevel_conditional_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('toplevel_conditional_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('toplevel_conditional_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('toplevel_conditional_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('toplevel_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('toplevel_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('toplevel_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('toplevel_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('function_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('function_class_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('function_class_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('function_conditional_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('function_conditional_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('function_conditional_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_conditional_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_conditional_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_conditional_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False)) + + node = mf.findNode('function_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=False)) + + + def test_multi_import(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-edgedata') + mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + script_name = os.path.join(root, 'script_multi_import.py') + mf.run_script(script_name) + + script_node = mf.findNode(script_name) + self.assertIsInstance(script_node, modulegraph.Script) + + # FIXME PyInstaller: original _load_tail returned a MissingModule if + # (_save)_import_module did return None. PyInstaller changed this in + # cae47e4f5b51a94ac3ceb5d093283ba0cc895589 and raises an ImportError. + # Thus the MissingModule node (which was expected to be created when + # scanning the script above) doesn't exist and findNode will return + # None, which makes this test fail. + #node = mf.findNode('os.path') + #ed = mf.edgeData(script_node, node) + #self.assertIsInstance(ed, modulegraph.DependencyInfo) + #self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=False)) + + node = mf.findNode('os') + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False)) + + node = mf.findNode('sys') + ed = mf.edgeData(script_node, node) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('platform') + ed = mf.edgeData(script_node, node) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=False)) + + node = mf.findNode('email') + ed = mf.edgeData(script_node, node) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=False)) + + def test_from_imports(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-edgedata') + mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + script_name = os.path.join(root, 'script_from_import.py') + mf.run_script(script_name) + + script_node = mf.findNode(script_name) + self.assertIsInstance(script_node, modulegraph.Script) + + + node = mf.findNode('pkg.toplevel_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.toplevel_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.toplevel_class_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.toplevel_class_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.toplevel_conditional_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.toplevel_conditional_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.toplevel_conditional_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.toplevel_conditional_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.toplevel_conditional_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.toplevel_conditional_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.toplevel_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.toplevel_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.toplevel_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.toplevel_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=False, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.function_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.function_class_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.function_class_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.function_conditional_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.function_conditional_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=False, fromlist=True)) + + node = mf.findNode('pkg.function_conditional_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_conditional_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_conditional_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_conditional_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=True, function=True, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_import_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_import_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_import2_existing') + self.assertIsInstance(node, modulegraph.SourceModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True)) + + node = mf.findNode('pkg.function_import2_nonexisting') + self.assertIsInstance(node, modulegraph.MissingModule) + ed = mf.edgeData(script_node, node) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo(conditional=False, function=True, tryexcept=True, fromlist=True)) + + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_explicit_packages.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_explicit_packages.py new file mode 100644 index 0000000000000000000000000000000000000000..2287e5f0331f5c6180aae67d57d4983e996131d9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_explicit_packages.py @@ -0,0 +1,51 @@ +import unittest + +import os +import sys + +from PyInstaller.lib.modulegraph import find_modules +from PyInstaller.lib.modulegraph import modulegraph + + +class PackagesTestCase (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, object, types, message=None): + self.assertTrue(isinstance(object, types), + message or '%r is not an instance of %r'%(object, types)) + + def testIncludePackage(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-packages') + + mf = find_modules.find_modules( + path=[root]+sys.path, + scripts=[os.path.join(root, "main_script.py")], + packages=['pkg'], + debug=1) + + node = mf.findNode('pkg') + self.assertIsInstance(node, modulegraph.Package) + + node = mf.findNode('pkg.sub3') + self.assertIsInstance(node, modulegraph.SourceModule) + + def testIncludePackageWithExclude(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-packages') + + mf = find_modules.find_modules( + path=[root]+sys.path, + scripts=[os.path.join(root, "main_script.py")], + packages=['pkg'], + excludes=['pkg.sub3']) + + node = mf.findNode('pkg') + self.assertIsInstance(node, modulegraph.Package) + + node = mf.findNode('pkg.sub3') + self.assertIsInstance(node, modulegraph.ExcludedModule) + +if __name__ == '__main__': + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_implies.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_implies.py new file mode 100644 index 0000000000000000000000000000000000000000..48b5ca932e1c70892aa7a5c9ebc13c8420889554 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_implies.py @@ -0,0 +1,77 @@ +import unittest + +import os, shutil, sys + +from PyInstaller.lib.modulegraph import modulegraph + +class ImpliesTestCase(unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, object, types, message=None): + self.assertTrue(isinstance(object, types), + message or '%r is not an instance of %r'%(object, types)) + + def testBasicImplies(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport') + + # First check that 'getopt' isn't accidently in the graph: + mg = modulegraph.ModuleGraph(path=[root]+sys.path) + mg.run_script(os.path.join(root, 'script.py')) + node = mg.findNode('mod') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = mg.findNode('getopt') + self.assertEqual(node, None) + + # Now check that adding an implied dependency actually adds + # 'getopt' to the graph: + mg = modulegraph.ModuleGraph(path=[root]+sys.path, implies={ + 'mod': ['getopt']}) + self.assertEqual(node, None) + mg.run_script(os.path.join(root, 'script.py')) + node = mg.findNode('mod') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = mg.findNode('getopt') + self.assertIsInstance(node, modulegraph.SourceModule) + + # Check that the edges are correct: + self.assertIn(mg.findNode('mod'), mg.get_edges(node)[1]) + self.assertIn(node, mg.get_edges(mg.findNode('mod'))[0]) + + def testPackagedImplies(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport') + + # First check that 'getopt' isn't accidently in the graph: + mg = modulegraph.ModuleGraph(path=[root]+sys.path) + mg.run_script(os.path.join(root, 'script.py')) + node = mg.findNode('mod') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = mg.findNode('getopt') + self.assertEqual(node, None) + + + # Now check that adding an implied dependency actually adds + # 'getopt' to the graph: + mg = modulegraph.ModuleGraph(path=[root]+sys.path, implies={ + 'pkg.relative': ['getopt']}) + node = mg.findNode('getopt') + self.assertEqual(node, None) + mg.run_script(os.path.join(root, 'script.py')) + node = mg.findNode('pkg.relative') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = mg.findNode('getopt') + self.assertIsInstance(node, modulegraph.SourceModule) + + # Check that the edges are correct: + self.assertIn(mg.findNode('pkg.relative'), mg.get_edges(node)[1]) + self.assertIn(node, mg.get_edges(mg.findNode('pkg.relative'))[0]) + + +if __name__ == '__main__': + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_import_from_init.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_import_from_init.py new file mode 100644 index 0000000000000000000000000000000000000000..3f44c3dd2312c756d5c5fa05a6fe3a0694f890c0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_import_from_init.py @@ -0,0 +1,128 @@ +import sys +if sys.version_info[:2] <= (2,6): + import unittest2 as unittest +else: + import unittest +import textwrap +import subprocess +import os +from PyInstaller.lib.modulegraph import modulegraph + +class TestNativeImport (unittest.TestCase): + # The tests check that Python's import statement + # works as these tests expect. + + def importModule(self, name): + if '.' in name: + script = textwrap.dedent("""\ + try: + import %s + except ImportError: + import %s + print (%s.__name__) + """) %(name, name.rsplit('.', 1)[0], name) + else: + script = textwrap.dedent("""\ + import %s + print (%s.__name__) + """) %(name, name) + + p = subprocess.Popen([sys.executable, '-c', script], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-import-from-init'), + ) + data = p.communicate()[0] + if sys.version_info[0] != 2: + data = data.decode('UTF-8') + data = data.strip() + + if data.endswith(' refs]'): + # with --with-pydebug builds + data = data.rsplit('\n', 1)[0].strip() + + sts = p.wait() + + if sts != 0: + print (data) + self.assertEqual(sts, 0) + return data + + + @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test") + def testRootPkg(self): + m = self.importModule('pkg') + self.assertEqual(m, 'pkg') + + @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test") + def testSubPackage(self): + m = self.importModule('pkg.subpkg') + self.assertEqual(m, 'pkg.subpkg') + + def testRootPkgRelImport(self): + m = self.importModule('pkg2') + self.assertEqual(m, 'pkg2') + + def testSubPackageRelImport(self): + m = self.importModule('pkg2.subpkg') + self.assertEqual(m, 'pkg2.subpkg') + + +class TestModuleGraphImport (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-import-from-init') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + #self.mf.debug = 999 + self.mf.run_script(os.path.join(root, 'script.py')) + + + @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test") + def testRootPkg(self): + node = self.mf.findNode('pkg') + self.assertIsInstance(node, modulegraph.Package) + self.assertEqual(node.identifier, 'pkg') + + @unittest.skipUnless(sys.version_info[0] == 2, "Python 2.x test") + def testSubPackage(self): + node = self.mf.findNode('pkg.subpkg') + self.assertIsInstance(node, modulegraph.Package) + self.assertEqual(node.identifier, 'pkg.subpkg') + + node = self.mf.findNode('pkg.subpkg.compat') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.subpkg.compat') + + node = self.mf.findNode('pkg.subpkg._collections') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.subpkg._collections') + + def testRootPkgRelImport(self): + node = self.mf.findNode('pkg2') + self.assertIsInstance(node, modulegraph.Package) + self.assertEqual(node.identifier, 'pkg2') + + def testSubPackageRelImport(self): + node = self.mf.findNode('pkg2.subpkg') + self.assertIsInstance(node, modulegraph.Package) + self.assertEqual(node.identifier, 'pkg2.subpkg') + + node = self.mf.findNode('pkg2.subpkg.compat') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg2.subpkg.compat') + + node = self.mf.findNode('pkg2.subpkg._collections') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg2.subpkg._collections') + + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_imports.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_imports.py new file mode 100644 index 0000000000000000000000000000000000000000..16382f7b40294d8c9d91a27248a87c0b27c76979 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_imports.py @@ -0,0 +1,559 @@ +""" +Test for import machinery +""" +import unittest +import sys +import textwrap +import subprocess +import os +from PyInstaller.lib.modulegraph import modulegraph + +class TestNativeImport (unittest.TestCase): + # The tests check that Python's import statement + # works as these tests expect. + + def importModule(self, name): + if '.' in name: + script = textwrap.dedent("""\ + try: + import %s + except ImportError: + import %s + print (%s.__name__) + """) %(name, name.rsplit('.', 1)[0], name) + else: + script = textwrap.dedent("""\ + import %s + print (%s.__name__) + """) %(name, name) + + p = subprocess.Popen([sys.executable, '-c', script], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport'), + ) + data = p.communicate()[0] + if sys.version_info[0] != 2: + data = data.decode('UTF-8') + data = data.strip() + + if data.endswith(' refs]'): + # with --with-pydebug builds + data = data.rsplit('\n', 1)[0].strip() + + sts = p.wait() + + if sts != 0: + print (data) + self.assertEqual(sts, 0) + return data + + + def testRootModule(self): + m = self.importModule('mod') + self.assertEqual(m, 'mod') + + def testRootPkg(self): + m = self.importModule('pkg') + self.assertEqual(m, 'pkg') + + def testSubModule(self): + m = self.importModule('pkg.mod') + self.assertEqual(m, 'pkg.mod') + + if sys.version_info[0] == 2: + def testOldStyle(self): + m = self.importModule('pkg.oldstyle.mod') + self.assertEqual(m, 'pkg.mod') + else: + # python3 always has __future__.absolute_import + def testOldStyle(self): + m = self.importModule('pkg.oldstyle.mod') + self.assertEqual(m, 'mod') + + def testNewStyle(self): + m = self.importModule('pkg.toplevel.mod') + self.assertEqual(m, 'mod') + + def testRelativeImport(self): + m = self.importModule('pkg.relative.mod') + self.assertEqual(m, 'pkg.mod') + + m = self.importModule('pkg.subpkg.relative.mod') + self.assertEqual(m, 'pkg.mod') + + m = self.importModule('pkg.subpkg.mod2.mod') + self.assertEqual(m, 'pkg.sub2.mod') + + m = self.importModule('pkg.subpkg.relative2') + self.assertEqual(m, 'pkg.subpkg.relative2') + +class TestModuleGraphImport (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + #self.mf.debug = 999 + self.script_name = os.path.join(root, 'script.py') + self.mf.run_script(self.script_name) + + def testGraphStructure(self): + + # 1. Script to imported modules + n = self.mf.findNode(self.script_name) + self.assertIsInstance(n, modulegraph.Script) + + imported = ('mod', 'pkg', 'pkg.mod', 'pkg.oldstyle', + 'pkg.relative', 'pkg.toplevel', 'pkg.subpkg.relative', + 'pkg.subpkg.relative2', 'pkg.subpkg.mod2') + + for nm in imported: + n2 = self.mf.findNode(nm) + ed = self.mf.edgeData(n, n2) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + refs = self.mf.getReferences(n) + self.assertEqual(set(refs), set(self.mf.findNode(nm) for nm in imported)) + + refs = list(self.mf.getReferers(n)) + # The script is a toplevel item and is therefore referred to from the graph root (aka 'None') + # FIXME fails since PyInstaller skips edges pointing to the current + # graph, see change 49c725e9f5a79b65923b8e1bfdd794f0f6f7c4bf + #self.assertEqual(refs, [None]) + + + # 2. 'mod' + n = self.mf.findNode('mod') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = list(self.mf.getReferences(n)) + self.assertEqual(refs, []) + + #refs = list(self.mf.getReferers(n)) + #self.assertEquals(refs, []) + + # 3. 'pkg' + n = self.mf.findNode('pkg') + self.assertIsInstance(n, modulegraph.Package) + refs = list(self.mf.getReferences(n)) + self.maxDiff = None + self.assertEqual(refs, [n]) + + #refs = list(self.mf.getReferers(n)) + #self.assertEquals(refs, []) + + # 4. pkg.mod + n = self.mf.findNode('pkg.mod') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = set(self.mf.getReferences(n)) + self.assertEqual(refs, set([self.mf.findNode('pkg')])) + ed = self.mf.edgeData(n, self.mf.findNode('pkg')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + + # 5. pkg.oldstyle + n = self.mf.findNode('pkg.oldstyle') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = set(self.mf.getReferences(n)) + if sys.version_info[0] == 2: + n2 = self.mf.findNode('pkg.mod') + else: + n2 = self.mf.findNode('mod') + self.assertEqual(refs, set([self.mf.findNode('pkg'), n2])) + ed = self.mf.edgeData(n, n2) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + + # 6. pkg.relative + n = self.mf.findNode('pkg.relative') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = set(self.mf.getReferences(n)) + self.assertEqual(refs, set([self.mf.findNode('__future__'), self.mf.findNode('pkg'), self.mf.findNode('pkg.mod')])) + + ed = self.mf.edgeData(n, self.mf.findNode('pkg.mod')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=True, conditional=False, function=False, tryexcept=False)) + + ed = self.mf.edgeData(n, self.mf.findNode('__future__')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + #ed = self.mf.edgeData(n, self.mf.findNode('__future__.absolute_import')) + #self.assertIsInstance(ed, modulegraph.DependencyInfo) + #self.assertEqual(ed, modulegraph.DependencyInfo( + #fromlist=True, conditional=False, function=False, tryexcept=False)) + + # 7. pkg.toplevel + n = self.mf.findNode('pkg.toplevel') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = set(self.mf.getReferences(n)) + self.assertEqual(refs, set([self.mf.findNode('__future__'), self.mf.findNode('pkg'), self.mf.findNode('mod')])) + + ed = self.mf.edgeData(n, self.mf.findNode('mod')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + ed = self.mf.edgeData(n, self.mf.findNode('__future__')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + #ed = self.mf.edgeData(n, self.mf.findNode('__future__.absolute_import')) + #self.assertIsInstance(ed, modulegraph.DependencyInfo) + #self.assertEqual(ed, modulegraph.DependencyInfo( + #fromlist=True, conditional=False, function=False, tryexcept=False)) + + # 8. pkg.subpkg + n = self.mf.findNode('pkg.subpkg') + self.assertIsInstance(n, modulegraph.Package) + refs = set(self.mf.getReferences(n)) + self.assertEqual(refs, set([self.mf.findNode('pkg')])) + + ed = self.mf.edgeData(n, self.mf.findNode('pkg')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + # 9. pkg.subpkg.relative + n = self.mf.findNode('pkg.subpkg.relative') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = set(self.mf.getReferences(n)) + self.assertEqual(refs, set([self.mf.findNode('__future__'), self.mf.findNode('pkg'), self.mf.findNode('pkg.subpkg'), self.mf.findNode('pkg.mod')])) + + ed = self.mf.edgeData(n, self.mf.findNode('pkg.subpkg')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=False, conditional=False, function=False, tryexcept=False)) + + ed = self.mf.edgeData(n, self.mf.findNode('pkg.mod')) + self.assertIsInstance(ed, modulegraph.DependencyInfo) + self.assertEqual(ed, modulegraph.DependencyInfo( + fromlist=True, conditional=False, function=False, tryexcept=False)) + + # 10. pkg.subpkg.relative2 + n = self.mf.findNode('pkg.subpkg.relative2') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = set(self.mf.getReferences(n)) + self.assertEqual(refs, set([self.mf.findNode('pkg.subpkg'), self.mf.findNode('pkg.relimport'), self.mf.findNode('__future__')])) + + # 10. pkg.subpkg.mod2 + n = self.mf.findNode('pkg.subpkg.mod2') + self.assertIsInstance(n, modulegraph.SourceModule) + refs = set(self.mf.getReferences(n)) + self.assertEqual(refs, set([ + self.mf.findNode('__future__'), + self.mf.findNode('pkg.subpkg'), + self.mf.findNode('pkg.sub2.mod'), + self.mf.findNode('pkg.sub2'), + ])) + + + def testRootModule(self): + node = self.mf.findNode('mod') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'mod') + + def testRootPkg(self): + node = self.mf.findNode('pkg') + self.assertIsInstance(node, modulegraph.Package) + self.assertEqual(node.identifier, 'pkg') + + def testSubModule(self): + node = self.mf.findNode('pkg.mod') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.mod') + + if sys.version_info[0] == 2: + def testOldStyle(self): + node = self.mf.findNode('pkg.oldstyle') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.oldstyle') + sub = [ n for n in self.mf.get_edges(node)[0] if n.identifier != '__future__' ][0] + self.assertEqual(sub.identifier, 'pkg.mod') + else: + # python3 always has __future__.absolute_import + def testOldStyle(self): + node = self.mf.findNode('pkg.oldstyle') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.oldstyle') + sub = [ n for n in self.mf.get_edges(node)[0] if n.identifier != '__future__' ][0] + self.assertEqual(sub.identifier, 'mod') + + def testNewStyle(self): + node = self.mf.findNode('pkg.toplevel') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.toplevel') + sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__')][0] + self.assertEqual(sub.identifier, 'mod') + + def testRelativeImport(self): + node = self.mf.findNode('pkg.relative') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.relative') + sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__') ][0] + self.assertIsInstance(sub, modulegraph.Package) + self.assertEqual(sub.identifier, 'pkg') + + node = self.mf.findNode('pkg.subpkg.relative') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.subpkg.relative') + sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__') ][0] + self.assertIsInstance(sub, modulegraph.Package) + self.assertEqual(sub.identifier, 'pkg') + + node = self.mf.findNode('pkg.subpkg.mod2') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.subpkg.mod2') + sub = [ n for n in self.mf.get_edges(node)[0] if not n.identifier.startswith('__future__') ][0] + self.assertIsInstance(sub, modulegraph.SourceModule) + self.assertEqual(sub.identifier, 'pkg.sub2.mod') + + node = self.mf.findNode('pkg.subpkg.relative2') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'pkg.subpkg.relative2') + + node = self.mf.findNode('pkg.relimport') + self.assertIsInstance(node, modulegraph.SourceModule) + +class TestRegressions1 (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r", value, types) + + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr1') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + self.mf.run_script(os.path.join(root, 'main_script.py')) + + def testRegr1(self): + node = self.mf.findNode('pkg.a') + self.assertIsInstance(node, modulegraph.SourceModule) + node = self.mf.findNode('pkg.b') + self.assertIsInstance(node, modulegraph.SourceModule) + + + def testMissingPathEntry(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'nosuchdirectory') + try: + mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + except os.error: + self.fail('modulegraph initialiser raises os.error') + +class TestRegressions2 (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr2') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + self.mf.run_script(os.path.join(root, 'main_script.py')) + + def testRegr1(self): + node = self.mf.findNode('pkg.base') + self.assertIsInstance(node, modulegraph.SourceModule) + node = self.mf.findNode('pkg.pkg') + self.assertIsInstance(node, modulegraph.SourceModule) + +class TestRegressions3 (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def assertStartswith(self, value, test): + if not value.startswith(test): + self.fail("%r does not start with %r"%(value, test)) + + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr3') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + self.mf.run_script(os.path.join(root, 'script.py')) + + @unittest.skipUnless(not hasattr(sys, 'real_prefix'), "Test doesn't work in virtualenv") + def testRegr1(self): + node = self.mf.findNode('mypkg.distutils') + self.assertIsInstance(node, modulegraph.Package) + node = self.mf.findNode('mypkg.distutils.ccompiler') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertStartswith(node.filename, os.path.dirname(__file__)) + + import distutils.sysconfig, distutils.ccompiler + node = self.mf.findNode('distutils.ccompiler') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(os.path.dirname(node.filename), + os.path.dirname(distutils.ccompiler.__file__)) + + node = self.mf.findNode('distutils.sysconfig') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(os.path.dirname(node.filename), + os.path.dirname(distutils.sysconfig.__file__)) + +class TestRegression4 (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr4') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + self.mf.run_script(os.path.join(root, 'script.py')) + + def testRegr1(self): + node = self.mf.findNode('pkg.core') + self.assertIsInstance(node, modulegraph.Package) + + node = self.mf.findNode('pkg.core.callables') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = self.mf.findNode('pkg.core.listener') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = self.mf.findNode('pkg.core.listenerimpl') + self.assertIsInstance(node, modulegraph.SourceModule) + +class TestRegression5 (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr5') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + self.mf.run_script(os.path.join(root, 'script.py')) + + def testRegr1(self): + node = self.mf.findNode('distutils') + self.assertIsInstance(node, modulegraph.Package) + self.assertIn(os.path.join('distutils', '__init__'), node.filename) + +class TestDeeplyNested (unittest.TestCase): + def setUp(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr6') + self.mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + self.mf.run_script(os.path.join(root, 'script.py')) + + def testRegr(self): + node = self.mf.findNode('os') + self.assertIsNot(node, None) + + +class TestRelativeReferenceToToplevel (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def test_relative_import_too_far(self): + # pkg.mod tries to import "..sys" (outside of the package...) + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr7') + mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + mf.run_script(os.path.join(root, 'script.py')) + + m = mf.findNode('') + self.assertIs(m, None) + + m = mf.findNode('pkg.mod') + self.assertIsInstance(m, modulegraph.SourceModule) + + imported = list(mf.get_edges(m)[0]) + self.assertEqual(len(imported), 5) + + im = imported[0] + self.assertIsInstance(im, modulegraph.InvalidRelativeImport) + self.assertEqual(im.relative_path, '..') + self.assertEqual(im.from_name, 'sys') + self.assertEqual(im.identifier, '..sys') + + im1 = imported[1] + im2 = imported[2] + if im1.identifier == '...xml': + # Order of modules imported in a single 'from .. import a, b' list + # is unspecified, ensure a fixed order for this test. + im2, im1 = im1, im2 + + self.assertIsInstance(im1, modulegraph.InvalidRelativeImport) + self.assertEqual(im1.relative_path, '...') + self.assertEqual(im1.from_name, 'os') + self.assertEqual(im1.identifier, '...os') + + im = imported[2] + self.assertIsInstance(im2, modulegraph.InvalidRelativeImport) + self.assertEqual(im2.relative_path, '...') + self.assertEqual(im2.from_name, 'xml') + self.assertEqual(im2.identifier, '...xml') + + im = imported[3] + self.assertIsInstance(im, modulegraph.InvalidRelativeImport) + self.assertEqual(im.relative_path, '..foo') + self.assertEqual(im.from_name, 'bar') + self.assertEqual(im.identifier, '..foo.bar') + + im = imported[4] + self.assertIs(im, mf.findNode('pkg')) + +class TestInvalidAsyncFunction (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + @unittest.skipUnless(sys.version_info[:2] == (3,5), "Requires python 3.5") + def test_invalid_async_function(self): + # In python 3.5 the following function is invalid: + # + # async def foo(): + # yield 1 + # + # This is a syntax error that's reported when compiling the AST + # to bytecode, which caused an error in modulegraph. + # + # In python 3.6 this is valid code (and in earlier versions async + # versions didn't exist) + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-regr8') + mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + mf.run_script(os.path.join(root, 'script.py')) + + n = mf.findNode('mod') + self.assertIsInstance(n, modulegraph.InvalidSourceModule) + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_modulegraph.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_modulegraph.py new file mode 100644 index 0000000000000000000000000000000000000000..986310ec46d8886410e81c62fe1afd6134532084 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_modulegraph.py @@ -0,0 +1,1036 @@ +import unittest +from PyInstaller.lib.modulegraph import modulegraph +import pkg_resources +import os +import imp +import sys +import shutil +import warnings +from altgraph import Graph +from PyInstaller.compat import is_win +import textwrap +from lxml import etree +import pickle + +from importlib._bootstrap_external import SourceFileLoader, ExtensionFileLoader +from zipimport import zipimporter + +try: + bytes +except NameError: + bytes = str + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +TESTDATA = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + "testdata", "nspkg") + +READ_MODE = "U" if sys.version_info[:2] < (3,4) else "r" + +try: + expectedFailure = unittest.expectedFailure +except AttributeError: + import functools + def expectedFailure(function): + @functools.wraps(function) + def wrapper(*args, **kwds): + try: + function(*args, **kwds) + except AssertionError: + pass + + else: + self.fail("unexpected pass") + +class TestDependencyInfo (unittest.TestCase): + def test_pickling(self): + info = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=False) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + b = pickle.dumps(info, proto) + self.assertTrue(isinstance(b, bytes)) + + o = pickle.loads(b) + self.assertEqual(o, info) + + def test_merging(self): + info1 = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=False) + info2 = modulegraph.DependencyInfo(function=False, conditional=True, tryexcept=True, fromlist=False) + self.assertEqual( + info1._merged(info2), modulegraph.DependencyInfo(function=True, conditional=True, tryexcept=True, fromlist=False)) + + info2 = modulegraph.DependencyInfo(function=False, conditional=True, tryexcept=False, fromlist=False) + self.assertEqual( + info1._merged(info2), modulegraph.DependencyInfo(function=True, conditional=True, tryexcept=True, fromlist=False)) + + info2 = modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False) + self.assertEqual( + info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False)) + + info1 = modulegraph.DependencyInfo(function=True, conditional=False, tryexcept=True, fromlist=True) + self.assertEqual( + info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=False)) + + info2 = modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=True) + self.assertEqual( + info1._merged(info2), modulegraph.DependencyInfo(function=False, conditional=False, tryexcept=False, fromlist=True)) + + +class TestFunctions (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, obj, types): + self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types)) + + def test_eval_str_tuple(self): + for v in [ + '()', + '("hello",)', + '("hello", "world")', + "('hello',)", + "('hello', 'world')", + "('hello', \"world\")", + ]: + + self.assertEqual(modulegraph._eval_str_tuple(v), eval(v)) + + self.assertRaises(ValueError, modulegraph._eval_str_tuple, "") + self.assertRaises(ValueError, modulegraph._eval_str_tuple, "'a'") + self.assertRaises(ValueError, modulegraph._eval_str_tuple, "'a', 'b'") + self.assertRaises(ValueError, modulegraph._eval_str_tuple, "('a', ('b', 'c'))") + self.assertRaises(ValueError, modulegraph._eval_str_tuple, "('a', ('b\", 'c'))") + + def test_namespace_package_path(self): + class DS (object): + def __init__(self, path, namespace_packages=None): + self.location = path + self._namespace_packages = namespace_packages + + def has_metadata(self, key): + if key == 'namespace_packages.txt': + return self._namespace_packages is not None + + raise ValueError("invalid lookup key") + + def get_metadata(self, key): + if key == 'namespace_packages.txt': + if self._namespace_packages is None: + raise ValueError("no file") + + return self._namespace_packages + + raise ValueError("invalid lookup key") + + class WS (object): + def __init__(self, path=None): + pass + + def __iter__(self): + yield DS("/pkg/pkg1") + yield DS("/pkg/pkg2", "foo\n") + yield DS("/pkg/pkg3", "bar.baz\n") + yield DS("/pkg/pkg4", "foobar\nfoo\n") + + saved_ws = pkg_resources.WorkingSet + try: + pkg_resources.WorkingSet = WS + + self.assertEqual(modulegraph._namespace_package_path("sys", ["appdir/pkg"]), + ["appdir/pkg"]) + self.assertEqual(modulegraph._namespace_package_path("foo", ["appdir/pkg"]), + ["appdir/pkg", + os.path.join("/pkg/pkg2", "foo"), + os.path.join("/pkg/pkg4", "foo")]) + self.assertEqual(modulegraph._namespace_package_path("bar.baz", ["appdir/pkg"]), + ["appdir/pkg", + os.path.join("/pkg/pkg3", "bar", "baz")]) + + finally: + pkg_resources.WorkingSet = saved_ws + + def test_os_listdir(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'testdata') + + if is_win: + dirname = 'C:\\Windows\\' + filename = 'C:\\Windows\\user32.dll\\foobar' + else: + dirname = '/etc/' + filename = '/etc/hosts/foobar' + + self.assertEqual(modulegraph.os_listdir(dirname), os.listdir(dirname)) + self.assertRaises(IOError, modulegraph.os_listdir, filename) + self.assertRaises(IOError, modulegraph.os_listdir, os.path.join(root, 'test.egg', 'bar')) + + self.assertEqual(list(sorted(modulegraph.os_listdir(os.path.join(root, 'test.egg', 'foo')))), + [ 'bar', 'bar.txt', 'baz.txt' ]) + + def test_code_to_file(self): + try: + code = modulegraph._code_to_file.__code__ + except AttributeError: + code = modulegraph._code_to_file.func_code + + data = modulegraph._code_to_file(code) + self.assertTrue(hasattr(data, 'read')) + + content = data.read() + self.assertIsInstance(content, bytes) + data.close() + + def test_find_module(self): + for path in ('syspath', 'syspath.zip', 'syspath.egg'): + path = os.path.join(os.path.dirname(TESTDATA), path) + if os.path.exists(os.path.join(path, 'mymodule.pyc')): + os.unlink(os.path.join(path, 'mymodule.pyc')) + + # Plain module + modgraph = modulegraph.ModuleGraph() + info = modgraph._find_module('mymodule', path=[path] + sys.path) + + filename, loader = info + + if path.endswith('.zip') or path.endswith('.egg'): + # Zip importers may precompile + if filename.endswith('.py'): + self.assertEqual(filename, os.path.join(path, 'mymodule.py')) + self.assertIsInstance(loader, zipimporter) + + else: + self.assertEqual(filename, os.path.join(path, 'mymodule.pyc')) + self.assertIsInstance(loader, zipimporter) + + else: + self.assertEqual(filename, os.path.join(path, 'mymodule.py')) + self.assertIsInstance(loader, SourceFileLoader) + + # Compiled plain module, no source + if path.endswith('.zip') or path.endswith('.egg'): + self.assertRaises(ImportError, modgraph._find_module, 'mymodule2', path=[path] + sys.path) + + else: + info = modgraph._find_module('mymodule2', path=[path] + sys.path) + + filename, loader = info + + self.assertEqual(filename, os.path.join(path, 'mymodule2.pyc')) + + + # Compiled plain module, with source +# info = modgraph._find_module('mymodule3', path=[path] + sys.path) +# +# fp = info[0] +# filename = info[1] +# description = info[2] +# +# self.assertTrue(hasattr(fp, 'read')) +# +# if sys.version_info[:2] >= (3,2): +# self.assertEqual(filename, os.path.join(path, '__pycache__', 'mymodule3.cpython-32.pyc')) +# else: +# self.assertEqual(filename, os.path.join(path, 'mymodule3.pyc')) +# self.assertEqual(description, ('.pyc', 'rb', imp.PY_COMPILED)) + + + # Package + info = modgraph._find_module('mypkg', path=[path] + sys.path) + + filename, loader = info + + # FIXME: PyInstaller appends `__init__.py` to the pkg-directory + #self.assertEqual(filename, os.path.join(path, 'mypkg')) + self.assertTrue(loader.is_package("mypkg")) + + # Extension + if path.endswith('.zip'): + self.assertRaises(ImportError, modgraph._find_module, 'myext', path=[path] + sys.path) + + elif path.endswith('.egg'): + # FIXME: restore modulegraph's behaviour + # For a zipped egg modulegraph finds 'myext.so', while + # PyInstaller (using the import machinery) finds 'myext.py' + # which is contained in the test-data .egg, too. + # + # See https://bitbucket.org/ronaldoussoren/modulegraph/issues/34 + # + # ronaldoussoren says: "The behavior is intentional and only + # for .egg archives. The reason is to match behavior of + # setuptools: setuptools will create an .egg archive that + # contains the C extension as well as a python module of the + # same name that extras the C extensions into a tempdir and + # than loads it. By preferring the .so file over a .py file in + # eggs the modulegraph is more useful as the .py file is + # generally just a hack to fake support for loading .so files + # from an archive." + pass + else: + info = modgraph._find_module('myext', path=[path] + sys.path) + filename, loader = info + + if sys.platform == 'win32': + ext = '.pyd' + else: + # This is a ly, but is good enough for now + ext = '.so' + + self.assertEqual(filename, os.path.join(path, 'myext' + ext)) + self.assertIsInstance(loader, ExtensionFileLoader) + + def test_moduleInfoForPath(self): + self.assertEqual(modulegraph.moduleInfoForPath("/somewhere/else/file.txt"), None) + + info = modulegraph.moduleInfoForPath("/somewhere/else/file.py") + self.assertEqual(info[0], "file") + if sys.version_info[:2] >= (3,4): + self.assertEqual(info[1], "r") + else: + self.assertEqual(info[1], "U") + self.assertEqual(info[2], imp.PY_SOURCE) + + info = modulegraph.moduleInfoForPath("/somewhere/else/file.pyc") + self.assertEqual(info[0], "file") + self.assertEqual(info[1], "rb") + self.assertEqual(info[2], imp.PY_COMPILED) + + if sys.platform in ('darwin', 'linux2'): + info = modulegraph.moduleInfoForPath("/somewhere/else/file.so") + self.assertEqual(info[0], "file") + self.assertEqual(info[1], "rb") + self.assertEqual(info[2], imp.C_EXTENSION) + + elif sys.platform in ('win32',): + info = modulegraph.moduleInfoForPath("/somewhere/else/file.pyd") + self.assertEqual(info[0], "file") + self.assertEqual(info[1], "rb") + self.assertEqual(info[2], imp.C_EXTENSION) + + if sys.version_info[:2] > (2,5): + def test_deprecated(self): + saved_add = modulegraph.addPackagePath + saved_replace = modulegraph.replacePackage + try: + called = [] + + def log_add(*args, **kwds): + called.append(('add', args, kwds)) + def log_replace(*args, **kwds): + called.append(('replace', args, kwds)) + + modulegraph.addPackagePath = log_add + modulegraph.replacePackage = log_replace + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter("always") + modulegraph.ReplacePackage('a', 'b') + modulegraph.AddPackagePath('c', 'd') + + self.assertEqual(len(w), 2) + self.assertTrue(w[-1].category is DeprecationWarning) + self.assertTrue(w[-2].category is DeprecationWarning) + + self.assertEqual(called, [ + ('replace', ('a', 'b'), {}), + ('add', ('c', 'd'), {}), + ]) + + finally: + modulegraph.addPackagePath = saved_add + modulegraph.replacePackage = saved_replace + + def test_addPackage(self): + saved = modulegraph._packagePathMap + self.assertIsInstance(saved, dict) + try: + modulegraph._packagePathMap = {} + + modulegraph.addPackagePath('foo', 'a') + self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a'] }) + + modulegraph.addPackagePath('foo', 'b') + self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a', 'b'] }) + + modulegraph.addPackagePath('bar', 'b') + self.assertEqual(modulegraph._packagePathMap, { 'foo': ['a', 'b'], 'bar': ['b'] }) + + finally: + modulegraph._packagePathMap = saved + + + def test_replacePackage(self): + saved = modulegraph._replacePackageMap + self.assertIsInstance(saved, dict) + try: + modulegraph._replacePackageMap = {} + + modulegraph.replacePackage("a", "b") + self.assertEqual(modulegraph._replacePackageMap, {"a": "b"}) + modulegraph.replacePackage("a", "c") + self.assertEqual(modulegraph._replacePackageMap, {"a": "c"}) + modulegraph.replacePackage("b", "c") + self.assertEqual(modulegraph._replacePackageMap, {"a": "c", 'b': 'c'}) + + finally: + modulegraph._replacePackageMap = saved + +class TestNode (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, obj, types): + self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types)) + def testBasicAttributes(self): + n = modulegraph.Node("foobar.xyz") + self.assertEqual(n.identifier, n.graphident) + self.assertEqual(n.identifier, 'foobar.xyz') + self.assertEqual(n.filename, None) + self.assertEqual(n.packagepath, None) + self.assertEqual(n.code, None) + self.assertEqual(n._deferred_imports, None) + self.assertEqual(n._starimported_ignored_module_names, set()) + + def test_global_attrs(self): + n = modulegraph.Node("foobar.xyz") + self.assertEqual(n._global_attr_names, set()) + + self.assertFalse(n.is_global_attr('foo')) + n.add_global_attr('foo') + self.assertTrue(n.is_global_attr('foo')) + n.remove_global_attr_if_found('foo') + self.assertFalse(n.is_global_attr('foo')) + # removing then name again must not fail + n.remove_global_attr_if_found('foo') + + def test_submodules(self): + n = modulegraph.Node("foobar.xyz") + self.assertEqual(n._submodule_basename_to_node, {}) + + sm = modulegraph.Node("bar.baz") + self.assertFalse(n.is_submodule('bar')) + n.add_submodule('bar', sm) + self.assertTrue(n.is_submodule('bar')) + self.assertIs(n.get_submodule('bar'), sm) + self.assertRaises(KeyError, n.get_submodule, 'XXX') + self.assertIs(n.get_submodule_or_none('XXX'), None) + + def testOrder(self): + n1 = modulegraph.Node("n1") + n2 = modulegraph.Node("n2") + + self.assertTrue(n1 < n2) + self.assertFalse(n2 < n1) + self.assertTrue(n1 <= n1) + self.assertFalse(n1 == n2) + self.assertTrue(n1 == n1) + self.assertTrue(n1 != n2) + self.assertFalse(n1 != n1) + self.assertTrue(n2 > n1) + self.assertFalse(n1 > n2) + self.assertTrue(n1 >= n1) + self.assertTrue(n2 >= n1) + + def testHashing(self): + n1a = modulegraph.Node('n1') + n1b = modulegraph.Node('n1') + n2 = modulegraph.Node('n2') + + d = {} + d[n1a] = 'n1' + d[n2] = 'n2' + self.assertEqual(d[n1b], 'n1') + self.assertEqual(d[n2], 'n2') + + def test_infoTuple(self): + n = modulegraph.Node('n1') + self.assertEqual(n.infoTuple(), ('n1',)) + + def assertNoMethods(self, klass): + d = dict(klass.__dict__) + del d['__doc__'] + del d['__module__'] + if '__weakref__' in d: + del d['__weakref__'] + if '__qualname__' in d: + # New in Python 3.3 + del d['__qualname__'] + if '__dict__' in d: + # New in Python 3.4 + del d['__dict__'] + if '__slotnames__' in d: + del d['__slotnames__'] + self.assertEqual(d, {}) + + def assertHasExactMethods(self, klass, *methods): + d = dict(klass.__dict__) + del d['__doc__'] + del d['__module__'] + if '__weakref__' in d: + del d['__weakref__'] + if '__qualname__' in d: + # New in Python 3.3 + del d['__qualname__'] + if '__dict__' in d: + # New in Python 3.4 + del d['__dict__'] + for nm in methods: + self.assertTrue(nm in d, "%s doesn't have attribute %r"%(klass, nm)) + del d[nm] + + self.assertEqual(d, {}) + + + if not hasattr(unittest.TestCase, 'assertIsSubclass'): + def assertIsSubclass(self, cls1, cls2, message=None): + self.assertTrue(issubclass(cls1, cls2), + message or "%r is not a subclass of %r"%(cls1, cls2)) + + def test_subclasses(self): + self.assertIsSubclass(modulegraph.AliasNode, modulegraph.Node) + self.assertIsSubclass(modulegraph.Script, modulegraph.Node) + self.assertIsSubclass(modulegraph.BadModule, modulegraph.Node) + self.assertIsSubclass(modulegraph.ExcludedModule, modulegraph.BadModule) + self.assertIsSubclass(modulegraph.MissingModule, modulegraph.BadModule) + self.assertIsSubclass(modulegraph.BaseModule, modulegraph.Node) + self.assertIsSubclass(modulegraph.BuiltinModule, modulegraph.BaseModule) + self.assertIsSubclass(modulegraph.SourceModule, modulegraph.BaseModule) + self.assertIsSubclass(modulegraph.CompiledModule, modulegraph.BaseModule) + self.assertIsSubclass(modulegraph.Package, modulegraph.BaseModule) + self.assertIsSubclass(modulegraph.Extension, modulegraph.BaseModule) + + # These classes have no new functionality, check that no code + # got added: + self.assertNoMethods(modulegraph.BadModule) + self.assertNoMethods(modulegraph.ExcludedModule) + self.assertNoMethods(modulegraph.MissingModule) + self.assertNoMethods(modulegraph.BuiltinModule) + self.assertNoMethods(modulegraph.SourceModule) + self.assertNoMethods(modulegraph.CompiledModule) + self.assertNoMethods(modulegraph.Package) + self.assertNoMethods(modulegraph.Extension) + + # AliasNode is basicly a clone of an existing node + self.assertHasExactMethods(modulegraph.Script, '__init__', 'infoTuple') + n1 = modulegraph.Node('n1') + n1.packagepath = ['a', 'b'] + + a1 = modulegraph.AliasNode('a1', n1) + self.assertEqual(a1.graphident, 'a1') + self.assertEqual(a1.identifier, 'n1') + self.assertTrue(a1.packagepath is n1.packagepath) + + self.assertIs(a1._deferred_imports, None) + self.assertIs(a1._global_attr_names, n1._global_attr_names) + self.assertIs(a1._starimported_ignored_module_names, + n1._starimported_ignored_module_names) + self.assertIs(a1._submodule_basename_to_node, + n1._submodule_basename_to_node) + + v = a1.infoTuple() + self.assertEqual(v, ('a1', 'n1')) + + # Scripts have a filename + self.assertHasExactMethods(modulegraph.Script, '__init__', 'infoTuple') + s1 = modulegraph.Script('do_import') + self.assertEqual(s1.graphident, 'do_import') + self.assertEqual(s1.identifier, 'do_import') + self.assertEqual(s1.filename, 'do_import') + + v = s1.infoTuple() + self.assertEqual(v, ('do_import',)) + + # BaseModule adds some attributes and a custom infotuple + self.assertHasExactMethods(modulegraph.BaseModule, '__init__', 'infoTuple') + m1 = modulegraph.BaseModule('foo') + self.assertEqual(m1.graphident, 'foo') + self.assertEqual(m1.identifier, 'foo') + self.assertEqual(m1.filename, None) + self.assertEqual(m1.packagepath, None) + + m1 = modulegraph.BaseModule('foo', 'bar', ['a']) + self.assertEqual(m1.graphident, 'foo') + self.assertEqual(m1.identifier, 'foo') + self.assertEqual(m1.filename, 'bar') + self.assertEqual(m1.packagepath, ['a']) + +class TestModuleGraph (unittest.TestCase): + # Test for class modulegraph.modulegraph.ModuleGraph + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, obj, types): + self.assertTrue(isinstance(obj, types), '%r is not instance of %r'%(obj, types)) + + def test_constructor(self): + o = modulegraph.ModuleGraph() + self.assertTrue(o.path is sys.path) + self.assertEqual(o.lazynodes, {}) + self.assertEqual(o.replace_paths, ()) + self.assertEqual(o.debug, 0) + + # Stricter tests would be nice, but that requires + # better control over what's on sys.path + self.assertIsInstance(o.nspackages, dict) + + g = Graph.Graph() + o = modulegraph.ModuleGraph(['a', 'b', 'c'], ['modA'], [ + ('fromA', 'toB'), ('fromC', 'toD')], + { + 'modA': ['modB', 'modC'], + 'modC': ['modE', 'modF'], + }, g, 1) + self.assertEqual(o.path, ['a', 'b', 'c']) + self.assertEqual(o.lazynodes, { + 'modA': None, + 'modC': ['modE', 'modF'], + }) + self.assertEqual(o.replace_paths, [('fromA', 'toB'), ('fromC', 'toD')]) + self.assertEqual(o.nspackages, {}) + self.assertTrue(o.graph is g) + self.assertEqual(o.debug, 1) + + def test_calc_setuptools_nspackages(self): + stdlib = [ fn for fn in sys.path if fn.startswith(sys.prefix) and 'site-packages' not in fn ] + for subdir in [ nm for nm in os.listdir(TESTDATA) if nm != 'src' ]: + graph = modulegraph.ModuleGraph(path=[ + os.path.join(TESTDATA, subdir, "parent"), + os.path.join(TESTDATA, subdir, "child"), + ] + stdlib) + + pkgs = graph.nspackages + self.assertTrue('namedpkg' in pkgs) + self.assertEqual(set(pkgs['namedpkg']), + set([ + os.path.join(TESTDATA, subdir, "parent", "namedpkg"), + os.path.join(TESTDATA, subdir, "child", "namedpkg"), + ])) + self.assertFalse(os.path.exists(os.path.join(TESTDATA, subdir, "parent", "namedpkg", "__init__.py"))) + self.assertFalse(os.path.exists(os.path.join(TESTDATA, subdir, "child", "namedpkg", "__init__.py"))) + + def testImpliedReference(self): + graph = modulegraph.ModuleGraph() + + record = [] + def import_hook(*args): + record.append(('import_hook',) + args) + return [graph.createNode(modulegraph.Node, args[0])] + + def _safe_import_hook(*args): + record.append(('_safe_import_hook',) + args) + return [graph.createNode(modulegraph.Node, args[0])] + + graph.import_hook = import_hook + graph._safe_import_hook = _safe_import_hook + + n1 = graph.createNode(modulegraph.Node, 'n1') + n2 = graph.createNode(modulegraph.Node, 'n2') + + graph.implyNodeReference(n1, n2) + outs, ins = map(list, graph.get_edges(n1)) + self.assertEqual(outs, [n2]) + self.assertEqual(ins, []) + + self.assertEqual(record, []) + + graph.implyNodeReference(n2, "n3") + n3 = graph.findNode('n3') + outs, ins = map(list, graph.get_edges(n2)) + self.assertEqual(outs, [n3]) + self.assertEqual(ins, [n1]) + self.assertEqual(record, [ + ('_safe_import_hook', 'n3', n2, None) + ]) + + + + @expectedFailure + def test_findNode(self): + self.fail("findNode") + + def test_run_script(self): + script = os.path.join(os.path.dirname(TESTDATA), 'script') + + graph = modulegraph.ModuleGraph() + master = graph.createNode(modulegraph.Node, 'root') + m = graph.run_script(script, master) + self.assertEqual(list(graph.get_edges(master)[0])[0], m) + self.assertEqual(set(graph.get_edges(m)[0]), set([ + graph.findNode('sys'), + graph.findNode('os'), + ])) + + @expectedFailure + def test_import_hook(self): + self.fail("import_hook") + + def test_determine_parent(self): + graph = modulegraph.ModuleGraph() + # FIXME PyInstaller: original _load_tail returned a MissingModule if + # the module was not found. PyInstaller changed this in + # cae47e4f5b51a94ac3ceb5d093283ba0cc895589 and raises an ImportError, + # which makes these two calls fail. + #graph.import_hook('os.path', None) + #graph.import_hook('idlelib', None) + graph.import_hook('xml.dom', None) + + for node in graph.nodes(): + if isinstance(node, modulegraph.Package): + break + else: + self.fail("No package located, should have at least 'os'") + + self.assertIsInstance(node, modulegraph.Package) + parent = graph._determine_parent(node) + self.assertEqual(parent.identifier, node.identifier) + self.assertEqual(parent, graph.findNode(node.identifier)) + self.assertTrue(isinstance(parent, modulegraph.Package)) + + # XXX: Might be a usecase for some odd code in determine_parent... + #node = modulegraph.Package('encodings') + #node.packagepath = parent.packagepath + #m = graph._determine_parent(node) + #self.assertTrue(m is parent) + + m = graph.findNode('xml') + self.assertEqual(graph._determine_parent(m), m) + + m = graph.findNode('xml.dom') + self.assertEqual(graph._determine_parent(m), graph.findNode('xml.dom')) + + + @expectedFailure + def test_find_head_package(self): + self.fail("find_head_package") + + + @expectedFailure + def test_ensure_fromlist(self): + # 1. basic 'from module import name, name' + # 2. 'from module import *' + # 3. from module import os + # (where 'os' is not a name in 'module', + # should create MissingModule node, and + # should *not* refer to the global os) + self.fail("ensure_fromlist") + + @expectedFailure + def test_find_all_submodules(self): + # 1. basic + # 2. no packagepath (basic module) + # 3. extensions, python modules + # 4. with/without zipfile + # 5. files that aren't python modules/extensions + self.fail("find_all_submodules") + + @expectedFailure + def test_import_module(self): + self.fail("import_module") + + @expectedFailure + def test_load_module(self): + self.fail("load_module") + + @expectedFailure + def test_safe_import_hook(self): + self.fail("safe_import_hook") + + @expectedFailure + def test_scan_code(self): + mod = modulegraph.Node('root') + + graph = modulegraph.ModuleGraph() + code = compile('', '', 'exec', 0, False) + graph.scan_code(code, mod) + self.assertEqual(list(graph.nodes()), []) + + node_map = {} + def _safe_import(name, mod, fromlist, level): + if name in node_map: + node = node_map[name] + else: + node = modulegraph.Node(name) + node_map[name] = node + return [node] + + graph = modulegraph.ModuleGraph() + graph._safe_import_hook = _safe_import + + code = compile(textwrap.dedent('''\ + import sys + import os.path + + def testfunc(): + import shutil + '''), '', 'exec', 0, False) + graph.scan_code(code, mod) + modules = [node.identifier for node in graph.nodes()] + self.assertEqual(set(node_map), set(['sys', 'os.path', 'shutil'])) + + + # from module import a, b, c + # from module import * + # both: + # -> with/without globals + # -> with/without modules in globals (e.g, + # from os import * adds dependency to os.path) + # from .module import a + # from ..module import a + # -> check levels + # import name + # import a.b + # -> should add dependency to a + # try to build case where commented out + # code would behave different than current code + # (Carbon.SomeMod contains 'import Sibling' seems + # to cause difference in real code) + + self.fail("actual test needed") + + + + @expectedFailure + def test_load_package(self): + self.fail("load_package") + + def test_find_module(self): + record = [] + class MockedModuleGraph(modulegraph.ModuleGraph): + def _find_module(self, name, path, parent=None): + if path == None: + path = sys.path + record.append((name, path)) + return super(MockedModuleGraph, self)._find_module(name, path, parent) + + mockedgraph = MockedModuleGraph() + try: + graph = modulegraph.ModuleGraph() + m = graph._find_module('sys', None) + self.assertEqual(record, []) + self.assertEqual(m, (None, modulegraph.BUILTIN_MODULE)) + + xml = graph.import_hook("xml")[0] + self.assertEqual(xml.identifier, 'xml') + + self.assertRaises(ImportError, graph._find_module, 'xml', None) + + self.assertEqual(record, []) + m = mockedgraph._find_module('shutil', None) + self.assertEqual(record, [ + ('shutil', graph.path), + ]) + self.assertTrue(isinstance(m, tuple)) + self.assertEqual(len(m), 2) + srcfn = shutil.__file__ + if srcfn.endswith('.pyc'): + srcfn = srcfn[:-1] + self.assertEqual(os.path.realpath(m[0]), os.path.realpath(srcfn)) + self.assertIsInstance(m[1], SourceFileLoader) + + m2 = graph._find_module('shutil', None) + self.assertEqual(m[1:], m2[1:]) + + + record[:] = [] + m = mockedgraph._find_module('sax', xml.packagepath, xml) + # FIXME: PyInstaller appends `__init__.py` to the pkg-directory + #self.assertEqual(m, + # (None, os.path.join(os.path.dirname(xml.filename), 'sax'), + # ('', '', imp.PKG_DIRECTORY))) + self.assertEqual(record, [ + ('sax', xml.packagepath), + ]) + + finally: + pass + + @expectedFailure + def test_create_xref(self): + self.fail("create_xref") + + @expectedFailure + def test_itergraphreport(self): + self.fail("itergraphreport") + + def test_report(self): + graph = modulegraph.ModuleGraph() + + saved_stdout = sys.stdout + try: + fp = sys.stdout = StringIO() + graph.report() + lines = fp.getvalue().splitlines() + fp.close() + + self.assertEqual(len(lines), 3) + self.assertEqual(lines[0], '') + self.assertEqual(lines[1], 'Class Name File') + self.assertEqual(lines[2], '----- ---- ----') + + fp = sys.stdout = StringIO() + graph._safe_import_hook('os', None, ()) + graph._safe_import_hook('sys', None, ()) + graph._safe_import_hook('nomod', None, ()) + graph.report() + lines = fp.getvalue().splitlines() + fp.close() + + self.assertEqual(lines[0], '') + self.assertEqual(lines[1], 'Class Name File') + self.assertEqual(lines[2], '----- ---- ----') + expected = [] + for n in graph.flatten(): + if n.filename: + expected.append([type(n).__name__, n.identifier, n.filename]) + else: + expected.append([type(n).__name__, n.identifier]) + + expected.sort() + actual = [item.split() for item in lines[3:]] + actual.sort() + self.assertEqual(expected, actual) + + + finally: + sys.stdout = saved_stdout + + def test_graphreport(self): + + def my_iter(flatpackages="packages"): + yield "line1\n" + yield str(flatpackages) + "\n" + yield "line2\n" + + graph = modulegraph.ModuleGraph() + graph.itergraphreport = my_iter + + fp = StringIO() + graph.graphreport(fp) + self.assertEqual(fp.getvalue(), "line1\n()\nline2\n") + + fp = StringIO() + graph.graphreport(fp, "deps") + self.assertEqual(fp.getvalue(), "line1\ndeps\nline2\n") + + saved_stdout = sys.stdout + try: + sys.stdout = fp = StringIO() + graph.graphreport() + self.assertEqual(fp.getvalue(), "line1\n()\nline2\n") + + finally: + sys.stdout = saved_stdout + + + def test_replace_paths_in_code(self): + join = os.path.join # shortcut + graph = modulegraph.ModuleGraph(replace_paths=[ + ('path1', 'path2'), + (join('path3', 'path5'), 'path4'), + ]) + + co = compile(textwrap.dedent(""" + [x for x in range(4)] + """), join("path4", "index.py"), 'exec', 0, 1) + co = graph._replace_paths_in_code(co) + self.assertEqual(co.co_filename, join('path4', 'index.py')) + + co = compile(textwrap.dedent(""" + [x for x in range(4)] + (x for x in range(4)) + """), join("path1", "index.py"), 'exec', 0, 1) + self.assertEqual(co.co_filename, join('path1', 'index.py')) + co = graph._replace_paths_in_code(co) + self.assertEqual(co.co_filename, join('path2', 'index.py')) + for c in co.co_consts: + if isinstance(c, type(co)): + self.assertEqual(c.co_filename, join('path2', 'index.py')) + + co = compile(textwrap.dedent(""" + [x for x in range(4)] + """), join("path3", "path4", "index.py"), 'exec', 0, 1) + co = graph._replace_paths_in_code(co) + self.assertEqual(co.co_filename, join('path3', 'path4', 'index.py')) + + co = compile(textwrap.dedent(""" + [x for x in range(4)] + """), join("path3", "path5.py"), 'exec', 0, 1) + co = graph._replace_paths_in_code(co) + self.assertEqual(co.co_filename, join('path3', 'path5.py')) + + co = compile(textwrap.dedent(""" + [x for x in range(4)] + """), join("path3", "path5", "index.py"), 'exec', 0, 1) + co = graph._replace_paths_in_code(co) + self.assertEqual(co.co_filename, join('path4', 'index.py')) + + def test_createReference(self): + graph = modulegraph.ModuleGraph() + n1 = modulegraph.Node('n1') + n2 = modulegraph.Node('n2') + graph.addNode(n1) + graph.addNode(n2) + + graph.createReference(n1, n2) + outs, ins = map(list, graph.get_edges(n1)) + self.assertEqual(outs, [n2]) + self.assertEqual(ins, []) + outs, ins = map(list, graph.get_edges(n2)) + self.assertEqual(outs, []) + self.assertEqual(ins, [n1]) + + e = graph.graph.edge_by_node('n1', 'n2') + self.assertIsInstance(e, int) + self.assertEqual(graph.graph.edge_data(e), 'direct') + + def test_create_xref(self): + # XXX: This test is far from optimal, it just ensures + # that all code is exercised to catch small bugs and + # py3k issues without verifying that the code actually + # works.... + graph = modulegraph.ModuleGraph() + if __file__.endswith('.py'): + graph.run_script(__file__) + else: + graph.run_script(__file__[:-1]) + + graph.import_hook('os') + graph.import_hook('xml.etree') + graph.import_hook('unittest') + + fp = StringIO() + graph.create_xref(out=fp) + + data = fp.getvalue() + # Don't tolerate any HTML `parsing errors `_. + parser = etree.HTMLParser(recover=False) + tree = etree.parse(StringIO(data), parser) + assert tree is not None + # Verify no `errors `_ occurred. + assert len(parser.error_log) == 0 + + def test_itergraphreport(self): + # XXX: This test is far from optimal, it just ensures + # that all code is exercised to catch small bugs and + # py3k issues without verifying that the code actually + # works.... + graph = modulegraph.ModuleGraph() + if __file__.endswith('.py'): + graph.run_script(__file__) + else: + graph.run_script(__file__[:-1]) + graph.import_hook('os') + graph.import_hook('xml.etree') + graph.import_hook('unittest') + # Upstream uses 'distutils.command.build', but this does not work at + # Travis which uses a virtualenv (PyInstaller has a + # pre_find_module_path hook for this case, plain modulegraph can't + # handle this). + graph.import_hook('lib2to3.fixes.fix_apply') + + fp = StringIO() + list(graph.itergraphreport()) + + # XXX: platpackages isn't implemented, and is undocumented hence + # it is unclear what this is inteded to be... + #list(graph.itergraphreport(flatpackages=...)) + + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_pep420_nspkg.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_pep420_nspkg.py new file mode 100644 index 0000000000000000000000000000000000000000..ba3e6dfd66177eae4b519aaba1bb4ef01f43f3f4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_pep420_nspkg.py @@ -0,0 +1,220 @@ +""" +Tests that deal with pep420 namespace packages. + +PEP 420 is new in Python 3.3 +""" +import os +import shutil +import sys +import subprocess +import textwrap + +if sys.version_info[:2] <= (2,6): + import unittest2 as unittest +else: + import unittest + +from PyInstaller.lib.modulegraph import modulegraph + +gRootDir = os.path.dirname(os.path.abspath(__file__)) +gSrcDir = os.path.join(gRootDir, 'testpkg-pep420-namespace') + +if sys.version_info[:2] >= (3,3): + + class TestPythonBehaviour (unittest.TestCase): + def importModule(self, name): + test_dir1 = os.path.join(gSrcDir, 'path1') + test_dir2 = os.path.join(gSrcDir, 'path2') + if '.' in name: + script = textwrap.dedent("""\ + import site + site.addsitedir(%r) + site.addsitedir(%r) + try: + import %s + except ImportError: + import %s + print (%s.__name__) + """) %(test_dir1, test_dir2, name, name.rsplit('.', 1)[0], name) + else: + script = textwrap.dedent("""\ + import site + site.addsitedir(%r) + site.addsitedir(%r) + import %s + print (%s.__name__) + """) %(test_dir1, test_dir2, name, name) + + p = subprocess.Popen([sys.executable, '-c', script], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport'), + ) + data = p.communicate()[0] + if sys.version_info[0] != 2: + data = data.decode('UTF-8') + data = data.strip() + if data.endswith(' refs]'): + data = data.rsplit('\n', 1)[0].strip() + + sts = p.wait() + + if sts != 0: + print (data) + self.fail("import of %r failed"%(name,)) + + return data + + def testToplevel(self): + m = self.importModule('package.sub1') + self.assertEqual(m, 'package.sub1') + + m = self.importModule('package.sub2') + self.assertEqual(m, 'package.sub2') + + def testSub(self): + m = self.importModule('package.subpackage.sub') + self.assertEqual(m, 'package.subpackage.sub') + + m = self.importModule('package.nspkg.mod') + self.assertEqual(m, 'package.nspkg.mod') + + class TestModuleGraphImport (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r", value, types) + + def setUp(self): + self.mf = modulegraph.ModuleGraph(path=[ + os.path.join(gSrcDir, 'path1'), + os.path.join(gSrcDir, 'path2'), + ] + sys.path) + + + def testRootPkg(self): + self.mf.import_hook('package') + + node = self.mf.findNode('package') + self.assertIsInstance(node, modulegraph.NamespacePackage) + self.assertEqual(node.identifier, 'package') + self.assertEqual(node.filename, '-') + + def testRootPkgModule(self): + self.mf.import_hook('package.sub1') + + node = self.mf.findNode('package.sub1') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'package.sub1') + + self.mf.import_hook('package.sub2') + node = self.mf.findNode('package.sub2') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'package.sub2') + + def testSubRootPkgModule(self): + self.mf.import_hook('package.subpackage.sub') + + node = self.mf.findNode('package.subpackage.sub') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'package.subpackage.sub') + + node = self.mf.findNode('package') + self.assertIsInstance(node, modulegraph.NamespacePackage) + + self.mf.import_hook('package.nspkg.mod') + node = self.mf.findNode('package.nspkg.mod') + self.assertIsInstance(node, modulegraph.SourceModule) + self.assertEqual(node.identifier, 'package.nspkg.mod') + +else: + # Check that PEP 420 is not implemented in python 3.2 and earlier + # (and that modulegraph also doesn't do this) + + class TestPythonBehaviour (unittest.TestCase): + def importModule(self, name): + test_dir1 = os.path.join(gSrcDir, 'path1') + test_dir2 = os.path.join(gSrcDir, 'path2') + if '.' in name: + script = textwrap.dedent("""\ + import site + site.addsitedir(%r) + site.addsitedir(%r) + try: + import %s + except ImportError: + import %s + print (%s.__name__) + """) %(test_dir1, test_dir2, name, name.rsplit('.', 1)[0], name) + else: + script = textwrap.dedent("""\ + import site + site.addsitedir(%r) + site.addsitedir(%r) + import %s + print (%s.__name__) + """) %(test_dir1, test_dir2, name, name) + + p = subprocess.Popen([sys.executable, '-c', script], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport'), + ) + data = p.communicate()[0] + if sys.version_info[0] != 2: + data = data.decode('UTF-8') + data = data.strip() + if data.endswith(' refs]'): + data = data.rsplit('\n', 1)[0].strip() + + sts = p.wait() + + if sts != 0: + raise ImportError(name) + + return data + + def testToplevel(self): + m = self.importModule('sys') + self.assertEqual(m, 'sys') + + self.assertRaises(ImportError, self.importModule, 'package.sub1') + self.assertRaises(ImportError, self.importModule, 'package.sub2') + + def testSub(self): + self.assertRaises(ImportError, self.importModule, 'package.subpackage.sub') + + class TestModuleGraphImport (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r", value, types) + + def setUp(self): + self.mf = modulegraph.ModuleGraph(path=[ + os.path.join(gSrcDir, 'path1'), + os.path.join(gSrcDir, 'path2'), + ] + sys.path) + + + def testRootPkg(self): + self.assertRaises(ImportError, self.mf.import_hook, 'package') + + node = self.mf.findNode('package') + self.assertIs(node, None) + + def testRootPkgModule(self): + self.assertRaises(ImportError, self.mf.import_hook, 'package.sub1') + + node = self.mf.findNode('package.sub1') + self.assertIs(node, None) + + node = self.mf.findNode('package.sub2') + self.assertIs(node, None) + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_pycompat_pkg.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_pycompat_pkg.py new file mode 100644 index 0000000000000000000000000000000000000000..ae850f7ab1b27b84dd5ed5582c8155726f39874b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_pycompat_pkg.py @@ -0,0 +1,60 @@ +""" +Test for import machinery +""" +import unittest +import sys +import textwrap +import subprocess +import os +from PyInstaller.lib.modulegraph import modulegraph + +class TestModuleGraphImport (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def test_compat(self): + root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-compatmodule') + mf = modulegraph.ModuleGraph(path=[ root ] + sys.path) + mf.import_hook('pkg.api') + + node = mf.findNode('pkg') + self.assertIsInstance(node, modulegraph.Package) + + node = mf.findNode('pkg.api') + self.assertIsInstance(node, modulegraph.SourceModule) + + if sys.version_info[0] == 2: + node = mf.findNode('pkg.api2') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = mf.findNode('pkg.api3') + self.assertIsInstance(node, modulegraph.InvalidSourceModule) + + node = mf.findNode('http.client') + self.assertIs(node, None) + + node = mf.findNode('urllib2') + self.assertIsInstance(node, modulegraph.SourceModule) + + else: + node = mf.findNode('pkg.api2') + self.assertIsInstance(node, modulegraph.InvalidSourceModule) + + node = mf.findNode('pkg.api3') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = mf.findNode('http.client') + self.assertIsInstance(node, modulegraph.SourceModule) + + node = mf.findNode('urllib2') + self.assertIs(node, None) + + + + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_relimport2.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_relimport2.py new file mode 100644 index 0000000000000000000000000000000000000000..2df3ee331245230fc12b8dc56a7114e783ffb27f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_relimport2.py @@ -0,0 +1,45 @@ +""" +Test for import machinery +""" +import unittest +import sys +import textwrap +import subprocess +import os +from PyInstaller.lib.modulegraph import modulegraph + +class TestModuleGraphImport (unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def setUp(self): + self.root = os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport2') + self.mf = modulegraph.ModuleGraph(path=[ self.root ] + sys.path) + + + def test_init_as_script(self): + self.mf.run_script(os.path.join(self.root, 'pkg/__init__.py')) + n = self.mf.findNode('mod1') + self.assertIs(n, None) + + n = self.mf.findNode('.mod2.*') + self.assertIsInstance(n, modulegraph.InvalidRelativeImport) + + def test_subpkg_bad_import(self): + self.mf.import_hook('pkg.sub') + + n = self.mf.findNode('toplevel') + self.assertIs(n, None) + + n = self.mf.findNode('pkg.mod1') + self.assertIsInstance(n, modulegraph.SourceModule) + + n = self.mf.findNode('pkg.mod3') + self.assertIsInstance(n, modulegraph.SourceModule) + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_setuptools_nspkg.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_setuptools_nspkg.py new file mode 100644 index 0000000000000000000000000000000000000000..88afd3977b423dacbe9e821d8786619e95403f25 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_setuptools_nspkg.py @@ -0,0 +1,120 @@ +""" +Tests that deal with setuptools namespace +packages, and in particular the installation +flavour used by pip +""" +import os +import sys +import subprocess +import unittest +import textwrap +import shutil + +import pytest + +from PyInstaller.lib.modulegraph import modulegraph + +gRootDir = os.path.dirname(os.path.abspath(__file__)) +gSrcDir = os.path.join(gRootDir, 'testpkg-setuptools-namespace') + + +@pytest.fixture +def install_testpkg(tmpdir): + # Copy the package to ``dest_dir``, so that the build won't modify anything in ``gSrcDir``. + dest_dir = str(tmpdir / 'data') + shutil.copytree(gSrcDir, dest_dir) + + # A directory to place the resulting built library in. + libdir = str(tmpdir / 'test') + + # Perform the build. + subprocess.check_call([ + sys.executable, 'setup.py', 'install', + '--install-lib', libdir, + '--single-version-externally-managed', + '--record', os.path.join(libdir, 'record.lst'), + ], cwd=dest_dir) + + return libdir + + +class TestPythonBehaviour(object): + + def importModule(self, name, libdir): + if '.' in name: + script = textwrap.dedent("""\ + import site + site.addsitedir(%r) + try: + import %s + except ImportError: + import %s + print (%s.__name__) + """) % (str(libdir), name, name.rsplit('.', 1)[0], name) + else: + script = textwrap.dedent("""\ + import site + site.addsitedir(%r) + import %s + print (%s.__name__) + """) % (str(libdir), name, name) + + data = subprocess.check_output( + [sys.executable, '-c', script], + stderr=subprocess.STDOUT, + cwd=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testpkg-relimport'), + ) + if sys.version_info[0] != 2: + data = data.decode('UTF-8') + data = data.strip() + if data.endswith(' refs]'): + data = data.rsplit('\n', 1)[0].strip() + + return data + + def testToplevel(self, install_testpkg): + m = self.importModule('nspkg.module', install_testpkg) + assert m == 'nspkg.module' + + def testSub(self, install_testpkg): + m = self.importModule('nspkg.nssubpkg.sub', install_testpkg) + assert m == 'nspkg.nssubpkg.sub' + + +@pytest.fixture +def install_testpkg_modulegraph(install_testpkg): + return modulegraph.ModuleGraph(path=[str(install_testpkg)] + sys.path) + + +class TestModuleGraphImport(object): + + def testRootPkg(self, install_testpkg_modulegraph): + install_testpkg_modulegraph.import_hook('nspkg') + + node = install_testpkg_modulegraph.findNode('nspkg') + assert isinstance(node, modulegraph.NamespacePackage) + assert node.identifier == 'nspkg' + assert node.filename == '-' + + def testRootPkgModule(self, install_testpkg_modulegraph): + install_testpkg_modulegraph.import_hook('nspkg.module') + + node = install_testpkg_modulegraph.findNode('nspkg.module') + assert isinstance(node, modulegraph.SourceModule) + assert node.identifier == 'nspkg.module' + + def testSubRootPkgModule(self, install_testpkg_modulegraph): + install_testpkg_modulegraph.import_hook('nspkg.nssubpkg.sub') + + node = install_testpkg_modulegraph.findNode('nspkg.nssubpkg.sub') + assert isinstance(node, modulegraph.SourceModule) + assert node.identifier == 'nspkg.nssubpkg.sub' + + node = install_testpkg_modulegraph.findNode('nspkg') + assert isinstance(node, modulegraph.NamespacePackage) + + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_swig.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_swig.py new file mode 100644 index 0000000000000000000000000000000000000000..51a0d6c2be2e8730a9b5e25f1013e937fbc178c1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_swig.py @@ -0,0 +1,40 @@ +""" +Test importability of SWIG-generated C extensions. +""" +import os +import sys +from PyInstaller.lib.modulegraph import modulegraph + +if sys.version_info[:2] <= (2,6): + import unittest2 as unittest +else: + import unittest + +class TestSWIGImportability(unittest.TestCase): + if not hasattr(unittest.TestCase, 'assertIsInstance'): + def assertIsInstance(self, value, types): + if not isinstance(value, types): + self.fail("%r is not an instance of %r"%(value, types)) + + def test_swig_importability(self): + # Absolute path of the top-level data directory for this unit test. + test_dir = os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'testpkg-swig') + + # Mock module graph relative to this directory. + module_graph = modulegraph.ModuleGraph(path=[ test_dir ] + sys.path) + + # Graph node corresponding to a mock SWIG module. + swig_module = module_graph._safe_import_hook( + 'pkg.sample', source_module=None, target_attr_names=())[0] + self.assertIsInstance(swig_module, modulegraph.SourceModule) + + # Graph node corresponding to a mock SWIG C extension imported by the + # prior module. While this should technically be a C extension rather + # than a module, reliably testing the latter in a cross-platform manner + # is both non-trivial and gains us relatively little over this approach. + swig_c_extension = module_graph.findNode('pkg._sample') + self.assertIsInstance(swig_c_extension, modulegraph.SourceModule) + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_util.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_util.py new file mode 100644 index 0000000000000000000000000000000000000000..6a88570ccd3e8392b57fae2b0f3c21d2d5308b06 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_util.py @@ -0,0 +1,69 @@ +import unittest +import encodings +import encodings.aliases +from PyInstaller.lib.modulegraph import util +import sys +import os + +try: + from io import BytesIO +except ImportError: + from cStringIO import StringIO as BytesIO + +class TestUtil (unittest.TestCase): + def assertStartsWith(self, a, b, message=None): + if not a.startswith(b): + if message is None: + message = "%r does not start with %r"%(a, b) + self.fail(message) + + def assertSamePath(self, a, b): + self.assertStartsWith(os.path.realpath(a), os.path.realpath(b)) + + def test_imp_find_module(self): + fn = util.imp_find_module('encodings.aliases')[1] + self.assertSamePath(encodings.aliases.__file__, fn) + + def test_imp_walk(self): + imps = list(util.imp_walk('encodings.aliases')) + self.assertEqual(len(imps), 2) + + self.assertEqual(imps[0][0], 'encodings') + self.assertSamePath(encodings.__file__, imps[0][1][1]) + + self.assertEqual(imps[1][0], 'aliases') + self.assertSamePath(encodings.aliases.__file__, imps[1][1][1]) + + # Close all files, avoid warning by unittest + for i in imps: + if i[1][0] is not None: + i[1][0].close() + + + def test_guess_encoding(self): + fp = BytesIO(b"# coding: utf-8") + self.assertEqual(util.guess_encoding(fp), "utf-8") + + fp = BytesIO(b"\n# coding: utf-8") + self.assertEqual(util.guess_encoding(fp), "utf-8") + + fp = BytesIO(b"# coding: latin-1") + self.assertEqual(util.guess_encoding(fp), "latin-1") + + fp = BytesIO(b"\n# coding: latin-1") + self.assertEqual(util.guess_encoding(fp), "latin-1") + + fp = BytesIO(b"#!/usr/bin/env/python\n# vim: set fileencoding=latin-1 :") + self.assertEqual(util.guess_encoding(fp), "latin-1") + + fp = BytesIO(b"\n\n\n# coding: latin-1") + if sys.version_info[0] == 2: + self.assertEqual(util.guess_encoding(fp), "ascii") + else: + self.assertEqual(util.guess_encoding(fp), "utf-8") + + del fp + + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_zipio.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_zipio.py new file mode 100644 index 0000000000000000000000000000000000000000..7d818c782706fe580f42f574a6f98e9bc40877d2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/test_zipio.py @@ -0,0 +1,255 @@ +from PyInstaller.lib.modulegraph import zipio +import os +import time +import sys +import stat + +if sys.version_info[:2] <= (2,6): + import unittest2 as unittest + +else: + import unittest + +TESTDATA=os.path.join( + os.path.dirname(os.path.abspath(__file__)), + 'testdata') + +class TestModuleGraph (unittest.TestCase): + def test_locating(self): + # Private function + # According to POSIX 'sh' has to be searched on $PATH, see + # http://pubs.opengroup.org/onlinepubs/007904875/utilities/sh.html + # Also try to find a program for Windows. + from distutils.spawn import find_executable + for name in ('sh', 'bash', 'cmd'): + prog = find_executable(name) + if prog: + break + self.assertIsNot(prog, None) + for suffix, part in ( + ('', None), + ('/bar', 'bar'), + ('/foo/bar///bar/', 'foo/bar/bar'), + ('///foo/bar///bar/', 'foo/bar/bar')): + self.assertEqual(zipio._locate(prog+suffix), (prog, part)) + self.assertRaises(IOError, zipio._locate, '/usr/bin/sh.bar') + self.assertRaises(IOError, zipio._locate, '/foo/bar/baz.txt') + + def test_open(self): + # 1. Regular file + with zipio.open(os.path.join(TESTDATA, 'test.txt'), 'r') as fp: + data = fp.read() + self.assertEqual(data, 'This is test.txt\n') + + if sys.version_info[0] == 3: + with zipio.open(os.path.join(TESTDATA, 'test.txt'), 'rb') as fp: + data = fp.read() + self.assertEqual(data, b'This is test.txt\n') + + # 2. File inside zipfile + with zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'r') as fp: + data = fp.read() + self.assertEqual(data, 'Zipped up test.txt\n') + + if sys.version_info[0] == 3: + with zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'rb') as fp: + data = fp.read() + self.assertEqual(data, b'Zipped up test.txt\n') + + # 3. EXC: Directory inside zipfile + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir')) + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir2')) + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir2/subdir')) + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir3')) + # TODO: Add subdir4/file.txt, without directory entry + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir4')) + + # 4. EXC: No such file in zipfile + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file')) + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'zipped.egg', 'subdir/no-such-file')) + + # 5. EXC: No such regular file + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'no-such-file.txt')) + + # 6. EXC: Open r/w + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'w') + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'a') + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'r+') + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'w+') + self.assertRaises(IOError, zipio.open, os.path.join(TESTDATA, 'test.txt'), 'a+') + + def test_listdir(self): + # 1. Regular directory + self.assertEqual(set(os.listdir(os.path.join(TESTDATA, 'subdir'))), set(['file1.txt', 'file2.txt'])) + + # 2. Zipfile with files in directory + self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg'))), set([ + 'test.txt', 'subdir', 'subdir2', 'subdir3', 'subdir4'])) + + # 3. Zipfile with files in subdirectory + self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir'))), set(['file1.txt', 'file2.txt'])) + self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir2'))), set(['subdir'])) + self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir4', 'subdir6'))), set(['mydir'])) + + # 4. Zipfile with entry for directory, no files + self.assertEqual(set(zipio.listdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir3'))), set([])) + + # 5. EXC: Zipfile without directory + self.assertRaises(IOError, zipio.listdir, os.path.join(TESTDATA, 'zipped.egg', 'subdir10')) + + # 6. EXC: Regular directory doesn't exist + self.assertRaises(IOError, zipio.listdir, os.path.join(TESTDATA, 'subdir10')) + + def test_isfile(self): + self.assertTrue(zipio.isfile(os.path.join(TESTDATA, 'test.txt'))) + self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'subdir'))) + self.assertRaises(IOError, zipio.isfile, os.path.join(TESTDATA, 'no-such-file')) + self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg'))) + self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'subdir4'))) + self.assertTrue(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'))) + self.assertFalse(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'subdir'))) + self.assertRaises(IOError, zipio.isfile, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file')) + self.assertTrue(zipio.isfile(os.path.join(TESTDATA, 'zipped.egg', 'subdir2', 'subdir', 'file1.txt'))) + + def test_isdir(self): + self.assertTrue(zipio.isdir(TESTDATA)) + self.assertFalse(zipio.isdir(os.path.join(TESTDATA, 'test.txt'))) + self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg'))) + self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir'))) + self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir2/subdir'))) + self.assertTrue(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir4'))) + self.assertFalse(zipio.isdir(os.path.join(TESTDATA, 'zipped.egg', 'subdir4', 'file.txt'))) + self.assertRaises(IOError, zipio.isdir, os.path.join(TESTDATA, 'no-such-file')) + self.assertRaises(IOError, zipio.isdir, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file')) + self.assertRaises(IOError, zipio.isdir, os.path.join(TESTDATA, 'zipped.egg', 'subdir', 'no-such-file')) + + @unittest.skipUnless(hasattr(os, "symlink"), + "os.symlink is not available") + def test_islink(self): + fn = os.path.join(TESTDATA, 'symlink') + os.symlink('test.txt', fn) + try: + self.assertTrue(zipio.islink(fn)) + + finally: + os.unlink(fn) + + self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'test.txt'))) + self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'subdir'))) + self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg'))) + self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/subdir'))) + self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/subdir4'))) + self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/test.txt'))) + self.assertFalse(zipio.islink(os.path.join(TESTDATA, 'zipped.egg/subdir/file1.txt'))) + + self.assertRaises(IOError, zipio.islink, os.path.join(TESTDATA, 'no-such-file')) + self.assertRaises(IOError, zipio.islink, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file')) + + @unittest.skipUnless(hasattr(os, "symlink"), + "os.symlink is not available") + def test_readlink(self): + fn = os.path.join(TESTDATA, 'symlink') + os.symlink('test.txt', fn) + try: + self.assertEqual(zipio.readlink(fn), 'test.txt') + + finally: + os.unlink(fn) + + self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'test.txt')) + self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'subdir')) + self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg')) + self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg', 'subdir4')) + self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg', 'no-such-file')) + self.assertRaises(OSError, zipio.readlink, os.path.join(TESTDATA, 'zipped.egg', 'subdir/no-such-file')) + + def test_getmode(self): + fn = os.path.join(TESTDATA, 'test.txt') + self.assertEqual(stat.S_IMODE(os.stat(fn).st_mode), zipio.getmode(fn)) + + # XXX: Not too happy about this... + fn = os.path.join(TESTDATA, 'zipped.egg') + self.assertEqual(stat.S_IMODE(os.stat(fn).st_mode), zipio.getmode(fn)) + + + fn = os.path.join(TESTDATA, 'zipped.egg/test.txt') + mode = zipio.getmode(fn) + self.assertEqual(mode, 0o644) + + fn = os.path.join(TESTDATA, 'zipped.egg/subdir') + mode = zipio.getmode(fn) + self.assertEqual(mode, 0o755) + + fn = os.path.join(TESTDATA, 'zipped.egg/subdir4') + self.assertEqual(zipio.getmode(fn), stat.S_IMODE(zipio._DFLT_DIR_MODE)) + + self.assertRaises(IOError, zipio.getmode, os.path.join(TESTDATA, 'no-file')) + self.assertRaises(IOError, zipio.getmode, os.path.join(TESTDATA, 'zipped.egg/no-file')) + + + def test_getmtime(self): + fn = os.path.join(TESTDATA, 'test.txt') + self.assertEqual(os.path.getmtime(fn), zipio.getmtime(fn)) + + fn = os.path.join(TESTDATA, 'zipped.egg') + self.assertEqual(os.path.getmtime(fn), zipio.getmtime(fn)) + + fn = os.path.join(TESTDATA, 'zipped.egg/test.txt') + mtime = zipio.getmtime(fn) + self.assertEqual(time.mktime((2011, 3, 15, 13, 54, 40, 0, 0, -1)), mtime) + + fn = os.path.join(TESTDATA, 'zipped.egg/subdir') + mtime = zipio.getmtime(fn) + self.assertEqual(time.mktime((2011, 3, 15, 13, 58, 10, 0, 0, -1)), mtime) + + fn = os.path.join(TESTDATA, 'zipped.egg/subdir4') + self.assertEqual(zipio.getmtime(fn), os.path.getmtime(os.path.join(TESTDATA, 'zipped.egg'))) + + self.assertRaises(IOError, zipio.getmtime, os.path.join(TESTDATA, 'no-file')) + self.assertRaises(IOError, zipio.getmtime, os.path.join(TESTDATA, 'zipped.egg/no-file')) + + def test_contextlib(self): + # 1. Regular file + with zipio.open(os.path.join(TESTDATA, 'test.txt'), 'r') as fp: + data = fp.read() + try: + fp.read() + self.fail("file not closed") + except (ValueError, IOError): + pass + + self.assertEqual(data, 'This is test.txt\n') + + if sys.version_info[0] == 3: + with zipio.open(os.path.join(TESTDATA, 'test.txt'), 'rb') as fp: + data = fp.read() + try: + fp.read() + self.fail("file not closed") + except (ValueError, IOError): + pass + + self.assertEqual(data, b'This is test.txt\n') + + # 2. File inside zipfile + with zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'r') as fp: + data = fp.read() + try: + fp.read() + self.fail("file not closed") + except (ValueError, IOError): + pass + self.assertEqual(data, 'Zipped up test.txt\n') + + if sys.version_info[0] == 3: + with zipio.open(os.path.join(TESTDATA, 'zipped.egg', 'test.txt'), 'rb') as fp: + data = fp.read() + try: + fp.read() + self.fail("file not closed") + except (IOError, ValueError): + pass + self.assertEqual(data, b'Zipped up test.txt\n') + +if __name__ == "__main__": + unittest.main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..6ee4926a7f343889574ad910e0b243507a839cbb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/child/nameduser-1.5-py2.6-nspkg.pth @@ -0,0 +1 @@ +import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',types.ModuleType('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..6ee4926a7f343889574ad910e0b243507a839cbb --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/parent/namedpkg-1.0-py2.6-nspkg.pth @@ -0,0 +1 @@ +import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',types.ModuleType('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.10/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..7654ab0e93ab6bea251ef5983606ebc3990b2487 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/child/nameduser-1.5-py2.5-nspkg.pth @@ -0,0 +1 @@ +import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..7654ab0e93ab6bea251ef5983606ebc3990b2487 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/parent/namedpkg-1.0-py2.5-nspkg.pth @@ -0,0 +1 @@ +import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/distribute-0.6.12/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..7654ab0e93ab6bea251ef5983606ebc3990b2487 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/child/nameduser-1.5-py2.5-nspkg.pth @@ -0,0 +1 @@ +import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..7654ab0e93ab6bea251ef5983606ebc3990b2487 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/parent/namedpkg-1.0-py2.5-nspkg.pth @@ -0,0 +1 @@ +import sys,new,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('namedpkg',new.module('namedpkg')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-0.6c9/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/child/nameduser-1.5-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/child/nameduser-1.5-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..5f484b8265ec1e2c37e98e7b658979125f21821a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/child/nameduser-1.5-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/parent/namedpkg-1.0-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/parent/namedpkg-1.0-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..5f484b8265ec1e2c37e98e7b658979125f21821a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/parent/namedpkg-1.0-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.1.0/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/child/nameduser-1.5-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/child/nameduser-1.5-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..b849525b6f86a90825a8a6f5b2a302bed71d59df --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/child/nameduser-1.5-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;pep420 = sys.version_info > (3, 3);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and not pep420 and sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/parent/namedpkg-1.0-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/parent/namedpkg-1.0-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..b849525b6f86a90825a8a6f5b2a302bed71d59df --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/parent/namedpkg-1.0-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;pep420 = sys.version_info > (3, 3);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));ie = os.path.exists(os.path.join(p,'__init__.py'));m = not ie and not pep420 and sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-28.7.0/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/child/nameduser-1.5-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/child/nameduser-1.5-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..55d905a0ef69cf643cfc97dc0240f7c5200a8605 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/child/nameduser-1.5-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('namedpkg', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('namedpkg', [os.path.dirname(p)])));m = m or not has_mfs and sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/parent/namedpkg-1.0-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/parent/namedpkg-1.0-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..55d905a0ef69cf643cfc97dc0240f7c5200a8605 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/parent/namedpkg-1.0-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('namedpkg', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('namedpkg', [os.path.dirname(p)])));m = m or not has_mfs and sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-31.0.0/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/child/nameduser-1.5-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/child/nameduser-1.5-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..3a216531ff4e09ff4d834c698afd1acb041c48bf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/child/nameduser-1.5-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('namedpkg', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('namedpkg', [os.path.dirname(p)])));m = m or sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/parent/namedpkg-1.0-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/parent/namedpkg-1.0-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..3a216531ff4e09ff4d834c698afd1acb041c48bf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/parent/namedpkg-1.0-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('namedpkg', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('namedpkg', [os.path.dirname(p)])));m = m or sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-34.3.0/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/child/nameduser-1.5-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/child/nameduser-1.5-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..3a216531ff4e09ff4d834c698afd1acb041c48bf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/child/nameduser-1.5-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('namedpkg', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('namedpkg', [os.path.dirname(p)])));m = m or sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/parent/namedpkg-1.0-py2.7-nspkg.pth b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/parent/namedpkg-1.0-py2.7-nspkg.pth new file mode 100644 index 0000000000000000000000000000000000000000..3a216531ff4e09ff4d834c698afd1acb041c48bf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/parent/namedpkg-1.0-py2.7-nspkg.pth @@ -0,0 +1 @@ +import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('namedpkg',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('namedpkg', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('namedpkg', [os.path.dirname(p)])));m = m or sys.modules.setdefault('namedpkg', types.ModuleType('namedpkg'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/setuptools-40.4.3/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/namedpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/namedpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4d0b94e2ae0b89f38bf5a1bb1e2b33afc74f7138 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/namedpkg/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/namedpkg/slave.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/namedpkg/slave.py new file mode 100644 index 0000000000000000000000000000000000000000..7969da927d58bd9cafe7db9926e68f7c878b0e2b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/namedpkg/slave.py @@ -0,0 +1,2 @@ +""" slave packages """ +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/setup.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..924b4fc57caa16a0c31c0500239f85080a7e1553 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/child/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup + +setup( + name="nameduser", + version="1.5", + packages=["namedpkg"], + namespace_packages=["namedpkg"], +) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/install.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/install.py new file mode 100644 index 0000000000000000000000000000000000000000..ada59d54c6d8e64e78d3630c0f32bac784b381b9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/install.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +""" +Script that will create a subdirectory one level up with two subdirs +with --single-version-externally-managed namespace packages. + +Use this script with new versions of distribute and setuptools to ensure +that changes in the handling of this option don't break us. +""" +import pkg_resources +import subprocess +import os +import sys +import shutil + +def main(): + r = pkg_resources.require('setuptools')[0] + install_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "%s-%s"%(r.project_name, r.version)) + if os.path.exists(install_dir): + print("Skip %s %s: already installed"%(r.project_name, r.version)) + + else: + os.mkdir(install_dir) + os.mkdir(os.path.join(install_dir, "parent")) + os.mkdir(os.path.join(install_dir, "child")) + + if os.path.exists('parent/build'): + shutil.rmtree('parent/build') + if os.path.exists('child/build'): + shutil.rmtree('child/build') + + for subdir in ('parent', 'child'): + p = subprocess.Popen([ + sys.executable, + "setup.py", + "install", + "--install-lib=%s/%s"%(install_dir, subdir), + "--single-version-externally-managed", + "--record", "files.txt" + ], + cwd=subdir) + xit = p.wait() + if xit != 0: + print("ERROR: install failed") + sys.exit(1) + + + if os.path.exists('%s/files.txt'%(subdir,)): + os.unlink('%s/files.txt'%(subdir,)) + + +if __name__ == "__main__": + main() diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/namedpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/namedpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4d0b94e2ae0b89f38bf5a1bb1e2b33afc74f7138 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/namedpkg/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/namedpkg/parent.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/namedpkg/parent.py new file mode 100644 index 0000000000000000000000000000000000000000..6422cac0c48876490e4cb2b448feba51221f5d8e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/namedpkg/parent.py @@ -0,0 +1,2 @@ +""" parent packages """ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/setup.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..f47327e2834a8920a8ae13e1c4e01aeef1fba9f1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/nspkg/src/parent/setup.py @@ -0,0 +1,8 @@ +from setuptools import setup + +setup( + name="namedpkg", + version="1.0", + packages=["namedpkg"], + namespace_packages=["namedpkg"], +) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/script b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/script new file mode 100644 index 0000000000000000000000000000000000000000..d0586ca4327d543ff5932fbab4c56ec97bac5462 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/script @@ -0,0 +1,4 @@ +#!/usr/bin/python +import sys, os + +print (sys.version) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/subdir/file1.txt b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/subdir/file1.txt new file mode 100644 index 0000000000000000000000000000000000000000..533790e525dfeb785a02edfceeb1c7d120972c0d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/subdir/file1.txt @@ -0,0 +1 @@ +a diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/subdir/file2.txt b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/subdir/file2.txt new file mode 100644 index 0000000000000000000000000000000000000000..485540d7ad7473f697234cebe0b55016c5dc1b40 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/subdir/file2.txt @@ -0,0 +1 @@ +b diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath.egg b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath.egg new file mode 100644 index 0000000000000000000000000000000000000000..64db323d834d65101677e577647e9590f8e1be1f Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath.zip b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath.zip new file mode 100644 index 0000000000000000000000000000000000000000..d3c1f423d31730ba5f23ddf38c67ae43f17757ea Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath.zip differ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/myext.pyd b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/myext.pyd new file mode 100644 index 0000000000000000000000000000000000000000..87248f74e980003466778a2dcc0464591833b0ec --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/myext.pyd @@ -0,0 +1 @@ +""" fake extension """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/myext.so b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/myext.so new file mode 100644 index 0000000000000000000000000000000000000000..87248f74e980003466778a2dcc0464591833b0ec --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/myext.so @@ -0,0 +1 @@ +""" fake extension """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mymodule.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mymodule.py new file mode 100644 index 0000000000000000000000000000000000000000..031635f9a8c4e4de0399c93b1d4656e67b525b74 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mymodule.py @@ -0,0 +1,3 @@ +""" +some module +""" diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mymodule3.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mymodule3.py new file mode 100644 index 0000000000000000000000000000000000000000..0b4c44b17b5f783c888d85cf5ecd009da619bb1c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mymodule3.py @@ -0,0 +1 @@ +""" fake module """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mypkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mypkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1ad960bdfd3943954d9a543a863016ff702dcc74 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/syspath/mypkg/__init__.py @@ -0,0 +1 @@ +""" fake package """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/test.egg b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/test.egg new file mode 100644 index 0000000000000000000000000000000000000000..219c1165e1584b1aca881acfe5fca0a5fa92fd81 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/test.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/test.txt b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/test.txt new file mode 100644 index 0000000000000000000000000000000000000000..3b862323195f9e7321da22d618da1b64f6b93554 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/test.txt @@ -0,0 +1 @@ +This is test.txt diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/zipped.egg b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/zipped.egg new file mode 100644 index 0000000000000000000000000000000000000000..bf8bd0930c828907a78c8e56032de0faf5c81c85 Binary files /dev/null and b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testdata/zipped.egg differ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..731d74061499abe240be309298584a06ed73545e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/__init__.py @@ -0,0 +1 @@ +""" pkg """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api.py new file mode 100644 index 0000000000000000000000000000000000000000..889ea13ed263d95e573a8e4e6feb2acce1c5a7a9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api.py @@ -0,0 +1,9 @@ +""" pkg.api """ + +import sys + +if sys.version_info[0] == 2: + from .api2 import * + +else: + from .api3 import * diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api2.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api2.py new file mode 100644 index 0000000000000000000000000000000000000000..fc727b1a73a12d45829a3f4d0d66c87f905221fe --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api2.py @@ -0,0 +1,11 @@ +import urllib2 + +def div(a, b): + try: + return a/b + + except ZeroDivisionError, exc: + return None + +class MyClass (object): + pass diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api3.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api3.py new file mode 100644 index 0000000000000000000000000000000000000000..71a78ecf21f47b438da2c6f11e6e29b46f0737c2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compatmodule/pkg/api3.py @@ -0,0 +1,11 @@ +import http.client + +def div(a, b): + try: + return a/b + + except ZeroDivisionError as exc: + return None + +class MyClass (object, metaclass=type): + pass diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/compiled/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/compiled/script.py new file mode 100644 index 0000000000000000000000000000000000000000..8d28e3010c8c1b30581b8ecb3024a997235ef969 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/compiled/script.py @@ -0,0 +1 @@ +import mod1, mod4 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod1.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod1.py new file mode 100644 index 0000000000000000000000000000000000000000..e65af9a71e99fc7580de3e6631ce7525e85d52fd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod1.py @@ -0,0 +1,4 @@ +import mod2 +import mod3 + +foo = 1 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod2.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod2.py new file mode 100644 index 0000000000000000000000000000000000000000..f73c04148ba23428551efc61307691d5921dd12a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod2.py @@ -0,0 +1,6 @@ +import mod1 +import sys + +def testme(): + global bar + bar = 42 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod3.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod3.py new file mode 100644 index 0000000000000000000000000000000000000000..80277c51455fb58f03e68554e89f1e4c79fed819 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod3.py @@ -0,0 +1,2 @@ +import os +from sys import path diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod4.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod4.py new file mode 100644 index 0000000000000000000000000000000000000000..43f3ea8e773ccf5f8bea51ec72051f3fee83db12 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-compiled/source/mod4.py @@ -0,0 +1,2 @@ +from zipfile import * +from math import * diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_class_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_class_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_class_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_conditional_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/function_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..517be24e25e54e08384dd32709e31d7f687a0192 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/__init__.py @@ -0,0 +1 @@ +""" pkg.__init__ """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_class_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_class_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_class_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_conditional_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/function_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_class_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_class_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_class_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_conditional_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/pkg/toplevel_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script.py new file mode 100644 index 0000000000000000000000000000000000000000..e1edfb714bd5779bde74fb54b82a7d63453f7d78 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script.py @@ -0,0 +1,51 @@ + +import toplevel_existing +import toplevel_nonexisting + +class MyClass: + import toplevel_class_existing + import toplevel_class_nonexisting + +if a == b: + import toplevel_conditional_existing + import toplevel_conditional_nonexisting + + try: + import toplevel_conditional_import_existing + import toplevel_conditional_import_nonexisting + except: + import toplevel_conditional_import2_existing + import toplevel_conditional_import2_nonexisting + +try: + import toplevel_import_existing + import toplevel_import_nonexisting +except: + import toplevel_import2_existing + import toplevel_import2_nonexisting + +def function(): + import function_existing + import function_nonexisting + + class MyClass: + import function_class_existing + import function_class_nonexisting + + if a == b: + import function_conditional_existing + import function_conditional_nonexisting + + try: + import function_conditional_import_existing + import function_conditional_import_nonexisting + except: + import function_conditional_import2_existing + import function_conditional_import2_nonexisting + + try: + import function_import_existing + import function_import_nonexisting + except: + import function_import2_existing + import function_import2_nonexisting diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script_from_import.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script_from_import.py new file mode 100644 index 0000000000000000000000000000000000000000..5406526fd53a7746fa9d524cdb2679d4b1a4ed77 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script_from_import.py @@ -0,0 +1,46 @@ +from pkg import toplevel_existing +from pkg import toplevel_nonexisting + +class MyClass: + from pkg import toplevel_class_existing + from pkg import toplevel_class_nonexisting + +if a == b: + from pkg import toplevel_conditional_existing + from pkg import toplevel_conditional_nonexisting + + try: + from pkg import toplevel_conditional_import_existing, toplevel_conditional_import_nonexisting + except: + from pkg import toplevel_conditional_import2_existing + from pkg import toplevel_conditional_import2_nonexisting + +try: + from pkg import toplevel_import_existing, toplevel_import_nonexisting +except: + from pkg import toplevel_import2_existing + from pkg import toplevel_import2_nonexisting + +def function(): + from pkg import function_existing, function_nonexisting + + class MyClass: + from pkg import function_class_existing, function_class_nonexisting + + if a == b: + from pkg import function_conditional_existing + from pkg import function_conditional_nonexisting + + try: + from pkg import function_conditional_import_existing + from pkg import function_conditional_import_nonexisting + except: + from pkg import function_conditional_import2_existing + from pkg import function_conditional_import2_nonexisting + + try: + from pkg import function_import_existing + from pkg import function_import_nonexisting + except: + from pkg import function_import2_existing + from pkg import function_import2_nonexisting diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script_multi_import.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script_multi_import.py new file mode 100644 index 0000000000000000000000000000000000000000..d0418c1ab66b83eb31312ab4299b09b67e0efee8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/script_multi_import.py @@ -0,0 +1,26 @@ + + +try: + import os.path +except ImportError: + pass + +import os + +def function(self): + import sys + + +if a == b: + import sys + +def function2(self): + if a == b: + import platform + +def function3(self): + import platform + import email + + +import email diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_class_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_class_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_class_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_conditional_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_import2_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_import2_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_import2_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_import_existing.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_import_existing.py new file mode 100644 index 0000000000000000000000000000000000000000..136e0dd778f33fb9b1f3f36adc1a8e7e433acdd9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-edgedata/toplevel_import_existing.py @@ -0,0 +1 @@ +""" $fname """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..731d74061499abe240be309298584a06ed73545e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/__init__.py @@ -0,0 +1 @@ +""" pkg """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ac8120f1491d85f7a745b044c90d528dbfd282fd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/__init__.py @@ -0,0 +1,5 @@ +""" pkg.subpkg """ + +from compat import X, Y + +from _collections import A, B diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/_collections.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/_collections.py new file mode 100644 index 0000000000000000000000000000000000000000..072ab7a5a73703c0bece32652bfbd30e6cac7ca0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/_collections.py @@ -0,0 +1,3 @@ +""" pkg.subpkg._collections """ + +A, B = "A", "B" diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/compat.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/compat.py new file mode 100644 index 0000000000000000000000000000000000000000..7caff20426a55c47fc81970f77f0babecada91b0 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg/subpkg/compat.py @@ -0,0 +1,3 @@ +""" pkg.subpkg.compat """ + +X, Y = 1, 2 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8b3b9088579e7c807c54b225081e32301f73daae --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/__init__.py @@ -0,0 +1 @@ +""" pkg2.__init__ """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..69e43afa939839277df4c5b1fc6d9efefae699f8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/__init__.py @@ -0,0 +1,5 @@ +""" pkg2.subpkg """ + +from .compat import X, Y + +from ._collections import A, B diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/_collections.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/_collections.py new file mode 100644 index 0000000000000000000000000000000000000000..e6e8f75ba6bb98b109c6ffd700feb6e95e7fd201 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/_collections.py @@ -0,0 +1,3 @@ +""" pkg2.subpkg._collections """ + +A, B = "A", "B" diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/compat.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/compat.py new file mode 100644 index 0000000000000000000000000000000000000000..96a4da778446bd8647af3f469265e8fb055db1cf --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/pkg2/subpkg/compat.py @@ -0,0 +1,3 @@ +""" pkg2.subpkg.compat """ + +X, Y = 1, 2 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/script.py new file mode 100644 index 0000000000000000000000000000000000000000..9702059113f48e4bb7dd61e32e58a2ec214381c3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-import-from-init/script.py @@ -0,0 +1,2 @@ +import pkg.subpkg +import pkg2.subpkg diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/main_script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/main_script.py new file mode 100644 index 0000000000000000000000000000000000000000..10fa5e45016104355dbe35d5c14d350de93ac580 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/main_script.py @@ -0,0 +1 @@ +import sys diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..47071efeb50a44be3556814ae9f9412c4965732f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/__init__.py @@ -0,0 +1 @@ +""" pkg.init """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub1/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub1/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..78d12036b7d9361a20b3e7edfea95f2dcb8fc4e8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub1/__init__.py @@ -0,0 +1 @@ +""" pkg.sub1.init """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub1/modA.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub1/modA.py new file mode 100644 index 0000000000000000000000000000000000000000..b366c909901e0e78b7c9f36c5564e22b07d8e273 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub1/modA.py @@ -0,0 +1 @@ +""" pkg.sub1.modA """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub2/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e9e66a6c06cbabde95f74dfe626fac4ba4127c26 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub2/__init__.py @@ -0,0 +1 @@ +""" pkg.sub2.init """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub2/mod.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub2/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..e605cb3436b26c5989545ef62f04ad96e0cf0e74 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub2/mod.py @@ -0,0 +1 @@ +""" pkg.sub2.mod """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub3.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub3.py new file mode 100644 index 0000000000000000000000000000000000000000..ec9a5aea21cfec58d168820e9a15d9fc2c37ad41 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-packages/pkg/sub3.py @@ -0,0 +1 @@ +""" pkg.sub3 """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path1/package/sub2.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path1/package/sub2.py new file mode 100644 index 0000000000000000000000000000000000000000..0bfeef52906935d6ee48fd3217df87b43a7c6f29 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path1/package/sub2.py @@ -0,0 +1 @@ +""" package.sub2 """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/nspkg/mod.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/nspkg/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..34bbb7142c09e19e3aca96998034ed19592675c9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/nspkg/mod.py @@ -0,0 +1 @@ +""" package.nspkg.mod """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/sub1.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/sub1.py new file mode 100644 index 0000000000000000000000000000000000000000..e786a687544b2d68ed0889628b482d8b8282316b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/sub1.py @@ -0,0 +1 @@ +""" package.sub1 """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/subpackage/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/subpackage/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f49aa42e776e1c8732b5af608f1306d5a89dfbbd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/subpackage/__init__.py @@ -0,0 +1 @@ +""" package.subpackage """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/subpackage/sub.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/subpackage/sub.py new file mode 100644 index 0000000000000000000000000000000000000000..719a8e690ff456ec6f91aa72a60b9e25dde8e8b2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-pep420-namespace/path2/package/subpackage/sub.py @@ -0,0 +1 @@ +""" package.subpackage.sub """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/main_script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/main_script.py new file mode 100644 index 0000000000000000000000000000000000000000..2a913cc9d09600f25880b635cd487468c95da54b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/main_script.py @@ -0,0 +1 @@ +from pkg import a diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/a.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/a.py new file mode 100644 index 0000000000000000000000000000000000000000..e667e7a903273532442840e8a3c8eef7b0c57522 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/a.py @@ -0,0 +1 @@ +from . import b diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/b.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/b.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr1/pkg/b.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/main_script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/main_script.py new file mode 100644 index 0000000000000000000000000000000000000000..89dd1a5f6803990f84b7b6904c78e9138c4fe8de --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/main_script.py @@ -0,0 +1 @@ +import pkg diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4988b9adea419714650b5f78b11ab2a9070a5a24 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/__init__.py @@ -0,0 +1,9 @@ +""" +Package structure simular to crcmod +""" +try: + from pkg.pkg import * + import pkg.base +except ImportError: + from pkg import * + import base diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/base.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/base.py new file mode 100644 index 0000000000000000000000000000000000000000..bc6ad947abde428c4945dd6e78361666fa1e1234 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/base.py @@ -0,0 +1 @@ +""" package base """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/pkg.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/pkg.py new file mode 100644 index 0000000000000000000000000000000000000000..dddc83e7d554c1276f469aa076cb4836caa99361 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr2/pkg/pkg.py @@ -0,0 +1 @@ +""" nested """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/distutils/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/distutils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/distutils/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/distutils/ccompiler.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/distutils/ccompiler.py new file mode 100644 index 0000000000000000000000000000000000000000..57e80ec7a750d6a0dd726d9efc9500e8e534c3c2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/mypkg/distutils/ccompiler.py @@ -0,0 +1,2 @@ +from distutils.ccompiler import * +from distutils.sysconfig import customize_compiler diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/script.py new file mode 100644 index 0000000000000000000000000000000000000000..cb44c23862af43a7a5d7321c18c5e1062e2594ec --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr3/script.py @@ -0,0 +1 @@ +from mypkg.distutils import ccompiler diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..517be24e25e54e08384dd32709e31d7f687a0192 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/__init__.py @@ -0,0 +1 @@ +""" pkg.__init__ """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d285732cf93750c0d4c8345ef0dcabf108e1c41c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/__init__.py @@ -0,0 +1 @@ +""" pkg.core.__init__ """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/callables.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/callables.py new file mode 100644 index 0000000000000000000000000000000000000000..ba62e78d2b5d2bbee0c68c4321795ac5f8638e55 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/callables.py @@ -0,0 +1,3 @@ +""" pkg.callables """ + +getID, getArgs, getRawFunction, ListenerInadequate, CallArgsInfo = [None]*5 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/listener.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/listener.py new file mode 100644 index 0000000000000000000000000000000000000000..b6b0f168184f50cca84c330927f829d895e86739 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/listener.py @@ -0,0 +1,6 @@ +from .callables import \ + getID, getArgs, getRawFunction,\ + ListenerInadequate, \ + CallArgsInfo + +from .listenerimpl import Listener, ListenerValidator diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/listenerimpl.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/listenerimpl.py new file mode 100644 index 0000000000000000000000000000000000000000..b7617bc2ae761de0fc6b8045e340e2b1d066ad0a --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/pkg/core/listenerimpl.py @@ -0,0 +1,6 @@ +""" pkg.listenerimp """ +class Listener: + pass + +class ListenerValidator: + pass diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/script.py new file mode 100644 index 0000000000000000000000000000000000000000..3d6a517bfec2fb9cd7ff9ebeca961781d7074d17 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr4/script.py @@ -0,0 +1 @@ +from pkg.core.listener import Listener as listen diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr5/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr5/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..3552782bba5c7207949e502887751d621fc396c3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr5/__init__.py @@ -0,0 +1 @@ +""" A dummy __init__ file """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr5/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr5/script.py new file mode 100644 index 0000000000000000000000000000000000000000..881df93cdb861e731c5f2bf744d977c6a37f0208 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr5/script.py @@ -0,0 +1,4 @@ +import __init__ + +from PyInstaller.lib.modulegraph.find_modules import find_needed_modules +import distutils diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr6/module.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr6/module.py new file mode 100644 index 0000000000000000000000000000000000000000..95dc052aa7b7bce8f8e64c90c803a36a066d07f9 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr6/module.py @@ -0,0 +1,1009 @@ + +ds = { + 'name': [ + list(1) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + + list(2) + ] +} + +import os diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr6/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr6/script.py new file mode 100644 index 0000000000000000000000000000000000000000..0c74d40efddb359607f17f5cc7fb19e47cc670f3 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr6/script.py @@ -0,0 +1 @@ +import module diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8656f7e1201f59b6b7990c5da911bba1a7406bd2 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/pkg/__init__.py @@ -0,0 +1 @@ +""" package """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/pkg/mod.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/pkg/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..af5eef5c793cd0df9ce32f17faefff8c5764f55d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/pkg/mod.py @@ -0,0 +1,3 @@ +from .. import sys +from ... import os, xml +from ..foo import bar diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/script.py new file mode 100644 index 0000000000000000000000000000000000000000..c102ee4bc3a42e35150360439b31a223ea18a441 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr7/script.py @@ -0,0 +1 @@ +import pkg.mod as m diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr8/mod.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr8/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..f8e7c9a9568f8e23819c1e0c2a90953d6ef98050 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr8/mod.py @@ -0,0 +1,3 @@ + +async def foo(): + yield 1 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr8/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr8/script.py new file mode 100644 index 0000000000000000000000000000000000000000..cbb78c746748812ad4bde7d603ec070126d963d8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-regr8/script.py @@ -0,0 +1 @@ +import mod diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/mod.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..d7b99723846e0b081738eac55d8df17ce4f348a5 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/mod.py @@ -0,0 +1 @@ +""" Toplevel module """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e15c7d97b7531792fb29a4a69fde80bf0d3dfe52 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/__init__.py @@ -0,0 +1 @@ +""" A Package """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/mod.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..674cee34dcb6015402fc3542d198d7c7475fcf5c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/mod.py @@ -0,0 +1 @@ +""" A package module """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/oldstyle.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/oldstyle.py new file mode 100644 index 0000000000000000000000000000000000000000..cbb78c746748812ad4bde7d603ec070126d963d8 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/oldstyle.py @@ -0,0 +1 @@ +import mod diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/relative.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/relative.py new file mode 100644 index 0000000000000000000000000000000000000000..b654da241705e0aa0d7e35377ab919da01c044a4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/relative.py @@ -0,0 +1,2 @@ +from __future__ import absolute_import +from . import mod diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/relimport.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/relimport.py new file mode 100644 index 0000000000000000000000000000000000000000..7e48a50ac6eb6489ae35d0673665fd84650f9702 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/relimport.py @@ -0,0 +1 @@ +""" pkg.relimport """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/sub2/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/sub2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..31b0be340b4f399104f7e6232dd4f25a9bb84796 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/sub2/__init__.py @@ -0,0 +1 @@ +""" pkg.sub2 """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/sub2/mod.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/sub2/mod.py new file mode 100644 index 0000000000000000000000000000000000000000..e605cb3436b26c5989545ef62f04ad96e0cf0e74 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/sub2/mod.py @@ -0,0 +1 @@ +""" pkg.sub2.mod """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..db371b575fa87d7a0b855a5f8bc3e3169e993d1c --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/__init__.py @@ -0,0 +1 @@ +""" pkg.subpkg """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/mod2.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/mod2.py new file mode 100644 index 0000000000000000000000000000000000000000..4b9a47f3f4315554b45c86e8f3aff5948f1aa483 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/mod2.py @@ -0,0 +1,4 @@ +""" pkg.subpkg.mod2 """ +from __future__ import absolute_import +from ..sub2.mod import __doc__ +from ..sub2 import mod diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/relative.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/relative.py new file mode 100644 index 0000000000000000000000000000000000000000..81338694d38cedb7976c130d60e4649c4a4f12bd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/relative.py @@ -0,0 +1,3 @@ +""" pkg.subpkg.relative """ +from __future__ import absolute_import +from .. import mod diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/relative2.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/relative2.py new file mode 100644 index 0000000000000000000000000000000000000000..56e5a7499fb0f760d39b98a9df289fa298615a5d --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/subpkg/relative2.py @@ -0,0 +1,3 @@ +""" pkg.subpkg.relative """ +from __future__ import absolute_import +from ..relimport import __doc__ as doc diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/toplevel.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/toplevel.py new file mode 100644 index 0000000000000000000000000000000000000000..f07b46691038e440651d148da00378bbb9f0a6c1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/pkg/toplevel.py @@ -0,0 +1,2 @@ +from __future__ import absolute_import +import mod diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/script.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/script.py new file mode 100644 index 0000000000000000000000000000000000000000..4519ac37536c2e3904e2ddb559f3b2b89e1ed86b --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport/script.py @@ -0,0 +1,9 @@ +import mod +import pkg +import pkg.mod +import pkg.oldstyle +import pkg.relative +import pkg.toplevel +import pkg.subpkg.relative +import pkg.subpkg.relative2 +import pkg.subpkg.mod2 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e3affa3213f1aa08463893f84ae18c2d360ef770 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/__init__.py @@ -0,0 +1,2 @@ +from . import mod1 +from .mod2 import * diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod1.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod1.py new file mode 100644 index 0000000000000000000000000000000000000000..654589d878f2fb96dde55a807f37494e431967f7 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod1.py @@ -0,0 +1 @@ +""" mod1 """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod2.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod2.py new file mode 100644 index 0000000000000000000000000000000000000000..b045c260238df1aa2b5ffa2a69c4ced900172357 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod2.py @@ -0,0 +1 @@ +""" mod2 """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod3.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod3.py new file mode 100644 index 0000000000000000000000000000000000000000..1085bb020158551471ef4743720a32013e518d01 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/mod3.py @@ -0,0 +1 @@ +""" mod3 """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/sub/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/sub/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..ea0e2305e9f9c81010a53af43a5fe15006f62921 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/pkg/sub/__init__.py @@ -0,0 +1,3 @@ +from .. import mod1 +from .. import mod3 +from ... import toplevel diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/toplevel.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/toplevel.py new file mode 100644 index 0000000000000000000000000000000000000000..5a67ac6eb2bfc9b979f844381791ce8961c31b9f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-relimport2/toplevel.py @@ -0,0 +1 @@ +""" toplevel """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/setup.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..4330c603fe4586a13b8b6d05029378965f28c7ba --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup + +setup( + name="nspkg", + version="1.0", + namespace_packages=['nspkg', 'nspkg.nssubpkg'], + packages=['nspkg', 'nspkg.nssubpkg'], + package_dir = {'': 'src'}, + zip_safe=False, +) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c2c4975d8c1aabfc59bf8e8ab3889f844b16eebd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/__init__.py @@ -0,0 +1,7 @@ +# this is a namespace package +try: + import pkg_resources + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/module.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/module.py new file mode 100644 index 0000000000000000000000000000000000000000..f6af00671f3507a37059925e1c019258ff14e39e --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/module.py @@ -0,0 +1 @@ +""" nspkg.module """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/nssubpkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/nssubpkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c2c4975d8c1aabfc59bf8e8ab3889f844b16eebd --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/nssubpkg/__init__.py @@ -0,0 +1,7 @@ +# this is a namespace package +try: + import pkg_resources + pkg_resources.declare_namespace(__name__) +except ImportError: + import pkgutil + __path__ = pkgutil.extend_path(__path__, __name__) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/nssubpkg/sub.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/nssubpkg/sub.py new file mode 100644 index 0000000000000000000000000000000000000000..193d360b938c9169dc0d1b6d9269c85c2cfb7899 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-setuptools-namespace/src/nspkg/nssubpkg/sub.py @@ -0,0 +1 @@ +""" nspkg.nsubpkg.sub """ diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/__init__.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/__init__.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/_sample.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/_sample.py new file mode 100644 index 0000000000000000000000000000000000000000..16e385321a2a15a7efca9bc344b9fdf2daec07e4 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/_sample.py @@ -0,0 +1 @@ +# diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/sample.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/sample.py new file mode 100644 index 0000000000000000000000000000000000000000..767f4776a09d90e18e587b6b72fbac34b0937cdc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph/testpkg-swig/pkg/sample.py @@ -0,0 +1,67 @@ +# This file was automatically generated by SWIG (http://www.swig.org). +# Version 2.0.4 +# +# Do not make changes to this file unless you know what you are doing--modify +# the SWIG interface file instead. + + + +from sys import version_info +if version_info >= (2,6,0): + def swig_import_helper(): + from os.path import dirname + import imp + fp = None + try: + fp, pathname, description = imp.find_module('_sample', [dirname(__file__)]) + except ImportError: + import _sample + return _sample + if fp is not None: + try: + _mod = imp.load_module('_sample', fp, pathname, description) + finally: + fp.close() + return _mod + _sample = swig_import_helper() + del swig_import_helper +else: + import _sample +del version_info +try: + _swig_property = property +except NameError: + pass # Python < 2.2 doesn't have 'property'. +def _swig_setattr_nondynamic(self,class_type,name,value,static=1): + if (name == "thisown"): return self.this.own(value) + if (name == "this"): + if type(value).__name__ == 'SwigPyObject': + self.__dict__[name] = value + return + method = class_type.__swig_setmethods__.get(name,None) + if method: return method(self,value) + if (not static): + self.__dict__[name] = value + else: + raise AttributeError("You cannot add attributes to %s" % self) + +def _swig_setattr(self,class_type,name,value): + return _swig_setattr_nondynamic(self,class_type,name,value,0) + +def _swig_getattr(self,class_type,name): + if (name == "thisown"): return self.this.own() + method = class_type.__swig_getmethods__.get(name,None) + if method: return method(self) + raise AttributeError(name) + +def _swig_repr(self): + try: strthis = "proxy of " + self.this.__repr__() + except: strthis = "" + return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,) + +try: + _object = object + _newclass = 1 +except AttributeError: + class _object : pass + _newclass = 0 diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph_more.py b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph_more.py new file mode 100644 index 0000000000000000000000000000000000000000..92726c6120325dba38531078beeafc8d55e335b1 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_modulegraph_more.py @@ -0,0 +1,667 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 ast +import os +import os.path +import sys +import py_compile +import textwrap +import zipfile +from importlib.machinery import EXTENSION_SUFFIXES + +import pytest + +from PyInstaller.lib.modulegraph import modulegraph +from PyInstaller.utils.tests import xfail + +def _import_and_get_node(tmpdir, module_name, path=None): + script = tmpdir.join('script.py') + script.write('import %s' % module_name) + if path is None: + path = [str(tmpdir)] + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + return mg.findNode(module_name) + + +def test_sourcefile(tmpdir): + tmpdir.join('source.py').write('###') + node = _import_and_get_node(tmpdir, 'source') + assert isinstance(node, modulegraph.SourceModule) + + +def test_invalid_sourcefile(tmpdir): + tmpdir.join('invalid_source.py').write('invalid python-source code') + node = _import_and_get_node(tmpdir, 'invalid_source') + assert isinstance(node, modulegraph.InvalidSourceModule) + + +def test_invalid_compiledfile(tmpdir): + tmpdir.join('invalid_compiled.pyc').write('invalid byte-code') + node = _import_and_get_node(tmpdir, 'invalid_compiled') + assert isinstance(node, modulegraph.InvalidCompiledModule) + + +def test_builtin(tmpdir): + node = _import_and_get_node(tmpdir, 'sys', path=sys.path) + assert isinstance(node, modulegraph.BuiltinModule) + + +def test_extension(tmpdir): + node = _import_and_get_node(tmpdir, '_ctypes', path=sys.path) + assert isinstance(node, modulegraph.Extension) + + +def test_package(tmpdir): + pysrc = tmpdir.join('stuff', '__init__.py') + pysrc.write('###', ensure=True) + node = _import_and_get_node(tmpdir, 'stuff') + assert node.__class__ is modulegraph.Package + assert node.filename in (str(pysrc), str(pysrc)+'c') + assert node.packagepath == [pysrc.dirname] + + +#-- Extension modules + +@pytest.mark.parametrize( + "num, modname, expected_nodetype", ( + # package's __init__ module is an extension + (1, "myextpkg", modulegraph.ExtensionPackage), + # __init__.py beside the __init__ module being an extension + (2, "myextpkg", modulegraph.ExtensionPackage), + # Importing a module beside + (3, "myextpkg.other", modulegraph.Extension), + # sub-package's __init__ module is an extension + (4, "myextpkg.subpkg", modulegraph.ExtensionPackage), + # importing a module beside, but from a sub-package + (5, "myextpkg.subpkg.other", modulegraph.Extension), + )) +def test_package_init_is_extension(tmpdir, num, modname, expected_nodetype): + # Regression: Recursion to deep + + def wt(*args): + f = tmpdir.join(*args) + f.write_text('###', encoding="ascii") + return f + + def create_package_files(test_case): + (tmpdir / 'myextpkg' / 'subpkg').ensure(dir=True) + m = wt('myextpkg', '__init__' + EXTENSION_SUFFIXES[0]) + if test_case == 1: return m # noqa: E701 + wt('myextpkg', '__init__.py') + if test_case == 2: return m # noqa: E701 return extension module anway + m = wt('myextpkg', 'other.py') + m = wt('myextpkg', 'other' + EXTENSION_SUFFIXES[0]) + if test_case == 3: return m # noqa: E701 + m = wt('myextpkg', 'subpkg', '__init__.py') + m = wt('myextpkg', 'subpkg', '__init__' + EXTENSION_SUFFIXES[0]) + if test_case == 4: return m # noqa: E701 + m = wt('myextpkg', 'subpkg', 'other.py') + m = wt('myextpkg', 'subpkg', 'other' + EXTENSION_SUFFIXES[0]) + return m + + module_file = create_package_files(num) + node = _import_and_get_node(tmpdir, modname) + assert node.__class__ is expected_nodetype + if expected_nodetype is modulegraph.ExtensionPackage: + assert node.packagepath == [module_file.dirname] + else: + assert node.packagepath is None # not a package + assert node.filename == str(module_file) + + +#-- Basic tests - these seem to be missing in the original modulegraph +#-- test-suite + +def test_relative_import_missing(tmpdir): + libdir = tmpdir.join('lib') + path = [str(libdir)] + pkg = libdir.join('pkg') + pkg.join('__init__.py').ensure().write('#') + pkg.join('x', '__init__.py').ensure().write('#') + pkg.join('x', 'y', '__init__.py').ensure().write('#') + pkg.join('x', 'y', 'z.py').ensure().write('from . import DoesNotExist') + + script = tmpdir.join('script.py') + script.write('import pkg.x.y.z') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pkg.x.y.z'), modulegraph.SourceModule) + assert isinstance(mg.findNode('pkg.x.y.DoesNotExist'), + modulegraph.MissingModule) + + +#-- Tests with a single module in a zip-file + +def _zip_directory(filename, path): + with zipfile.ZipFile(filename, mode='w') as zfh: + for filename in path.visit(fil='*.py*'): + zfh.write(str(filename), filename.relto(path)) + + +def test_zipped_module_source(tmpdir): + pysrc = tmpdir.join('stuff.py') + pysrc.write('###', ensure=True) + zipfilename = str(tmpdir.join('unstuff.zip')) + _zip_directory(zipfilename, tmpdir) + node = _import_and_get_node(tmpdir, 'stuff', path=[zipfilename]) + assert node.__class__ is modulegraph.SourceModule + assert node.filename.startswith(os.path.join(zipfilename, 'stuff.py')) + + +def test_zipped_module_source_and_compiled(tmpdir): + pysrc = tmpdir.join('stuff.py') + pysrc.write('###', ensure=True) + py_compile.compile(str(pysrc)) + zipfilename = str(tmpdir.join('unstuff.zip')) + _zip_directory(zipfilename, tmpdir) + node = _import_and_get_node(tmpdir, 'stuff', path=[zipfilename]) + # Do not care whether it's source or compiled, as long as it is + # neither invalid nor missing. + assert node.__class__ in (modulegraph.SourceModule, modulegraph.CompiledModule) + assert node.filename.startswith(os.path.join(zipfilename, 'stuff.py')) + + +#-- Tests with a package in a zip-file + +def _zip_package(filename, path): + with zipfile.ZipFile(filename, mode='w') as zfh: + for filename in path.visit(): + zfh.write(str(filename), filename.relto(path.dirname)) + +def test_zipped_package_source(tmpdir): + pysrc = tmpdir.join('stuff', '__init__.py') + pysrc.write('###', ensure=True) + zipfilename = str(tmpdir.join('stuff.zip')) + _zip_package(zipfilename, tmpdir.join('stuff')) + node = _import_and_get_node(tmpdir, 'stuff', path=[zipfilename]) + assert node.__class__ is modulegraph.Package + assert node.packagepath == [os.path.join(zipfilename, 'stuff')] + + +def test_zipped_package_source_and_compiled(tmpdir): + pysrc = tmpdir.join('stuff', '__init__.py') + pysrc.write('###', ensure=True) + py_compile.compile(str(pysrc)) + zipfilename = str(tmpdir.join('stuff.zip')) + _zip_package(zipfilename, tmpdir.join('stuff')) + node = _import_and_get_node(tmpdir, 'stuff', path=[zipfilename]) + assert node.__class__ is modulegraph.Package + assert node.packagepath == [os.path.join(zipfilename, 'stuff')] + + +#-- Namespace packages + +def test_nspackage_pep420(tmpdir): + p1 = tmpdir.join('p1') + p2 = tmpdir.join('p2') + p1.join('stuff', 'a.py').ensure().write('###') + p2.join('stuff', 'b.py').ensure().write('###') + path = [str(p1), str(p2)] + + script = tmpdir.join('script.py') + script.write('import stuff.a, stuff.b') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + + mg.report() + + assert isinstance(mg.findNode('stuff.a'), modulegraph.SourceModule) + assert isinstance(mg.findNode('stuff.b'), modulegraph.SourceModule) + + node = mg.findNode('stuff') + assert isinstance(node, modulegraph.NamespacePackage) + assert node.packagepath == [os.path.join(p, 'stuff') for p in path] + +# :todo: test_namespace_setuptools +# :todo: test_namespace_pkg_resources + + +@pytest.mark.darwin +@pytest.mark.linux +def test_symlinks(tmpdir): + base_dir = tmpdir.join('base').ensure(dir=True) + p1_init = tmpdir.join('p1', '__init__.py').ensure() + p2_init = tmpdir.join('p2', '__init__.py').ensure() + p1_init.write('###') + p2_init.write('###') + + base_dir.join('p1').ensure(dir=True) + + os.symlink(str(p1_init), str(base_dir.join('p1', '__init__.py'))) + os.symlink(str(p2_init), str(base_dir.join('p1', 'p2.py'))) + + node = _import_and_get_node(base_dir, 'p1.p2') + assert isinstance(node, modulegraph.SourceModule) + + +def test_import_order_1(tmpdir): + # Ensure modulegraph processes modules in the same order as Python does. + + class MyModuleGraph(modulegraph.ModuleGraph): + def _load_module(self, fqname, pathname, loader): + if not record or record[-1] != fqname: + record.append(fqname) # record non-consecutive entries + return super(MyModuleGraph, self)._load_module(fqname, + pathname, loader) + + record = [] + + for filename, content in ( + ('a/', 'from . import c, d'), + ('a/c', '#'), + ('a/d/', 'from . import f, g, h'), + ('a/d/f/', 'from . import j, k'), + ('a/d/f/j', '#'), + ('a/d/f/k', '#'), + ('a/d/g/', 'from . import l, m'), + ('a/d/g/l', '#'), + ('a/d/g/m', '#'), + ('a/d/h', '#'), + ('b/', 'from . import e'), + ('b/e/', 'from . import i'), + ('b/e/i', '#')): + if filename.endswith('/'): filename += '__init__' + tmpdir.join(*(filename+'.py').split('/')).ensure().write(content) + + script = tmpdir.join('script.py') + script.write('import a, b') + mg = MyModuleGraph([str(tmpdir)]) + mg.run_script(str(script)) + + # This is the order Python imports these modules given that script. + expected = ['a', + 'a.c', 'a.d', 'a.d.f', 'a.d.f.j', 'a.d.f.k', + 'a.d.g', 'a.d.g.l', 'a.d.g.m', + 'a.d.h', + 'b', 'b.e', 'b.e.i'] + assert record == expected + + +def test_import_order_2(tmpdir): + # Ensure modulegraph processes modules in the same order as Python does. + + class MyModuleGraph(modulegraph.ModuleGraph): + def _load_module(self, fqname, pathname, loader): + if not record or record[-1] != fqname: + record.append(fqname) # record non-consecutive entries + return super(MyModuleGraph, self)._load_module(fqname, + pathname, loader) + + record = [] + + for filename, content in ( + ('a/', '#'), + ('a/c/', '#'), + ('a/c/g', '#'), + ('a/c/h', 'from . import g'), + ('a/d/', '#'), + ('a/d/i', 'from ..c import h'), + ('a/d/j/', 'from .. import i'), + ('a/d/j/o', '#'), + ('b/', 'from .e import k'), + ('b/e/', 'import a.c.g'), + ('b/e/k', 'from .. import f'), + ('b/e/l', 'import a.d.j'), + ('b/f/', '#'), + ('b/f/m', '#'), + ('b/f/n/', '#'), + ('b/f/n/p', 'from ...e import l')): + if filename.endswith('/'): filename += '__init__' + tmpdir.join(*(filename+'.py').split('/')).ensure().write(content) + + script = tmpdir.join('script.py') + script.write('import b.f.n.p') + mg = MyModuleGraph([str(tmpdir)]) + mg.run_script(str(script)) + + # This is the order Python imports these modules given that script. + expected = ['b', 'b.e', + 'a', 'a.c', 'a.c.g', + 'b.e.k', + 'b.f', 'b.f.n', 'b.f.n.p', + 'b.e.l', + 'a.d', 'a.d.j', 'a.d.i', 'a.c.h'] + assert record == expected + print(record) + + +#---- scan bytecode + +def __scan_code(code, use_ast, monkeypatch): + mg = modulegraph.ModuleGraph() + # _process_imports would set _deferred_imports to None + monkeypatch.setattr(mg, '_process_imports', lambda m: None) + module = mg.createNode(modulegraph.Script, 'dummy.py') + + code = textwrap.dedent(code) + if use_ast: + co_ast = compile(code, 'dummy', 'exec', ast.PyCF_ONLY_AST) + co = compile(co_ast, 'dummy', 'exec') + else: + co_ast = None + co = compile(code, 'dummy', 'exec') + mg._scan_code(module, co) + return module + + +@pytest.mark.parametrize("use_ast", (True, False)) +def test_scan_code__empty(monkeypatch, use_ast): + code = "# empty code" + module = __scan_code(code, use_ast, monkeypatch) + assert len(module._deferred_imports) == 0 + assert len(module._global_attr_names) == 0 + + +@pytest.mark.parametrize("use_ast", (True, False)) +def test_scan_code__basic(monkeypatch, use_ast): + code = """ + import os.path + from sys import maxint, exitfunc, platform + del exitfunc + def testfunc(): + import shutil + """ + module = __scan_code(code, use_ast, monkeypatch) + assert len(module._deferred_imports) == 3 + assert ([di[1][0] for di in module._deferred_imports] + == ['os.path', 'sys', 'shutil']) + assert module.is_global_attr('maxint') + assert module.is_global_attr('os') + assert module.is_global_attr('platform') + assert not module.is_global_attr('shutil') # not imported at module level + assert not module.is_global_attr('exitfunc') + + +#-- SWIG packages - pyinstaller specific tests + +def test_swig_import_simple_BUGGY(tmpdir): + libdir = tmpdir.join('lib') + path = [str(libdir)] + osgeo = libdir.join('pyi_test_osgeo') + osgeo.join('__init__.py').ensure().write('#') + osgeo.join('pyi_gdal.py').write('# automatically generated by SWIG\n' + 'import _pyi_gdal') + osgeo.join('_pyi_gdal.py').write('#') + + script = tmpdir.join('script.py') + script.write('from pyi_test_osgeo import pyi_gdal') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + + assert isinstance(mg.findNode('pyi_test_osgeo'), modulegraph.Package) + assert isinstance(mg.findNode('pyi_test_osgeo.pyi_gdal'), + modulegraph.SourceModule) + # The "C" module is frozen under its unqualified rather than qualified + # name. See comment in modulegraph._safe_import_hook. + # BUG: modulegraph contains a probable bug: Only the module's identifier + # is changed, not the module's graphident. Thus the node is still found + # under it's old name. The relevant code was brought from PyInstaller to + # upstream, so this might be PyInstaller's fault. See + # test_swig_import_simple for what it should be. + # This is a separate test-case, not marked as xfail, so we can spot + # whether the SWIG support works at all. + assert isinstance(mg.findNode('pyi_test_osgeo._pyi_gdal'), + modulegraph.SourceModule) + # Due the the buggy implementation, the graphident is unchanged, but + # at least the identifier should have changed. + assert mg.findNode('pyi_test_osgeo._pyi_gdal').identifier \ + == '_pyi_gdal' + # Due the the buggy implementation, this node does not exist. + assert mg.findNode('_pyi_gdal') is None + return mg # for use in test_swig_import_simple_BUG + + +@xfail +def test_swig_import_simple(tmpdir): + # Test the expected (but not implemented) behavior if SWIG support. + mg = test_swig_import_simple_BUGGY(tmpdir) + # Given the bug in modulegraph (see test_swig_import_simple_BUGGY) this is + # what would be the expected behavior. + # TODO: When modulegraph is fixed, merge the two test-cases and correct + # test_swig_import_from_top_level and siblings. + assert mg.findNode('pyi_test_osgeo._pyi_gdal') is None + assert isinstance(mg.findNode('_pyi_gdal'), modulegraph.SourceModule) + + +def test_swig_import_from_top_level(tmpdir): + # While there is a SWIG wrapper module as expected, the package module + # already imports the "C" module in the same way the SWIG wrapper would + # do. + # See the issue #1522 (at about 2017-04-26), pull-request #2578 and commit + # 711e9e77c93a979a63648ba05f725b30dbb7c3cc. + # + # For Python > 2.6, SWIG tries to import the C module from the package's + # directory and if this fails, uses "import _XXX" (which is the code + # triggering import in modulegraph). For Python 2 this is a relative + # import, but for Python 3 this is an absolute import. + # + # In this test-case, the package's __init__.py contains code equivalent to + # the SWIG wrapper-module, causing the C module to be searched as an + # absolute import (in Python 3). But the importing module is not a SWIG + # candidate (names do not match), leading to the (absolute) C module to + # become a MissingModule - which is okay up to this point. Now if the SWIG + # wrapper-module imports the C module, there already is this + # MissingModule, inhibiting modulegraph's SWIG import mechanism. + # + # This is where commit 711e9e77c93 steps in and tries to reimport the C + # module (relative to the SWIG wrapper-module). + libdir = tmpdir.join('lib') + path = [str(libdir)] + osgeo = libdir.join('pyi_test_osgeo') + osgeo.join('__init__.py').ensure().write('import _pyi_gdal') + osgeo.join('pyi_gdal.py').write('# automatically generated by SWIG\n' + 'import _pyi_gdal') + osgeo.join('_pyi_gdal.py').write('#') + + script = tmpdir.join('script.py') + script.write('from pyi_test_osgeo import pyi_gdal') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + + assert isinstance(mg.findNode('pyi_test_osgeo'), modulegraph.Package) + assert isinstance(mg.findNode('pyi_test_osgeo.pyi_gdal'), + modulegraph.SourceModule) + # The "C" module is frozen under its unqualified rather than qualified + # name. See comment in modulegraph._safe_import_hook. + # Due the the buggy implementation (see test_swig_import_simple): + assert isinstance(mg.findNode('pyi_test_osgeo._pyi_gdal'), + modulegraph.SourceModule) + assert mg.findNode('_pyi_gdal') is None + # This would be the correct implementation: + #assert mg.findNode('pyi_test_osgeo._pyi_gdal') is None + #assert isinstance(mg.findNode('_pyi_gdal'), modulegraph.SourceModule) + + +def test_swig_import_from_top_level_missing(tmpdir): + # Like test_swig_import_from_top_level, but the "C" module is missing and + # should be reported as a MissingModule. + libdir = tmpdir.join('lib') + path = [str(libdir)] + osgeo = libdir.join('pyi_test_osgeo') + osgeo.join('__init__.py').ensure().write('import _pyi_gdal') + osgeo.join('pyi_gdal.py').write('# automatically generated by SWIG\n' + 'import _pyi_gdal') + # no module '_pyi_gdal.py' + + script = tmpdir.join('script.py') + script.write('from pyi_test_osgeo import pyi_gdal') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pyi_test_osgeo'), + modulegraph.Package) + assert isinstance(mg.findNode('pyi_test_osgeo.pyi_gdal'), + modulegraph.SourceModule) + # BUG: Again, this is unecpected behaviour in modulegraph: While + # MissingModule('_pyi_gdal') is (arguable) removed when trying to import + # the SWIG C module, there is no MissingModule('pyi_test_osgeo.pyi_gdal') + # added, but again MissingModule('_pyi_gdal'). I still need to understand + # why. + assert mg.findNode('pyi_test_osgeo._pyi_gdal') is None + assert isinstance(mg.findNode('_pyi_gdal'), modulegraph.MissingModule) + + +def test_swig_import_from_top_level_but_nested(tmpdir): + # Like test_swig_import_from_top_level, but both the wrapper and the "top + # level" are nested. This is intented to test relative import of the "C" + # module. + libdir = tmpdir.join('lib') + path = [str(libdir)] + osgeo = libdir.join('pyi_test_osgeo') + osgeo.join('__init__.py').ensure().write('#') + osgeo.join('x', '__init__.py').ensure().write('#') + osgeo.join('x', 'y', '__init__.py').ensure().write('import _pyi_gdal') + osgeo.join('x', 'y', 'pyi_gdal.py').write( + '# automatically generated by SWIG\n' + 'import _pyi_gdal') + osgeo.join('x', 'y', '_pyi_gdal.py').write('#') + + script = tmpdir.join('script.py') + script.write('from pyi_test_osgeo.x.y import pyi_gdal') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + + assert isinstance(mg.findNode('pyi_test_osgeo.x.y.pyi_gdal'), + modulegraph.SourceModule) + # The "C" module is frozen under its unqualified rather than qualified + # name. See comment in modulegraph._safe_import_hook. + # Due the the buggy implementation (see test_swig_import_simple): + assert isinstance(mg.findNode('pyi_test_osgeo.x.y._pyi_gdal'), + modulegraph.SourceModule) + assert mg.findNode('_pyi_gdal') is None + # This would be the correct implementation: + #assert mg.findNode('pyi_test_osgeo.x.y._pyi_gdal') is None + #assert isinstance(mg.findNode('_pyi_gdal'), modulegraph.SourceModule) + + +def test_swig_top_level_but_no_swig_at_all(tmpdir): + # From the script import an absolute module which looks like a SWIG + # candidate but is no SWIG module. See issue #3040 ('_decimal') + # The center of this test-case is that it doesn't raise a recursion too + # deep error. + libdir = tmpdir.join('lib') + path = [str(libdir)] + libdir.join('pyi_dezimal.py').ensure().write('import _pyi_dezimal') + # no module '_pyi_dezimal.py' + + script = tmpdir.join('script.py') + script.write('import pyi_dezimal') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pyi_dezimal'), modulegraph.SourceModule) + assert isinstance(mg.findNode('_pyi_dezimal'), modulegraph.MissingModule) + + +def test_swig_top_level_but_no_swig_at_all_existing(tmpdir): + # Like test_swig_top_level_but_no_swig_at_all, but the "C" module exists. + # The test-case is here for symmetry. + libdir = tmpdir.join('lib') + path = [str(libdir)] + libdir.join('pyi_dezimal.py').ensure().write('import _pyi_dezimal') + libdir.join('_pyi_dezimal.py').ensure().write('#') + + script = tmpdir.join('script.py') + script.write('import pyi_dezimal') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pyi_dezimal'), modulegraph.SourceModule) + assert isinstance(mg.findNode('_pyi_dezimal'), modulegraph.SourceModule) + + +def test_swig_candidate_but_not_swig(tmpdir): + # From a package module import an absolute module which looks like a SWIG + # candidate but is no SWIG module . See issue #2911 (tifffile). + # The center of this test-case is that it doesn't raise a recursion too + # deep error. + libdir = tmpdir.join('lib') + path = [str(libdir)] + pkg = libdir.join('pkg') + pkg.join('__init__.py').ensure().write('from . import mymod') + pkg.join('mymod.py').write('import _mymod') + pkg.join('_mymod.py').write('#') + + script = tmpdir.join('script.py') + script.write('from pkg import XXX') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pkg'), modulegraph.Package) + assert isinstance(mg.findNode('pkg.mymod'), modulegraph.SourceModule) + assert mg.findNode('pkg._mymod') is None + # This is not a SWIG module, thus the SWIG import mechanism should not + # trigger. + assert isinstance(mg.findNode('_mymod'), modulegraph.MissingModule) + + +def test_swig_candidate_but_not_swig2(tmpdir): + """ + Variation of test_swig_candidate_but_not_swig using differnt import + statements (like tifffile/tifffile.py does) + """ + libdir = tmpdir.join('lib') + path = [str(libdir)] + pkg = libdir.join('pkg') + pkg.join('__init__.py').ensure().write('from . import mymod') + pkg.join('mymod.py').write('from . import _mymod\n' + 'import _mymod') + pkg.join('_mymod.py').write('#') + + script = tmpdir.join('script.py') + script.write('from pkg import XXX') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pkg'), modulegraph.Package) + assert isinstance(mg.findNode('pkg.mymod'), modulegraph.SourceModule) + assert isinstance(mg.findNode('pkg._mymod'), modulegraph.SourceModule) + assert isinstance(mg.findNode('_mymod'), modulegraph.MissingModule) + + +def test_swig_candidate_but_not_swig_missing(tmpdir): + # Like test_swig_candidate_but_not_swig, but the "C" module is missing and + # should be reported as a MissingModule. + libdir = tmpdir.join('lib') + path = [str(libdir)] + pkg = libdir.join('pkg') + pkg.join('__init__.py').ensure().write('from . import mymod') + pkg.join('mymod.py').write('import _mymod') + # no module '_mymod.py' + + script = tmpdir.join('script.py') + script.write('import pkg') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pkg'), modulegraph.Package) + assert isinstance(mg.findNode('pkg.mymod'), modulegraph.SourceModule) + assert mg.findNode('pkg._mymod') is None + assert isinstance(mg.findNode('_mymod'), modulegraph.MissingModule) + + +def test_swig_candidate_but_not_swig_missing2(tmpdir): + """ + Variation of test_swig_candidate_but_not_swig_missing using differnt import + statements (like tifffile/tifffile.py does) + """ + libdir = tmpdir.join('lib') + path = [str(libdir)] + pkg = libdir.join('pkg') + pkg.join('__init__.py').ensure().write('from . import mymod') + pkg.join('mymod.py').write('from . import _mymod\n' + 'import _mymod') + # no module '_mymod.py' + + script = tmpdir.join('script.py') + script.write('import pkg') + mg = modulegraph.ModuleGraph(path) + mg.run_script(str(script)) + assert isinstance(mg.findNode('pkg'), modulegraph.Package) + assert isinstance(mg.findNode('pkg.mymod'), modulegraph.SourceModule) + assert isinstance(mg.findNode('pkg._mymod'), modulegraph.MissingModule) + assert isinstance(mg.findNode('_mymod'), modulegraph.MissingModule) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_pyimodulegraph.py b/3rdparty/pyinstaller-4.3/tests/unit/test_pyimodulegraph.py new file mode 100644 index 0000000000000000000000000000000000000000..1a5628a6a5c1c5892c5e82c865e3067f730be182 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_pyimodulegraph.py @@ -0,0 +1,183 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 types +import pytest +import itertools + +from PyInstaller import HOMEPATH +from PyInstaller.depend import analysis +from PyInstaller.lib.modulegraph import modulegraph +import PyInstaller.log as logging +from PyInstaller.utils.tests import gen_sourcefile + + +def test_get_co_using_ctypes(tmpdir): + logging.logger.setLevel(logging.DEBUG) + mg = analysis.PyiModuleGraph(HOMEPATH, excludes=["xencodings"]) + script = tmpdir.join('script.py') + script.write('import ctypes') + script_filename = str(script) + mg.run_script(script_filename) + res = mg.get_co_using_ctypes() + # Script's code object must be in the results + assert script_filename in res + assert isinstance(res[script_filename], types.CodeType), res + + +def test_get_co_using_ctypes_from_extension(): + # If an extension module has an hidden import to ctypes (e.g. added by the + # hook), the extension module must not show up in the result of + # `get_co_using_ctypes()`, since it has no code-object to be analyzed. + # See issue #2492 and test_regression::issue_2492. + logging.logger.setLevel(logging.DEBUG) + mg = analysis.PyiModuleGraph(HOMEPATH, excludes=["xencodings"]) + struct = mg.createNode(modulegraph.Extension, '_struct', 'struct.so') + mg.implyNodeReference(struct, 'ctypes') # simulate the hidden import + res = mg.get_co_using_ctypes() + # _struct must not be in the results + assert '_struct' not in res + + +class FakePyiModuleGraph(analysis.PyiModuleGraph): + def _analyze_base_modules(self): + # suppress this to speed up set-up + self._base_modules = () + + +@pytest.fixture +def fresh_pyi_modgraph(monkeypatch): + """ + Get a fresh PyiModuleGraph + """ + + def fake_base_modules(self): + # speed up set up + self._base_modules = () + + logging.logger.setLevel(logging.DEBUG) + # ensure we get a fresh PyiModuleGraph + monkeypatch.setattr(analysis, "_cached_module_graph_", None) + # speed up setup + monkeypatch.setattr(analysis.PyiModuleGraph, + "_analyze_base_modules", fake_base_modules) + return analysis.initialize_modgraph() + + +def test_cached_graph_is_not_leaking(fresh_pyi_modgraph, monkeypatch, tmpdir): + """ + Ensure cached PyiModulegraph can separate imports between scripts. + """ + mg = fresh_pyi_modgraph + # self-test 1: uuid is not included in the graph by default + src = gen_sourcefile(tmpdir, """print""", test_id="1") + mg.run_script(str(src)) + assert not mg.findNode("uuid") # self-test + + # self-test 2: uuid is available and included when imported + src = gen_sourcefile(tmpdir, """import uuid""", test_id="2") + node = mg.run_script(str(src)) + assert node is not None + names = [n.identifier for n in mg.flatten(start=node)] + assert "uuid" in names + + # the acutal test: uuid is not leaking to the other script + src = gen_sourcefile(tmpdir, """print""", test_id="3") + node = mg.run_script(str(src)) + assert node is not None + names = [n.identifier for n in mg.flatten(start=node)] + assert "uuid" not in names + + +def test_cached_graph_is_not_leaking_hidden_imports(fresh_pyi_modgraph, tmpdir): + """ + Ensure cached PyiModulegraph can separate hidden imports between scripts. + """ + mg = fresh_pyi_modgraph + # self-test 1: skipped here, see test_cached_graph_is_not_leaking + + # self-test 2: uuid is included when hidden imported + src = gen_sourcefile(tmpdir, """print""", test_id="2") + node = mg.run_script(str(src)) + assert node is not None + mg.add_hiddenimports(["uuid"]) + names = [n.identifier for n in mg.flatten(start=node)] + assert "uuid" in names + + # the acutal test: uuid is not leaking to the other script + src = gen_sourcefile(tmpdir, """print""", test_id="3") + node = mg.run_script(str(src)) + assert node is not None + names = [n.identifier for n in mg.flatten(start=node)] + assert "uuid" not in names + + +def test_graph_collects_script_dependencies(fresh_pyi_modgraph, tmpdir): + mg = fresh_pyi_modgraph + # self-test 1: uuid is not included in the graph by default + src1 = gen_sourcefile(tmpdir, """print""", test_id="1") + node = mg.run_script(str(src1)) + assert node is not None + assert not mg.findNode("uuid") # self-test + + # Add script importing uuid + src2 = gen_sourcefile(tmpdir, """import uuid""", test_id="2") + mg.run_script(str(src2)) + assert mg.findNode("uuid") # self-test + + # The acutal test: uuid is (indirectly) linked to the first script + names = [n.identifier for n in mg.flatten(start=node)] + assert str(src2) in names + assert "uuid" in names + + +def _gen_pseudo_rthooks(name, rthook_dat, tmpdir, gen_files=True): + hd = tmpdir.ensure(name, dir=True) + if gen_files: + for fn in itertools.chain(*rthook_dat.values()): + hd.ensure("rthooks", fn) + rhd = hd.ensure("rthooks.dat") + rhd.write(repr(rthook_dat)) + return hd + + +def test_collect_rthooks_1(tmpdir, monkeypatch): + rh1 = {"test_pyimodulegraph_mymod1": ["m1.py"]} + hd1 = _gen_pseudo_rthooks("h1", rh1, tmpdir) + mg = FakePyiModuleGraph(HOMEPATH, user_hook_dirs=[str(hd1)]) + assert len(mg._available_rthooks["test_pyimodulegraph_mymod1"]) == 1 + + +def test_collect_rthooks_2(tmpdir, monkeypatch): + rh1 = {"test_pyimodulegraph_mymod1": ["m1.py"]} + rh2 = {"test_pyimodulegraph_mymod2": ["rth1.py", "rth1.py"]} + hd1 = _gen_pseudo_rthooks("h1", rh1, tmpdir) + hd2 = _gen_pseudo_rthooks("h2", rh2, tmpdir) + mg = FakePyiModuleGraph(HOMEPATH, user_hook_dirs=[str(hd1), str(hd2)]) + assert len(mg._available_rthooks["test_pyimodulegraph_mymod1"]) == 1 + assert len(mg._available_rthooks["test_pyimodulegraph_mymod2"]) == 2 + + +def test_collect_rthooks_3(tmpdir, monkeypatch): + rh1 = {"test_pyimodulegraph_mymod1": ["m1.py"]} + rh2 = {"test_pyimodulegraph_mymod1": ["rth1.py", "rth1.py"]} + hd1 = _gen_pseudo_rthooks("h1", rh1, tmpdir) + hd2 = _gen_pseudo_rthooks("h2", rh2, tmpdir) + mg = FakePyiModuleGraph(HOMEPATH, user_hook_dirs=[str(hd1), str(hd2)]) + assert len(mg._available_rthooks["test_pyimodulegraph_mymod1"]) == 1 + + +def test_collect_rthooks_fail_1(tmpdir, monkeypatch): + rh1 = {"test_pyimodulegraph_mymod1": ["m1.py"]} + hd1 = _gen_pseudo_rthooks("h1", rh1, tmpdir, False) + with pytest.raises(AssertionError): + FakePyiModuleGraph(HOMEPATH, user_hook_dirs=[str(hd1)]) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_recursion_limit.py b/3rdparty/pyinstaller-4.3/tests/unit/test_recursion_limit.py new file mode 100644 index 0000000000000000000000000000000000000000..1ba16fce35fe5a73e4361d5ab2c128e3f703dfcc --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_recursion_limit.py @@ -0,0 +1,84 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 pytest + +from PyInstaller.lib.modulegraph import modulegraph +from PyInstaller import configure +from PyInstaller import __main__ as pyi_main +from PyInstaller.compat import is_py37, is_win + + +@pytest.fixture +def large_import_chain(tmpdir): + pkg = tmpdir.join('pkg') + pkg.join('__init__.py').ensure().write('from . import a') + mod = None + for alpha in "abcdefg": + if mod: + # last module of prior sub-pkg imports this package + mod.write("import pkg.%s" % alpha) + subpkg = pkg.join(alpha).mkdir() + subpkg.join('__init__.py').write('from . import %s000' % alpha) + for num in range(250): + # module importing its next sibling + mod = subpkg.join("%s%03i.py" % (alpha, num)) + mod.write("from . import %s%03i" % (alpha, num + 1)) + script = tmpdir.join('script.py') + script.write('import pkg') + return [str(tmpdir)], str(script) + + +def test_recursion_to_deep(large_import_chain): + """ + modulegraph is recursive and thus triggers RecursionError if + nesting of imported modules is to deep. This can be worked around + by increasing recursion limit. + + With the default recursion limit (1000), the recursion error + occurs at about 115 modules, with limit 2000 (as tested below) at + about 240 modules, with limit 5000 at about 660 modules. + """ + if is_py37 and is_win: + pytest.xfail("worker is know to crash for Py 3.7, 3.8 on Windows") + path, script = large_import_chain + mg = modulegraph.ModuleGraph(path) + # Increase recursion limit to 5 times of the default. Given the + # module import chain created above this still should fail. + with pytest.raises(RecursionError): + mg.run_script(str(script)) + + +def test_RecursionError_prints_message(tmpdir, large_import_chain, + monkeypatch): + """ + modulegraph is recursive and thus triggers RecursionError if + nesting of imported modules is to deep. Ensure a respective + informative message is printed if recursion error occurs. + """ + if is_py37 and is_win: + pytest.xfail("worker is know to crash for Py 3.7, 3.8 on Windows") + path, script = large_import_chain + + default_args = [ + '--specpath', str(tmpdir), + '--distpath', str(tmpdir.join("dist")), + '--workpath', str(tmpdir.join("build")), + '--path', str(tmpdir), + ] + + pyi_args = [script] + default_args + PYI_CONFIG = configure.get_config(upx_dir=None) + PYI_CONFIG['cachedir'] = str(tmpdir) + + with pytest.raises(SystemExit) as execinfo: + pyi_main.run(pyi_args, PYI_CONFIG) + assert "sys.setrecursionlimit" in str(execinfo.value) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_systemexit.py b/3rdparty/pyinstaller-4.3/tests/unit/test_systemexit.py new file mode 100644 index 0000000000000000000000000000000000000000..9f206736c286b81846256e1d4722416aa014e623 --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_systemexit.py @@ -0,0 +1,35 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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) +#----------------------------------------------------------------------------- + +""" +In `#4592 `_, the +bootloader was modified to allow the execution traceback to be shown in +Windowed mode. Unfortunately, this modification caused the recurrence of +a bug raised in #1869. A quick summary of the bug would be +""" + +import pytest + + +@pytest.mark.parametrize( + 'src,retcode', + [ + # Code to run, retcode + ('raise SystemExit', 0), + ('import sys; sys.exit()', 0), + ('raise SystemExit(1)', 1), + ('import sys; sys.exit(2)', 2), + ('raise SystemExit("Message to get printed to the console.")', 1), + ('raise Exception("Unhandled exception.")', 1) # See issue #5480 + ] +) +def test_systemexit_is_handled_correctly(src, retcode, pyi_builder): + pyi_builder.test_source(src, retcode=retcode) diff --git a/3rdparty/pyinstaller-4.3/tests/unit/test_winmanifest.py b/3rdparty/pyinstaller-4.3/tests/unit/test_winmanifest.py new file mode 100644 index 0000000000000000000000000000000000000000..b4063702797fc0e317112dddf0f3ebb7c4a6d67f --- /dev/null +++ b/3rdparty/pyinstaller-4.3/tests/unit/test_winmanifest.py @@ -0,0 +1,54 @@ +#----------------------------------------------------------------------------- +# Copyright (c) 2005-2021, 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 shutil + +import pytest + +from PyInstaller import HOMEPATH, PLATFORM + + +@pytest.mark.win32 +def test_manifest_from_res_file(tmp_path): + # This import only works on Windows. Place it here, protected by the + # `@pytest.mark.win32`` decorator. + from PyInstaller.utils.win32 import winmanifest + + # Locate bootloader executable + bootloader_file = os.path.join( + HOMEPATH, + 'PyInstaller', + 'bootloader', + PLATFORM, + 'run.exe' + ) + + # Create a local copy + test_file = str(tmp_path / 'test_file.exe') + shutil.copyfile(bootloader_file, test_file) + + # Create a manifest, ... + manifest_filename = test_file + '.manifest' + manifest = winmanifest.Manifest( + type_="win32", + name='test_file.exe', + processorArchitecture=winmanifest.processor_architecture(), + version=(1, 0, 0, 0)) + manifest.filename = manifest_filename + manifest.requestedExecutionLevel = 'asInvoker' + manifest.uiAccess = True + + # ... embed it, ... + manifest.update_resources(test_file, [1]) + + # ... and read it back + manifest2 = winmanifest.ManifestFromResFile(test_file) + assert manifest == manifest2