diff --git a/.github/workflows/nuitka.yml b/.github/workflows/nuitka.yml index 20159e81..0fdf414e 100644 --- a/.github/workflows/nuitka.yml +++ b/.github/workflows/nuitka.yml @@ -21,11 +21,11 @@ jobs: - name: Set up Visual Studio environment if: success() uses: seanmiddleditch/gha-setup-vsdevenv@master - - name: Set up Python 3.9.4 (x64) + - name: Set up Python 3.10.4 (x64) if: success() uses: actions/setup-python@v2 with: - python-version: 3.9.4 + python-version: 3.10.4 architecture: x64 - name: Create virtual environment if: success() diff --git a/build.py b/build.py index 5c92b353..82d09261 100644 --- a/build.py +++ b/build.py @@ -7,6 +7,8 @@ import sys import zipfile +from pyro.Comparators import endswith + class Application: logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname).4s] %(message)s') @@ -24,25 +26,38 @@ def __init__(self, args: argparse.Namespace) -> None: self.root_tools_path: str = os.path.join(self.root_path, 'tools') self.dist_tools_path: str = os.path.join(self.dist_path, 'tools') + self.site_path: str = os.path.join(self.dist_path, 'site') + self.zip_path: str = os.path.join(self.root_path, 'bin', f'{self.package_name}.zip') + + self.dist_glob_pattern: str = os.path.join(self.dist_path, r'**\*') + + self.icon_path: str = os.path.join(self.root_path, 'fire.ico') + + pyro_version: str = '1.0.0.0' + self.nuitka_args: list = [ 'python', '-m', 'nuitka', '--standalone', 'pyro', '--include-package=pyro', - '--experimental=use_pefile', - '--python-flag=nosite', - f'--python-for-scons={sys.executable}', '--assume-yes-for-downloads', '--plugin-enable=multiprocessing', - '--show-progress', - '--plugin-enable=pkg-resources' + '--plugin-enable=pkg-resources', + '--msvc=14.3', + '--disable-ccache', + '--windows-company-name=fireundubh', + f'--windows-product-name={self.package_name.capitalize()}', + f'--windows-file-version={pyro_version}', + f'--windows-product-version={pyro_version}', + '--windows-file-description=https://github.com/fireundubh/pyro', + '--windows-icon-from-ico=fire.ico' ] def __setattr__(self, key: str, value: object) -> None: # sanitize paths - if isinstance(value, str) and key.endswith('path'): + if isinstance(value, str) and endswith(key, 'path', ignorecase=True): value = os.path.normpath(value) # normpath converts empty paths to os.curdir which we don't want - if value == '.': + if value == os.curdir: value = '' super(Application, self).__setattr__(key, value) @@ -57,6 +72,7 @@ def _clean_dist_folder(self) -> None: 'python37.dll', 'python38.dll', 'python39.dll', + 'python310.dll', '_elementpath.pyd', '_hashlib.pyd', '_multiprocessing.pyd', @@ -69,7 +85,7 @@ def _clean_dist_folder(self) -> None: 'unicodedata.pyd' ) - for f in glob.iglob(os.path.join(self.dist_path, r'**\*'), recursive=True): + for f in glob.iglob(self.dist_glob_pattern, recursive=True): if not os.path.isfile(f): continue @@ -80,7 +96,7 @@ def _clean_dist_folder(self) -> None: Application.log.warning(f'Deleting: "{f}"') os.remove(f) - for f in glob.iglob(os.path.join(self.dist_path, r'**\*'), recursive=True): + for f in glob.iglob(self.dist_glob_pattern, recursive=True): if not os.path.isdir(f): continue @@ -88,25 +104,21 @@ def _clean_dist_folder(self) -> None: Application.log.warning(f'Deleting empty folder: "{f}"') shutil.rmtree(f, ignore_errors=True) - site_dir: str = os.path.join(self.dist_path, 'site') - if os.path.exists(site_dir): - shutil.rmtree(site_dir, ignore_errors=True) + if os.path.exists(self.site_path): + shutil.rmtree(self.site_path, ignore_errors=True) - def _build_zip_archive(self) -> str: - zip_path: str = os.path.join(self.root_path, 'bin', f'{self.package_name}.zip') - os.makedirs(os.path.dirname(zip_path), exist_ok=True) + def _build_zip_archive(self) -> None: + os.makedirs(os.path.dirname(self.zip_path), exist_ok=True) - files: list = [f for f in glob.glob(os.path.join(self.dist_path, r'**\*'), recursive=True) + files: list = [f for f in glob.glob(self.dist_glob_pattern, recursive=True) if os.path.isfile(f)] - with zipfile.ZipFile(zip_path, 'w') as z: + with zipfile.ZipFile(self.zip_path, 'w') as z: for f in files: z.write(f, os.path.join(self.package_name, os.path.relpath(f, self.dist_path)), compress_type=zipfile.ZIP_STORED) Application.log.info(f'Added file to archive: {f}') - return zip_path - @staticmethod def exec_process(cmd: list, env: dict) -> int: try: @@ -126,7 +138,7 @@ def run(self) -> int: sys.exit(1) if self.vcvars64_path: - if not os.path.exists(self.vcvars64_path) or not self.vcvars64_path.endswith('.bat'): + if not os.path.exists(self.vcvars64_path) or not endswith(self.vcvars64_path, '.bat', ignorecase=True): Application.log.error('Cannot build Pyro with MSVC compiler because VsDevCmd path is invalid') sys.exit(1) @@ -193,9 +205,9 @@ def run(self) -> int: if not self.no_zip: Application.log.info('Building archive...') - zip_created: str = self._build_zip_archive() + self._build_zip_archive() - Application.log.info(f'Wrote archive: "{zip_created}"') + Application.log.info(f'Wrote archive: "{self.zip_path}"') Application.log.info('Build complete.') diff --git a/fire.ico b/fire.ico new file mode 100644 index 00000000..2f8c4e44 Binary files /dev/null and b/fire.ico differ diff --git a/pyro/Application.py b/pyro/Application.py index 58116bf3..c11593d6 100644 --- a/pyro/Application.py +++ b/pyro/Application.py @@ -4,7 +4,11 @@ import sys from pyro.Enums.Event import (BuildEvent, - ImportEvent) + ImportEvent, + CompileEvent, + AnonymizeEvent, + PackageEvent, + ZipEvent) from pyro.BuildFacade import BuildFacade from pyro.Comparators import startswith from pyro.PapyrusProject import PapyrusProject @@ -145,11 +149,23 @@ def run(self) -> int: ppj.try_run_event(BuildEvent.PRE) if build.scripts_count > 0: + if ppj.use_pre_compile_event: + ppj.try_run_event(CompileEvent.PRE) + build.try_compile() + if ppj.use_post_compile_event: + ppj.try_run_event(CompileEvent.POST) + if ppj.options.anonymize: if build.compile_data.failed_count == 0 or ppj.options.ignore_errors: + if ppj.use_pre_anonymize_event: + ppj.try_run_event(AnonymizeEvent.PRE) + build.try_anonymize() + + if ppj.use_post_anonymize_event: + ppj.try_run_event(AnonymizeEvent.POST) else: Application.log.error(f'Cannot anonymize scripts because {build.compile_data.failed_count} scripts failed to compile') sys.exit(build.compile_data.failed_count) @@ -158,7 +174,13 @@ def run(self) -> int: if ppj.options.package: if build.compile_data.failed_count == 0 or ppj.options.ignore_errors: + if ppj.use_pre_package_event: + ppj.try_run_event(PackageEvent.PRE) + build.try_pack() + + if ppj.use_post_package_event: + ppj.try_run_event(PackageEvent.POST) else: Application.log.error(f'Cannot create Packages because {build.compile_data.failed_count} scripts failed to compile') sys.exit(build.compile_data.failed_count) @@ -167,7 +189,13 @@ def run(self) -> int: if ppj.options.zip: if build.compile_data.failed_count == 0 or ppj.options.ignore_errors: + if ppj.use_pre_zip_event: + ppj.try_run_event(ZipEvent.PRE) + build.try_zip() + + if ppj.use_post_zip_event: + ppj.try_run_event(ZipEvent.POST) else: Application.log.error(f'Cannot create ZipFile because {build.compile_data.failed_count} scripts failed to compile') sys.exit(build.compile_data.failed_count) diff --git a/pyro/Constants.py b/pyro/Constants.py index ee967a14..e972e042 100644 --- a/pyro/Constants.py +++ b/pyro/Constants.py @@ -53,8 +53,16 @@ class XmlTagName(Constant): PAPYRUS_PROJECT: str = 'PapyrusProject' POST_BUILD_EVENT: str = 'PostBuildEvent' POST_IMPORT_EVENT: str = 'PostImportEvent' + POST_COMPILE_EVENT: str = 'PostCompileEvent' + POST_ANONYMIZE_EVENT: str = 'PostAnonymizeEvent' + POST_PACKAGE_EVENT: str = 'PostPackageEvent' + POST_ZIP_EVENT: str = 'PostZipEvent' PRE_BUILD_EVENT: str = 'PreBuildEvent' PRE_IMPORT_EVENT: str = 'PreImportEvent' + PRE_COMPILE_EVENT: str = 'PreCompileEvent' + PRE_ANONYMIZE_EVENT: str = 'PreAnonymizeEvent' + PRE_PACKAGE_EVENT: str = 'PrePackageEvent' + PRE_ZIP_EVENT: str = 'PreZipEvent' SCRIPTS: str = 'Scripts' VARIABLES: str = 'Variables' ZIP_FILE: str = 'ZipFile' diff --git a/pyro/Enums/Event.py b/pyro/Enums/Event.py index 42f558bb..1c7bf8e2 100644 --- a/pyro/Enums/Event.py +++ b/pyro/Enums/Event.py @@ -12,4 +12,24 @@ class ImportEvent(Enum): POST = 1 -Event = Union[BuildEvent, ImportEvent] +class CompileEvent(Enum): + PRE = 0 + POST = 1 + + +class AnonymizeEvent(Enum): + PRE = 0 + POST = 1 + + +class PackageEvent(Enum): + PRE = 0 + POST = 1 + + +class ZipEvent(Enum): + PRE = 0 + POST = 1 + + +Event = Union[BuildEvent, ImportEvent, CompileEvent, AnonymizeEvent, PackageEvent, ZipEvent] diff --git a/pyro/PapyrusProject.py b/pyro/PapyrusProject.py index 2ecaadc5..6511175a 100644 --- a/pyro/PapyrusProject.py +++ b/pyro/PapyrusProject.py @@ -12,7 +12,11 @@ from pyro.Enums.Event import (Event, BuildEvent, - ImportEvent) + ImportEvent, + CompileEvent, + AnonymizeEvent, + PackageEvent, + ZipEvent) from pyro.CommandArguments import CommandArguments from pyro.Comparators import (endswith, is_folder_node, @@ -45,6 +49,16 @@ class PapyrusProject(ProjectBase): zip_files_node: etree.ElementBase = None pre_build_node: etree.ElementBase = None post_build_node: etree.ElementBase = None + pre_import_node: etree.ElementBase = None + post_import_node: etree.ElementBase = None + pre_compile_node: etree.ElementBase = None + post_compile_node: etree.ElementBase = None + pre_anonymize_node: etree.ElementBase = None + post_anonymize_node: etree.ElementBase = None + pre_package_node: etree.ElementBase = None + post_package_node: etree.ElementBase = None + pre_zip_node: etree.ElementBase = None + post_zip_node: etree.ElementBase = None remote: RemoteBase = None remote_schemas: tuple = ('https:', 'http:') @@ -132,6 +146,30 @@ def bool_attr(element: etree.Element, attr_name: str) -> bool: self.post_import_node = self.ppj_root.find(XmlTagName.POST_IMPORT_EVENT) self.use_post_import_event = bool_attr(self.post_import_node, XmlAttributeName.USE_IN_BUILD) + self.pre_compile_node = self.ppj_root.find(XmlTagName.PRE_COMPILE_EVENT) + self.use_pre_compile_event = bool_attr(self.pre_compile_node, XmlAttributeName.USE_IN_BUILD) + + self.post_compile_node = self.ppj_root.find(XmlTagName.POST_COMPILE_EVENT) + self.use_post_compile_event = bool_attr(self.post_compile_node, XmlAttributeName.USE_IN_BUILD) + + self.pre_anonymize_node = self.ppj_root.find(XmlTagName.PRE_ANONYMIZE_EVENT) + self.use_pre_anonymize_event = bool_attr(self.pre_anonymize_node, XmlAttributeName.USE_IN_BUILD) + + self.post_anonymize_node = self.ppj_root.find(XmlTagName.POST_ANONYMIZE_EVENT) + self.use_post_anonymize_event = bool_attr(self.post_anonymize_node, XmlAttributeName.USE_IN_BUILD) + + self.pre_package_node = self.ppj_root.find(XmlTagName.PRE_PACKAGE_EVENT) + self.use_pre_package_event = bool_attr(self.pre_package_node, XmlAttributeName.USE_IN_BUILD) + + self.post_package_node = self.ppj_root.find(XmlTagName.POST_PACKAGE_EVENT) + self.use_post_package_event = bool_attr(self.post_package_node, XmlAttributeName.USE_IN_BUILD) + + self.pre_zip_node = self.ppj_root.find(XmlTagName.PRE_ZIP_EVENT) + self.use_pre_zip_event = bool_attr(self.pre_zip_node, XmlAttributeName.USE_IN_BUILD) + + self.post_zip_node = self.ppj_root.find(XmlTagName.POST_ZIP_EVENT) + self.use_post_zip_event = bool_attr(self.post_zip_node, XmlAttributeName.USE_IN_BUILD) + if self.options.package and self.packages_node is not None: if not self.options.package_path: self.options.package_path = self.packages_node.get(XmlAttributeName.OUTPUT) @@ -758,5 +796,21 @@ def try_run_event(self, event: Event) -> None: ProcessManager.run_event(self.pre_build_node, self.project_path) elif event == BuildEvent.POST: ProcessManager.run_event(self.post_build_node, self.project_path) + elif event == CompileEvent.PRE: + ProcessManager.run_event(self.pre_compile_node, self.project_path) + elif event == CompileEvent.POST: + ProcessManager.run_event(self.post_compile_node, self.project_path) + elif event == AnonymizeEvent.PRE: + ProcessManager.run_event(self.pre_anonymize_node, self.project_path) + elif event == AnonymizeEvent.POST: + ProcessManager.run_event(self.post_anonymize_node, self.project_path) + elif event == PackageEvent.PRE: + ProcessManager.run_event(self.pre_package_node, self.project_path) + elif event == PackageEvent.POST: + ProcessManager.run_event(self.post_package_node, self.project_path) + elif event == ZipEvent.PRE: + ProcessManager.run_event(self.pre_zip_node, self.project_path) + elif event == ZipEvent.POST: + ProcessManager.run_event(self.post_zip_node, self.project_path) else: raise NotImplementedError diff --git a/pyro/PapyrusProject.xsd b/pyro/PapyrusProject.xsd index 3b123853..10e33bce 100644 --- a/pyro/PapyrusProject.xsd +++ b/pyro/PapyrusProject.xsd @@ -22,6 +22,14 @@ + + + + + + + + diff --git a/requirements.txt b/requirements.txt index f2069d16..50e04468 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ lxml nuitka psutil wcmatch +ordered-set