WORKING
This commit is contained in:
263
scripts/build_addon.py
Executable file
263
scripts/build_addon.py
Executable file
@@ -0,0 +1,263 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Build script for Text Texture Generator addon.
|
||||
Creates ZIP packages for both full and free versions.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import zipfile
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
import tempfile
|
||||
import argparse
|
||||
import fnmatch
|
||||
|
||||
def get_project_root() -> Path:
|
||||
"""Get the project root directory."""
|
||||
return Path(__file__).parent.parent
|
||||
|
||||
def load_config() -> Dict:
|
||||
"""Load build configuration."""
|
||||
config_path = get_project_root() / "build" / "config.json"
|
||||
with open(config_path, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
def load_exclusions_config() -> Dict:
|
||||
"""Load file exclusions configuration."""
|
||||
config_path = get_project_root() / "build" / "file_exclusions.json"
|
||||
with open(config_path, 'r') as f:
|
||||
return json.load(f)
|
||||
|
||||
def clean_directory(path: Path):
|
||||
"""Clean a directory by removing all contents."""
|
||||
if path.exists():
|
||||
shutil.rmtree(path)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def should_exclude_file(file_path: Path, source_dir: Path, exclude_patterns: List[str], exclude_files: List[str]) -> bool:
|
||||
"""Check if a file should be excluded based on patterns and specific files."""
|
||||
file_str = str(file_path)
|
||||
name = file_path.name
|
||||
|
||||
# Get relative path from source directory for exclusion checking
|
||||
try:
|
||||
rel_path = file_path.relative_to(source_dir)
|
||||
rel_path_str = str(rel_path)
|
||||
except ValueError:
|
||||
rel_path_str = str(file_path)
|
||||
|
||||
# Check specific file exclusions
|
||||
for exclude_file in exclude_files:
|
||||
# Handle directory exclusions (ending with /)
|
||||
if exclude_file.endswith('/'):
|
||||
exclude_dir = exclude_file.rstrip('/')
|
||||
if rel_path_str.startswith(exclude_dir + '/') or rel_path_str == exclude_dir:
|
||||
return True
|
||||
# Handle exact file matches
|
||||
elif rel_path_str == exclude_file:
|
||||
return True
|
||||
|
||||
# Check general patterns (for cache files, etc.)
|
||||
for pattern in exclude_patterns:
|
||||
if pattern in file_str or pattern in name:
|
||||
return True
|
||||
|
||||
# Handle wildcards
|
||||
if pattern.startswith('*.'):
|
||||
extension = pattern[1:]
|
||||
if file_str.endswith(extension):
|
||||
return True
|
||||
|
||||
# Handle glob patterns
|
||||
if fnmatch.fnmatch(name, pattern) or fnmatch.fnmatch(rel_path_str, pattern):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def copy_source_files(source_dir: Path, dest_dir: Path, exclude_patterns: List[str], exclude_files: List[str] = None):
|
||||
"""Copy source files to destination, excluding specified patterns and files."""
|
||||
if exclude_files is None:
|
||||
exclude_files = []
|
||||
|
||||
print(f"Copying files from {source_dir} to {dest_dir}")
|
||||
print(f"Excluding files: {exclude_files}")
|
||||
print(f"Excluding patterns: {exclude_patterns}")
|
||||
|
||||
copied_count = 0
|
||||
excluded_count = 0
|
||||
|
||||
for item in source_dir.rglob('*'):
|
||||
if item.is_file():
|
||||
if should_exclude_file(item, source_dir, exclude_patterns, exclude_files):
|
||||
rel_path = item.relative_to(source_dir)
|
||||
print(f" Excluded: {rel_path}")
|
||||
excluded_count += 1
|
||||
else:
|
||||
# Calculate relative path
|
||||
rel_path = item.relative_to(source_dir)
|
||||
dest_file = dest_dir / rel_path
|
||||
|
||||
# Create parent directories
|
||||
dest_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy file
|
||||
shutil.copy2(item, dest_file)
|
||||
print(f" Copied: {rel_path}")
|
||||
copied_count += 1
|
||||
|
||||
print(f"Files copied: {copied_count}, excluded: {excluded_count}")
|
||||
|
||||
def create_zip_package(source_dir: Path, output_file: Path, addon_name: str):
|
||||
"""Create a ZIP package from source directory."""
|
||||
print(f"Creating ZIP package: {output_file}")
|
||||
|
||||
with zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
for file_path in source_dir.rglob('*'):
|
||||
if file_path.is_file():
|
||||
# Calculate archive path (include addon name as root folder)
|
||||
rel_path = file_path.relative_to(source_dir)
|
||||
archive_path = Path(addon_name) / rel_path
|
||||
|
||||
zip_file.write(file_path, archive_path)
|
||||
print(f" Added to ZIP: {archive_path}")
|
||||
|
||||
print(f"ZIP package created: {output_file}")
|
||||
|
||||
def get_version_from_git() -> Optional[str]:
|
||||
"""Get version from git tags."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['python3', 'build/sync_version.py', '--version'],
|
||||
capture_output=True, text=True, cwd=get_project_root()
|
||||
)
|
||||
if result.returncode == 0:
|
||||
# Extract version from output
|
||||
lines = result.stdout.strip().split('\n')
|
||||
for line in lines:
|
||||
if 'Synchronizing version:' in line:
|
||||
return line.split(':')[1].strip().split()[0]
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not get version from git: {e}")
|
||||
|
||||
return None
|
||||
|
||||
def build_addon(version_type: str, version: Optional[str] = None, output_dir: Optional[Path] = None):
|
||||
"""Build addon for specified version type."""
|
||||
config = load_config()
|
||||
exclusions_config = load_exclusions_config()
|
||||
project_root = get_project_root()
|
||||
|
||||
# Determine version
|
||||
if version is None:
|
||||
version = get_version_from_git() or "1.0.0"
|
||||
|
||||
# Set up paths
|
||||
source_dir = project_root / config['build']['source_dir']
|
||||
if output_dir is None:
|
||||
output_dir = project_root / config['build']['dist_dir']
|
||||
temp_dir = project_root / config['build']['temp_dir']
|
||||
|
||||
# Clean and create directories
|
||||
clean_directory(temp_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Sync version files for the specific version type (simplified - no feature flags)
|
||||
print(f"Synchronizing version files for {version_type} version {version}")
|
||||
sync_result = subprocess.run([
|
||||
'python3', 'build/sync_version.py',
|
||||
'--version', version,
|
||||
'--type', version_type
|
||||
], cwd=project_root)
|
||||
|
||||
if sync_result.returncode != 0:
|
||||
raise RuntimeError(f"Version synchronization failed")
|
||||
|
||||
# Get version-specific exclusions
|
||||
version_config = exclusions_config['version_configs'][version_type]
|
||||
exclude_files = version_config.get('exclude_files', [])
|
||||
exclude_patterns = config['build']['exclude_patterns'] + version_config.get('exclude_patterns', [])
|
||||
|
||||
print(f"\n📋 Build configuration for {version_type} version:")
|
||||
print(f"Max texture size: {version_config.get('max_texture_size', 'unlimited')}")
|
||||
print(f"Max concurrent operations: {version_config.get('max_concurrent_operations', 'unlimited')}")
|
||||
if exclude_files:
|
||||
print(f"Files to exclude: {exclude_files}")
|
||||
if version_config.get('exclude_patterns'):
|
||||
print(f"Additional patterns to exclude: {version_config.get('exclude_patterns')}")
|
||||
|
||||
# Copy source files to temp directory with version-specific exclusions
|
||||
build_temp_dir = temp_dir / f"{config['project']['id']}_{version_type}"
|
||||
copy_source_files(source_dir, build_temp_dir, exclude_patterns, exclude_files)
|
||||
|
||||
# Create output filename
|
||||
version_suffix = f"_{version_type}" if version_type == "free" else ""
|
||||
output_filename = f"{config['project']['id']}_v{version}{version_suffix}.zip"
|
||||
output_file = output_dir / output_filename
|
||||
|
||||
# Create ZIP package
|
||||
create_zip_package(build_temp_dir, output_file, config['project']['id'])
|
||||
|
||||
# Clean up temp directory
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
print(f"\n✅ Build complete!")
|
||||
print(f"Package: {output_file}")
|
||||
print(f"Version: {version} ({version_type})")
|
||||
|
||||
return output_file
|
||||
|
||||
def build_all_versions(version: Optional[str] = None):
|
||||
"""Build both full and free versions."""
|
||||
print("Building all versions...")
|
||||
|
||||
built_packages = []
|
||||
|
||||
# Build full version
|
||||
print("\n" + "="*50)
|
||||
print("BUILDING FULL VERSION")
|
||||
print("="*50)
|
||||
full_package = build_addon("full", version)
|
||||
built_packages.append(("full", full_package))
|
||||
|
||||
# Build free version
|
||||
print("\n" + "="*50)
|
||||
print("BUILDING FREE VERSION")
|
||||
print("="*50)
|
||||
free_package = build_addon("free", version)
|
||||
built_packages.append(("free", free_package))
|
||||
|
||||
print("\n" + "="*50)
|
||||
print("BUILD SUMMARY")
|
||||
print("="*50)
|
||||
for version_type, package_path in built_packages:
|
||||
file_size = package_path.stat().st_size / 1024 # KB
|
||||
print(f"{version_type.upper():>4}: {package_path.name} ({file_size:.1f} KB)")
|
||||
|
||||
return built_packages
|
||||
|
||||
def main():
|
||||
"""Main entry point."""
|
||||
parser = argparse.ArgumentParser(description="Build Text Texture Generator addon")
|
||||
parser.add_argument("--version", help="Version to build (defaults to git tag)")
|
||||
parser.add_argument("--type", choices=["free", "full", "all"], default="all",
|
||||
help="Version type to build")
|
||||
parser.add_argument("--output", type=Path, help="Output directory")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
if args.type == "all":
|
||||
build_all_versions(args.version)
|
||||
else:
|
||||
build_addon(args.type, args.version, args.output)
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Build failed: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user