From 9f20225a59c6113a331a5b1cb6f318ce8bdcdfea Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sun, 27 Aug 2023 02:41:07 +0100 Subject: [PATCH] build scripts added --- .gitignore | 7 ++ build.ps1 | 40 ++++++++ poetry.lock | 111 +++++++++++++++++++- pyproject.toml | 10 +- scripts.py | 24 +++++ tools/rewriter.py | 250 +++++++++++++++++++++++++++++++++++++++++++++ tools/src/.gitkeep | 0 7 files changed, 439 insertions(+), 3 deletions(-) create mode 100644 build.ps1 create mode 100644 scripts.py create mode 100644 tools/rewriter.py create mode 100644 tools/src/.gitkeep diff --git a/.gitignore b/.gitignore index ee29b3e..e02a219 100644 --- a/.gitignore +++ b/.gitignore @@ -132,4 +132,11 @@ dmypy.json # Pyre type checker .pyre/ +# build +sv_ttk/ +theme/ + +sv_*.py +fst_*.py + .vscode/ \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..ba6170a --- /dev/null +++ b/build.ps1 @@ -0,0 +1,40 @@ +param( + [Parameter(Mandatory = $true)] + [string]$prefix, + [string]$theme +) + +function Format-Path { + param($Kind) + return @( + $prefix, + (& { if ($theme) { $theme } else { "" } }), + "${Kind}" + ).Where({ $_ -ne "" }) -Join "_" +} + +function Compress-Builds { + $target = Join-Path -Path $PSScriptRoot -ChildPath "dist" + @("basic", "banana", "potato") | ForEach-Object { + $compress_path = Format-Path -Kind $_ + Compress-Archive -Path $(Join-Path -Path $target -ChildPath $compress_path) -DestinationPath $(Join-Path -Path $target -ChildPath "${compress_path}.zip") -Force + } +} + +function Get-Builds { + @("basic", "banana", "potato") | ForEach-Object { + $spec_path = Format-Path -Kind $_ + + "building $spec_path" | Write-Host + + poetry run pyinstaller "$spec_path.spec" --noconfirm + } +} + +function main { + Get-Builds + + Compress-Builds +} + +if ($MyInvocation.InvocationName -ne '.') { main } \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 035f8e1..a0719da 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,16 @@ # This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +[[package]] +name = "altgraph" +version = "0.17.3" +description = "Python graph (network) package" +optional = false +python-versions = "*" +files = [ + {file = "altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"}, + {file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"}, +] + [[package]] name = "black" version = "22.12.0" @@ -76,6 +87,20 @@ pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib" plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] +[[package]] +name = "macholib" +version = "1.16.2" +description = "Mach-O header analysis and editing" +optional = false +python-versions = "*" +files = [ + {file = "macholib-1.16.2-py2.py3-none-any.whl", hash = "sha256:44c40f2cd7d6726af8fa6fe22549178d3a4dfecc35a9cd15ea916d9c83a688e0"}, + {file = "macholib-1.16.2.tar.gz", hash = "sha256:557bbfa1bb255c20e9abafe7ed6cd8046b48d9525db2f9b77d3122a63a2a8bf8"}, +] + +[package.dependencies] +altgraph = ">=0.17" + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -98,6 +123,17 @@ files = [ {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, ] +[[package]] +name = "pefile" +version = "2023.2.7" +description = "Python PE parsing module" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"}, + {file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"}, +] + [[package]] name = "platformdirs" version = "3.8.1" @@ -113,6 +149,77 @@ files = [ docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] +[[package]] +name = "pyinstaller" +version = "5.13.1" +description = "PyInstaller bundles a Python application and all its dependencies into a single package." +optional = false +python-versions = "<3.13,>=3.7" +files = [ + {file = "pyinstaller-5.13.1-py3-none-macosx_10_13_universal2.whl", hash = "sha256:3c9cfe6d5d2f392d5d47389f6d377a8f225db460cdd01048b5a3de1d99c24ebe"}, + {file = "pyinstaller-5.13.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:29341d2e86d5ce7df993e797ee96ef679041fc85376d31c35c7b714085a21299"}, + {file = "pyinstaller-5.13.1-py3-none-manylinux2014_i686.whl", hash = "sha256:ad6e31a8f35a463c6140e4cf979859197edc9831a1039253408b0fe5eec274dc"}, + {file = "pyinstaller-5.13.1-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:5d801db3ceee58d01337473ea897e96e4bb21421a169dd7cf8716754617ff7fc"}, + {file = "pyinstaller-5.13.1-py3-none-manylinux2014_s390x.whl", hash = "sha256:2519db3edec87d8c33924c2c4b7e176d8c1bbd9ba892d77efb67281925e621d6"}, + {file = "pyinstaller-5.13.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e033218c8922f0342b6095fb444ecb3bc6747dfa58cac5eac2b985350f4b681e"}, + {file = "pyinstaller-5.13.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:086e68aa1e72f6aa13b9d170a395755e2b194b8ab410caeed02d16b432410c8c"}, + {file = "pyinstaller-5.13.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:aa609aca62edd8cdcf7740677a21525e6c23b5e9a8f821ec8a80c68947771b5d"}, + {file = "pyinstaller-5.13.1-py3-none-win32.whl", hash = "sha256:b8d4000af72bf72f8185d420cd0a0aee0961f03a5c3511dc3ff08cdaef0583de"}, + {file = "pyinstaller-5.13.1-py3-none-win_amd64.whl", hash = "sha256:b70ebc10811b30bbea4cf5b81fd1477db992c2614cf215edc987cda9c5468911"}, + {file = "pyinstaller-5.13.1-py3-none-win_arm64.whl", hash = "sha256:78d1601a11475b95dceff6eaf0c9cd74d93e3f47b5ce4ad63cd76e7a369d3d04"}, + {file = "pyinstaller-5.13.1.tar.gz", hash = "sha256:a2e7a1d76a7ac26f1db849d691a374f2048b0e204233028d25d79a90ecd1fec8"}, +] + +[package.dependencies] +altgraph = "*" +macholib = {version = ">=1.8", markers = "sys_platform == \"darwin\""} +pefile = {version = ">=2022.5.30", markers = "sys_platform == \"win32\""} +pyinstaller-hooks-contrib = ">=2021.4" +pywin32-ctypes = {version = ">=0.2.1", markers = "sys_platform == \"win32\""} +setuptools = ">=42.0.0" + +[package.extras] +encryption = ["tinyaes (>=1.0.0)"] +hook-testing = ["execnet (>=1.5.0)", "psutil", "pytest (>=2.7.3)"] + +[[package]] +name = "pyinstaller-hooks-contrib" +version = "2023.7" +description = "Community maintained hooks for PyInstaller" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pyinstaller-hooks-contrib-2023.7.tar.gz", hash = "sha256:0c436a4c3506020e34116a8a7ddfd854c1ad6ddca9a8cd84500bd6e69c9e68f9"}, + {file = "pyinstaller_hooks_contrib-2023.7-py2.py3-none-any.whl", hash = "sha256:3c10df14c0f71ab388dfbf1625375b087e7330d9444cbfd2b310ba027fa0cff0"}, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.2" +description = "A (partial) reimplementation of pywin32 using ctypes/cffi" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"}, + {file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"}, +] + +[[package]] +name = "setuptools" +version = "68.1.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"}, + {file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5,<=7.1.2)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "sv-ttk" version = "2.5.5" @@ -165,5 +272,5 @@ tomli = {version = ">=2.0.1,<3.0.0", markers = "python_version < \"3.11\""} [metadata] lock-version = "2.0" -python-versions = "^3.10" -content-hash = "cde74de8c9de0895555068efea354edf6b51f2332c253f02f6c8f26fe6b4e795" +python-versions = ">=3.10,<3.13" +content-hash = "781e80b07b3619ad403c9dd62c0c0cae82eebd5ee07dba5ca7a8aa451d26d0a6" diff --git a/pyproject.toml b/pyproject.toml index 71a2a6d..0d6c731 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ packages = [{ include = "vmcompact" }] include = ["vmcompact/img/cat.ico"] [tool.poetry.dependencies] -python = "^3.10" +python = ">=3.10,<3.13" sv-ttk = "^2.5.5" tomli = { version = "^2.0.1", python = "<3.11" } voicemeeter-api = "^2.4.4" @@ -21,6 +21,14 @@ vban-cmd = "^2.4.4" black = { version = "^22.6.0", allow-prereleases = true } isort = "^5.12.0" +[tool.poetry.group.dev.dependencies] +pyinstaller = "^5.13.1" + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + +[tool.poetry.scripts] +build_sunvalley = "scripts:build_sunvalley" +build_forest = "scripts:build_forest" +build_all = "scripts:build_all" \ No newline at end of file diff --git a/scripts.py b/scripts.py new file mode 100644 index 0000000..061a889 --- /dev/null +++ b/scripts.py @@ -0,0 +1,24 @@ +import subprocess +import sys +from pathlib import Path + + +def build_sunvalley(): + buildscript = Path.cwd() / "build.ps1" + subprocess.run(["powershell", str(buildscript), "sv"]) + + +def build_forest(): + rewriter = Path.cwd() / "tools" / "rewriter.py" + subprocess.run([sys.executable, str(rewriter), "-r"]) + + buildscript = Path.cwd() / "build.ps1" + for theme in ("light", "dark"): + subprocess.run(["powershell", str(buildscript), "fst", theme]) + + subprocess.run([sys.executable, str(rewriter), "-c"]) + + +def build_all(): + steps = (build_sunvalley, build_forest) + [step() for step in steps] diff --git a/tools/rewriter.py b/tools/rewriter.py new file mode 100644 index 0000000..bbe34df --- /dev/null +++ b/tools/rewriter.py @@ -0,0 +1,250 @@ +import argparse +import logging +from pathlib import Path + +logging.basicConfig(level=logging.DEBUG) + +logger = logging.getLogger("vm-compact-rewriter") + +PACKAGE_DIR = Path(__file__).parent.parent / "vmcompact" + +SRC_DIR = Path(__file__).parent / "src" + + +def write_outs(output, outs: tuple): + for out in outs: + output.write(out) + + +def rewrite_app(): + app_logger = logger.getChild("app") + app_logger.info("rewriting app.py") + infile = Path(SRC_DIR) / "app.bk" + outfile = Path(PACKAGE_DIR) / "app.py" + with open(infile, "r") as input: + with open(outfile, "w") as output: + for line in input: + match line: + # App init() + case " def __init__(self, vmr):\n": + output.write(" def __init__(self, vmr, theme):\n") + case " self._vmr = vmr\n": + write_outs( + output, + ( + " self._vmr = vmr\n", + " self._theme = theme\n", + ' tcldir = Path.cwd() / "theme"\n', + " if not tcldir.is_dir():\n", + ' tcldir = Path.cwd() / "builds" / "theme"\n', + ' self.tk.call("source", tcldir.resolve() / f"forest-{self._theme}.tcl")\n', + ), + ) + # def connect() + case "def connect(kind_id: str, vmr) -> App:\n": + output.write( + 'def connect(kind_id: str, vmr, theme="light") -> App:\n' + ) + case " return VMMIN_cls(vmr)\n": + output.write(" return VMMIN_cls(vmr, theme)\n") + case _: + output.write(line) + + +def rewrite_builders(): + builders_logger = logger.getChild("builders") + builders_logger.info("rewriting builders.py") + infile = Path(SRC_DIR) / "builders.bk" + outfile = Path(PACKAGE_DIR) / "builders.py" + with open(infile, "r") as input: + with open(outfile, "w") as output: + ignore_next_lines = 0 + + for line in input: + if ignore_next_lines > 0: + builders_logger.info(f"ignoring: {line}") + ignore_next_lines -= 1 + continue + + match line: + # loading themes + case "import sv_ttk\n": + output.write("#import sv_ttk\n") + case " self.app.resizable(False, False)\n": + write_outs( + output, + ( + " self.app.resizable(False, False)\n" + " if _configuration.themes_enabled:\n", + ' ttk.Style().theme_use(f"forest-{self.app._theme}")\n', + ' self.logger.info(f"Forest Theme applied")\n', + ), + ) + ignore_next_lines = 6 + # setting navframe button widths + case " variable=self.navframe.submix,\n": + write_outs( + output, + ( + " variable=self.navframe.submix,\n" + " width=8,\n", + ), + ) + case " variable=self.navframe.channel,\n": + write_outs( + output, + ( + " variable=self.navframe.channel,\n" + " width=8,\n", + ), + ) + case " variable=self.navframe.extend,\n": + write_outs( + output, + ( + " variable=self.navframe.extend,\n" + " width=8,\n", + ), + ) + case " variable=self.navframe.info,\n": + write_outs( + output, + ( + " variable=self.navframe.info,\n" + " width=8,\n", + ), + ) + # set channelframe button widths + case " variable=self.labelframe.mute,\n": + write_outs( + output, + ( + " variable=self.labelframe.mute,\n" + " width=7,\n", + ), + ) + case " variable=self.labelframe.conf,\n": + write_outs( + output, + ( + " variable=self.labelframe.conf,\n" + " width=7,\n", + ), + ) + case " variable=self.labelframe.on,\n": + write_outs( + output, + ( + " variable=self.labelframe.on,\n" + " width=7,\n", + ), + ) + # set stripconfigframe button widths + case " self.configframe.phys_out_params.index(param)\n": + write_outs( + output, + ( + " self.configframe.phys_out_params.index(param)\n", + " ],\n", + " width=6,\n", + ), + ) + ignore_next_lines = 1 + case " self.configframe.virt_out_params.index(param)\n": + write_outs( + output, + ( + " self.configframe.virt_out_params.index(param)\n", + " ],\n", + " width=6,\n", + ), + ) + ignore_next_lines = 1 + # This does both strip and bus param vars buttons + case " variable=self.configframe.param_vars[i],\n": + write_outs( + output, + ( + " variable=self.configframe.param_vars[i],\n", + " width=6,\n", + ), + ) + case _: + if "Toggle.TButton" in line: + output.write(line.replace("Toggle.TButton", "ToggleButton")) + else: + output.write(line) + + +def rewrite_menu(): + menu_logger = logger.getChild("menu") + menu_logger.info("rewriting menu.py") + infile = Path(SRC_DIR) / "menu.bk" + outfile = Path(PACKAGE_DIR) / "menu.py" + with open(infile, "r") as input: + with open(outfile, "w") as output: + ignore_next_lines = 0 + + for line in input: + if ignore_next_lines > 0: + menu_logger.info(f"ignoring: {line}") + ignore_next_lines -= 1 + continue + match line: + case "import sv_ttk\n": + output.write("#import sv_ttk\n") + case " # layout/themes\n": + ignore_next_lines = 14 + case _: + output.write(line) + + +def prepare_for_build(): + ################# MOVE FILES FROM PACKAGE DIR INTO SRC DIR ######################### + for path in ( + Path(PACKAGE_DIR) / "app.py", + Path(PACKAGE_DIR) / "builders.py", + Path(PACKAGE_DIR) / "menu.py", + ): + if path.exists(): + logger.debug(f"moving {str(path)}") + path.rename(SRC_DIR / f"{path.stem}.bk") + + ###################### RUN THE FILE REWRITER FOR EACH *.BK ######################### + steps = ( + rewrite_app, + rewrite_builders, + rewrite_menu, + ) + [step() for step in steps] + + +def cleanup(): + ########################## RESTORE *.BK FILES ##################################### + for file in ( + PACKAGE_DIR / "app.py", + PACKAGE_DIR / "builders.py", + PACKAGE_DIR / "menu.py", + ): + file.unlink() + + for file in ( + SRC_DIR / "app.bk", + SRC_DIR / "builders.bk", + SRC_DIR / "menu.bk", + ): + file.rename(PACKAGE_DIR / f"{file.stem}.py") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("-r", "--rewrite", action="store_true") + parser.add_argument("-c", "--cleanup", action="store_true") + args = parser.parse_args() + + if args.rewrite: + logger.info("preparing files for build") + prepare_for_build() + elif args.cleanup: + logger.info("cleaning up files") + cleanup() diff --git a/tools/src/.gitkeep b/tools/src/.gitkeep new file mode 100644 index 0000000..e69de29