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
|
||||
- echo "Release complete"
|
||||
|
||||
generate-specs:
|
||||
desc: Generate all spec files from templates
|
||||
cmds:
|
||||
- poetry run python tools/spec_generator.py --clean
|
||||
|
||||
build:
|
||||
desc: Build all artifacts
|
||||
deps: [generate-specs]
|
||||
cmds:
|
||||
- for:
|
||||
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