mirror of
https://github.com/onyx-and-iris/voicemeeter-compact.git
synced 2026-03-12 05:09:12 +00:00
add spec_generator script
add generate-specs target to Taskfile. add generate-specs target as build dep.
This commit is contained in:
parent
bd868d4613
commit
03d8415f68
@ -30,8 +30,14 @@ tasks:
|
|||||||
- task: compress
|
- task: compress
|
||||||
- echo "Release complete"
|
- echo "Release complete"
|
||||||
|
|
||||||
|
generate-specs:
|
||||||
|
desc: Generate all spec files from templates
|
||||||
|
cmds:
|
||||||
|
- poetry run python tools/spec_generator.py --clean
|
||||||
|
|
||||||
build:
|
build:
|
||||||
desc: Build all artifacts
|
desc: Build all artifacts
|
||||||
|
deps: [generate-specs]
|
||||||
cmds:
|
cmds:
|
||||||
- for:
|
- for:
|
||||||
matrix:
|
matrix:
|
||||||
|
|||||||
192
tools/spec_generator.py
Normal file
192
tools/spec_generator.py
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Spec file generator for voicemeeter-compact builds.
|
||||||
|
Generates Python launcher files and PyInstaller spec files from templates.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Build configuration
|
||||||
|
THEMES = {
|
||||||
|
'azure': ['azure-light', 'azure-dark'],
|
||||||
|
'forest': ['forest-light', 'forest-dark'],
|
||||||
|
'sunvalley': ['sunvalley'], # Single variant, no light/dark
|
||||||
|
}
|
||||||
|
|
||||||
|
KINDS = ['basic', 'banana', 'potato']
|
||||||
|
|
||||||
|
# Templates
|
||||||
|
PYTHON_TEMPLATE = """import voicemeeterlib
|
||||||
|
|
||||||
|
import vmcompact
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
KIND_ID = '{kind}'
|
||||||
|
|
||||||
|
with voicemeeterlib.api(KIND_ID) as vmr:{theme_arg}
|
||||||
|
app = vmcompact.connect(KIND_ID, vmr{theme_param})
|
||||||
|
app.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
"""
|
||||||
|
|
||||||
|
SPEC_TEMPLATE = """# -*- mode: python ; coding: utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
added_files = [
|
||||||
|
( '../../vmcompact/img', 'img' ),{theme_files}
|
||||||
|
( '../../configs', 'configs' ),
|
||||||
|
]
|
||||||
|
|
||||||
|
a = Analysis(
|
||||||
|
['{script_name}'],
|
||||||
|
pathex=[],
|
||||||
|
binaries=[],
|
||||||
|
datas=added_files,
|
||||||
|
hiddenimports=[],
|
||||||
|
hookspath=[],
|
||||||
|
hooksconfig={{}},
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False,
|
||||||
|
)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||||
|
|
||||||
|
exe = EXE(
|
||||||
|
pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='{kind}',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=False,
|
||||||
|
disable_windowed_traceback=False,
|
||||||
|
argv_emulation=False,
|
||||||
|
target_arch=None,
|
||||||
|
codesign_identity=None,
|
||||||
|
entitlements_file=None,
|
||||||
|
)
|
||||||
|
coll = COLLECT(
|
||||||
|
exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
upx_exclude=[],
|
||||||
|
name='{kind}',
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def generate_python_file(theme_variant: str, kind: str, output_dir: Path) -> None:
|
||||||
|
"""Generate a Python launcher file."""
|
||||||
|
if theme_variant == 'sunvalley':
|
||||||
|
# Sunvalley doesn't use theme parameter
|
||||||
|
theme_arg = ''
|
||||||
|
theme_param = ''
|
||||||
|
else:
|
||||||
|
theme_arg = f"\n theme = '{theme_variant}'"
|
||||||
|
theme_param = ', theme=theme'
|
||||||
|
|
||||||
|
content = PYTHON_TEMPLATE.format(
|
||||||
|
kind=kind, theme_arg=theme_arg, theme_param=theme_param
|
||||||
|
)
|
||||||
|
|
||||||
|
filename = f'{theme_variant}-{kind}.py'
|
||||||
|
output_path = output_dir / filename
|
||||||
|
|
||||||
|
with open(output_path, 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f'Generated: {output_path}')
|
||||||
|
|
||||||
|
|
||||||
|
def generate_spec_file(theme_variant: str, kind: str, output_dir: Path) -> None:
|
||||||
|
"""Generate a PyInstaller spec file."""
|
||||||
|
script_name = f'{theme_variant}-{kind}.py'
|
||||||
|
|
||||||
|
if theme_variant == 'sunvalley':
|
||||||
|
# Sunvalley doesn't include theme files
|
||||||
|
theme_files = ''
|
||||||
|
else:
|
||||||
|
theme_base = theme_variant.split('-')[0] # 'azure' from 'azure-dark'
|
||||||
|
theme_files = f"\n ( '../../theme/{theme_base}', 'theme' ),"
|
||||||
|
|
||||||
|
content = SPEC_TEMPLATE.format(
|
||||||
|
script_name=script_name, theme_files=theme_files, kind=kind
|
||||||
|
)
|
||||||
|
|
||||||
|
filename = f'{theme_variant}-{kind}.spec'
|
||||||
|
output_path = output_dir / filename
|
||||||
|
|
||||||
|
with open(output_path, 'w') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
print(f'Generated: {output_path}')
|
||||||
|
|
||||||
|
|
||||||
|
def generate_all_files(output_base_dir: Path) -> None:
|
||||||
|
"""Generate all Python and spec files for all theme/kind combinations."""
|
||||||
|
for theme_family, theme_variants in THEMES.items():
|
||||||
|
theme_dir = output_base_dir / theme_family
|
||||||
|
theme_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for theme_variant in theme_variants:
|
||||||
|
for kind in KINDS:
|
||||||
|
generate_python_file(theme_variant, kind, theme_dir)
|
||||||
|
generate_spec_file(theme_variant, kind, theme_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def clean_existing_files(output_base_dir: Path) -> None:
|
||||||
|
"""Remove all existing generated files."""
|
||||||
|
for theme_family in THEMES.keys():
|
||||||
|
theme_dir = output_base_dir / theme_family
|
||||||
|
if theme_dir.exists():
|
||||||
|
for file in theme_dir.glob('*.py'):
|
||||||
|
file.unlink()
|
||||||
|
print(f'Removed: {file}')
|
||||||
|
for file in theme_dir.glob('*.spec'):
|
||||||
|
file.unlink()
|
||||||
|
print(f'Removed: {file}')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Generate spec files for voicemeeter-compact'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--clean', action='store_true', help='Clean existing files before generating'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--output-dir',
|
||||||
|
type=Path,
|
||||||
|
default=Path('spec'),
|
||||||
|
help='Output directory for spec files (default: spec)',
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.clean:
|
||||||
|
print('Cleaning existing files...')
|
||||||
|
clean_existing_files(args.output_dir)
|
||||||
|
|
||||||
|
print('Generating spec files...')
|
||||||
|
generate_all_files(args.output_dir)
|
||||||
|
print('Done!')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Loading…
x
Reference in New Issue
Block a user