Add PyInstaller build system for single executable distribution

- Added requirements-build.txt with PyInstaller dependencies
- Created fixture-manager.spec with comprehensive build configuration
- Added build.py automated build script with full workflow
- Created BUILD.md with detailed build instructions and troubleshooting
- Updated README.md with build section and executable distribution info
- Includes hooks for Flask-SQLAlchemy and pandas compatibility
- Supports cross-platform builds (Linux, Windows, macOS)
- Creates distribution package with executable and essential files
- Executable size ~80-120MB with all dependencies included
- No Python installation required on target systems
parent 1876c8db
Pipeline #169 canceled with stages
# Building Fixture Manager as a Single Executable
This document explains how to build the Fixture Manager daemon as a single executable file using PyInstaller.
## Prerequisites
- Python 3.8 or higher
- pip (Python package installer)
- At least 2GB of free disk space for the build process
- Internet connection for downloading dependencies
## Quick Build
The easiest way to build the executable is using the automated build script:
```bash
# Make the build script executable (Linux/macOS)
chmod +x build.py
# Run the build script
python build.py
```
This will:
1. Install all required dependencies
2. Clean previous build artifacts
3. Create the single executable file
4. Test the executable
5. Create a distribution package
## Manual Build Process
If you prefer to build manually or need more control:
### 1. Install Build Dependencies
```bash
# Install PyInstaller and build tools
pip install -r requirements-build.txt
# Install runtime dependencies
pip install -r requirements.txt
```
### 2. Clean Previous Builds
```bash
# Remove build artifacts
rm -rf build/ dist/ __pycache__/
find . -name "*.pyc" -delete
```
### 3. Build with PyInstaller
```bash
# Build using the spec file
pyinstaller --clean fixture-manager.spec
```
### 4. Test the Executable
```bash
# Test the built executable
./dist/fixture-manager --help
```
## Build Output
After a successful build, you'll find:
- **`dist/fixture-manager`** - The main executable file (Linux/macOS)
- **`dist/fixture-manager.exe`** - The main executable file (Windows)
- **`distribution/`** - Complete distribution package with:
- The executable file
- README.md
- .env.example (configuration template)
- database/schema.sql
- install.sh
- run.sh (Linux/macOS launcher script)
- run.bat (Windows launcher script)
## Distribution
The single executable file contains:
- All Python dependencies
- The complete Flask application
- Database models and utilities
- Authentication system
- File upload handlers
- Web dashboard
- RESTful API
- All necessary libraries
### File Size
The executable will be approximately 80-120MB depending on your platform.
### Platform Compatibility
- **Linux**: Build on the target distribution for best compatibility
- **Windows**: Build on Windows 10/11 for maximum compatibility
- **macOS**: Build on macOS 10.15+ for modern compatibility
## Running the Executable
### Basic Usage
```bash
# Start the daemon
./fixture-manager start
# Stop the daemon
./fixture-manager stop
# Check status
./fixture-manager status
# Run in foreground (for testing)
./fixture-manager start --foreground
```
### Configuration
The executable looks for configuration in these locations (in order):
1. `.env` file in the same directory as the executable
2. Environment variables
3. Default configuration values
### First Run Setup
1. Copy `.env.example` to `.env`
2. Edit `.env` with your database and security settings
3. Ensure MySQL is installed and running
4. Run the executable to initialize the database
## Troubleshooting
### Common Build Issues
**1. Missing Dependencies**
```bash
# Install missing system packages (Ubuntu/Debian)
sudo apt-get install python3-dev libmysqlclient-dev build-essential
# Install missing system packages (CentOS/RHEL)
sudo yum install python3-devel mysql-devel gcc gcc-c++
```
**2. PyInstaller Import Errors**
If you get import errors during build, add the missing modules to the `hiddenimports` list in `fixture-manager.spec`.
**3. Large Executable Size**
The executable includes all dependencies. To reduce size:
- Remove unused dependencies from requirements.txt
- Use `--exclude-module` in the spec file for unnecessary modules
**4. Runtime Errors**
If the executable fails at runtime:
- Check that all data files are included in the `datas` section of the spec file
- Verify that hidden imports are correctly specified
- Test with `--debug` flag for more verbose output
### Platform-Specific Issues
**Linux:**
- Ensure all shared libraries are available on the target system
- Consider building on the oldest supported distribution
**Windows:**
- Install Microsoft Visual C++ Redistributable on target systems
- Some antivirus software may flag the executable as suspicious
**macOS:**
- Code signing may be required for distribution
- Gatekeeper may prevent execution of unsigned binaries
## Advanced Configuration
### Custom Build Options
You can modify `fixture-manager.spec` to customize the build:
```python
# Add additional data files
datas = [
('custom_templates/', 'templates/'),
('static_files/', 'static/'),
]
# Exclude unnecessary modules
excludes = [
'tkinter',
'matplotlib',
'test',
]
# Add custom hidden imports
hiddenimports = [
'your.custom.module',
]
```
### Build Optimization
For smaller executables:
1. Use `--onefile` for single file distribution
2. Use `--strip` to remove debug symbols (Linux/macOS)
3. Use `--upx` to compress the executable (if UPX is installed)
### Cross-Platform Building
To build for different platforms:
1. Use virtual machines or containers for each target platform
2. Consider using GitHub Actions or similar CI/CD for automated builds
3. Docker can be used for consistent Linux builds
## Security Considerations
### Executable Security
- The executable contains all source code (though compiled)
- Sensitive configuration should still use environment variables
- Consider code obfuscation for proprietary deployments
### Distribution Security
- Sign executables for production distribution
- Provide checksums for integrity verification
- Use secure channels for distribution
## Deployment
### Single Server Deployment
1. Copy the executable to the target server
2. Copy the configuration files
3. Set up the database
4. Run the executable
### Multiple Server Deployment
1. Build once on a compatible system
2. Distribute the same executable to all servers
3. Use different configuration files per server
4. Consider using configuration management tools
## Support
For build issues:
1. Check the build logs for specific error messages
2. Verify all dependencies are correctly installed
3. Test the build on a clean system
4. Check PyInstaller documentation for platform-specific issues
The single executable provides a convenient way to distribute the Fixture Manager daemon without requiring Python installation on target systems.
\ No newline at end of file
...@@ -402,6 +402,37 @@ python daemon.py start --foreground --config development ...@@ -402,6 +402,37 @@ python daemon.py start --foreground --config development
- Access log monitoring - Access log monitoring
- Incident response procedures - Incident response procedures
## Building Single Executable
The project can be packaged as a single executable file for easy distribution:
### Quick Build
```bash
# Run the automated build script
python build.py
```
### Manual Build
```bash
# Install build dependencies
pip install -r requirements-build.txt
# Build with PyInstaller
pyinstaller --clean fixture-manager.spec
```
The executable will be created in the `dist/` directory and includes:
- All Python dependencies
- Complete Flask application
- Database utilities and models
- Web dashboard and API
- Configuration templates
**Executable Size**: ~80-120MB
**No Python Installation Required** on target systems
See [BUILD.md](BUILD.md) for detailed build instructions and troubleshooting.
## Contributing ## Contributing
1. Fork the repository 1. Fork the repository
...@@ -419,6 +450,7 @@ This project is licensed under the MIT License - see the LICENSE file for detail ...@@ -419,6 +450,7 @@ This project is licensed under the MIT License - see the LICENSE file for detail
For support and questions: For support and questions:
- Check the troubleshooting section - Check the troubleshooting section
- Review system logs - Review system logs
- See BUILD.md for executable build issues
- Contact system administrator - Contact system administrator
--- ---
......
#!/usr/bin/env python3
"""
Build script for creating a single-file executable of the Fixture Manager daemon
using PyInstaller.
"""
import os
import sys
import subprocess
import shutil
import platform
from pathlib import Path
def run_command(cmd, cwd=None):
"""Run a command and return the result"""
print(f"Running: {' '.join(cmd)}")
try:
result = subprocess.run(cmd, cwd=cwd, check=True, capture_output=True, text=True)
if result.stdout:
print(result.stdout)
return True
except subprocess.CalledProcessError as e:
print(f"Error: {e}")
if e.stdout:
print(f"STDOUT: {e.stdout}")
if e.stderr:
print(f"STDERR: {e.stderr}")
return False
def check_python_version():
"""Check if Python version is compatible"""
if sys.version_info < (3, 8):
print("Error: Python 3.8 or higher is required")
return False
print(f"Python version: {sys.version}")
return True
def install_build_dependencies():
"""Install PyInstaller and other build dependencies"""
print("Installing build dependencies...")
# Install build requirements
if not run_command([sys.executable, "-m", "pip", "install", "-r", "requirements-build.txt"]):
print("Failed to install build dependencies")
return False
# Install runtime requirements
if not run_command([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"]):
print("Failed to install runtime dependencies")
return False
return True
def clean_build_directories():
"""Clean previous build artifacts"""
print("Cleaning build directories...")
dirs_to_clean = ['build', 'dist', '__pycache__']
for dir_name in dirs_to_clean:
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
print(f"Removed {dir_name}")
# Clean .pyc files
for root, dirs, files in os.walk('.'):
for file in files:
if file.endswith('.pyc'):
os.remove(os.path.join(root, file))
def create_hooks_directory():
"""Create PyInstaller hooks for better compatibility"""
hooks_dir = Path("hooks")
hooks_dir.mkdir(exist_ok=True)
# Create hook for Flask-SQLAlchemy
hook_content = '''
from PyInstaller.utils.hooks import collect_all
datas, binaries, hiddenimports = collect_all('flask_sqlalchemy')
hiddenimports += [
'sqlalchemy.dialects.mysql',
'sqlalchemy.dialects.mysql.pymysql',
'sqlalchemy.pool',
'sqlalchemy.engine.default',
]
'''
with open(hooks_dir / "hook-flask_sqlalchemy.py", "w") as f:
f.write(hook_content)
# Create hook for pandas
pandas_hook = '''
from PyInstaller.utils.hooks import collect_all
datas, binaries, hiddenimports = collect_all('pandas')
hiddenimports += [
'pandas._libs.tslibs.base',
'pandas._libs.tslibs.ccalendar',
'pandas._libs.tslibs.dtypes',
'pandas._libs.tslibs.field_array',
'pandas._libs.tslibs.nattype',
'pandas._libs.tslibs.np_datetime',
'pandas._libs.tslibs.offsets',
'pandas._libs.tslibs.parsing',
'pandas._libs.tslibs.period',
'pandas._libs.tslibs.resolution',
'pandas._libs.tslibs.strptime',
'pandas._libs.tslibs.timedeltas',
'pandas._libs.tslibs.timestamps',
'pandas._libs.tslibs.timezones',
'pandas._libs.tslibs.tzconversion',
'pandas._libs.tslibs.vectorized',
]
'''
with open(hooks_dir / "hook-pandas.py", "w") as f:
f.write(pandas_hook)
def build_executable():
"""Build the executable using PyInstaller"""
print("Building executable with PyInstaller...")
# Create hooks directory
create_hooks_directory()
# Build command
cmd = [
sys.executable, "-m", "PyInstaller",
"--clean",
"--onefile",
"--console",
"--name", "fixture-manager",
"--additional-hooks-dir", "hooks",
"fixture-manager.spec"
]
if not run_command(cmd):
print("PyInstaller build failed")
return False
return True
def test_executable():
"""Test the built executable"""
print("Testing the built executable...")
executable_name = "fixture-manager"
if platform.system() == "Windows":
executable_name += ".exe"
executable_path = Path("dist") / executable_name
if not executable_path.exists():
print(f"Executable not found: {executable_path}")
return False
# Test help command
cmd = [str(executable_path), "--help"]
if not run_command(cmd):
print("Executable test failed")
return False
print(f"Executable built successfully: {executable_path}")
print(f"File size: {executable_path.stat().st_size / (1024*1024):.1f} MB")
return True
def create_distribution_package():
"""Create a distribution package with the executable and necessary files"""
print("Creating distribution package...")
dist_dir = Path("distribution")
if dist_dir.exists():
shutil.rmtree(dist_dir)
dist_dir.mkdir()
# Copy executable
executable_name = "fixture-manager"
if platform.system() == "Windows":
executable_name += ".exe"
src_exe = Path("dist") / executable_name
dst_exe = dist_dir / executable_name
if src_exe.exists():
shutil.copy2(src_exe, dst_exe)
# Make executable on Unix systems
if platform.system() != "Windows":
os.chmod(dst_exe, 0o755)
# Copy essential files
files_to_copy = [
"README.md",
".env.example",
"database/schema.sql",
"install.sh"
]
for file_path in files_to_copy:
src = Path(file_path)
if src.exists():
if src.is_file():
dst = dist_dir / src.name
shutil.copy2(src, dst)
else:
dst = dist_dir / src.name
shutil.copytree(src, dst)
# Create a simple run script
run_script_content = f'''#!/bin/bash
# Fixture Manager Daemon Runner
# Set executable permissions
chmod +x ./{executable_name}
# Run the daemon
./{executable_name} "$@"
'''
run_script = dist_dir / "run.sh"
with open(run_script, "w") as f:
f.write(run_script_content)
os.chmod(run_script, 0o755)
# Create Windows batch file
if platform.system() == "Windows":
bat_content = f'''@echo off
REM Fixture Manager Daemon Runner
{executable_name} %*
'''
bat_file = dist_dir / "run.bat"
with open(bat_file, "w") as f:
f.write(bat_content)
print(f"Distribution package created in: {dist_dir}")
return True
def main():
"""Main build process"""
print("=" * 60)
print("Fixture Manager - PyInstaller Build Script")
print("=" * 60)
# Check Python version
if not check_python_version():
sys.exit(1)
# Clean previous builds
clean_build_directories()
# Install dependencies
if not install_build_dependencies():
print("Failed to install dependencies")
sys.exit(1)
# Build executable
if not build_executable():
print("Build failed")
sys.exit(1)
# Test executable
if not test_executable():
print("Executable test failed")
sys.exit(1)
# Create distribution package
if not create_distribution_package():
print("Failed to create distribution package")
sys.exit(1)
print("=" * 60)
print("Build completed successfully!")
print("=" * 60)
print("Distribution files are in the 'distribution' directory")
print("You can now distribute the single executable file.")
print("=" * 60)
if __name__ == "__main__":
main()
\ No newline at end of file
# Build requirements for PyInstaller
pyinstaller==6.3.0
auto-py-to-exe==2.41.0
# Runtime requirements (included in the main requirements.txt)
Flask==2.3.3
Flask-SQLAlchemy==3.0.5
Flask-Login==0.6.3
Flask-WTF==1.1.1
Flask-JWT-Extended==4.5.3
PyMySQL==1.1.0
cryptography==41.0.4
pandas==2.1.1
openpyxl==3.1.2
xlrd==2.0.1
python-daemon==3.0.1
lockfile==0.12.2
bcrypt==4.0.1
Werkzeug==2.3.7
WTForms==3.0.1
python-dotenv==1.0.0
gunicorn==21.2.0
psutil==5.9.5
watchdog==3.0.0
click==8.1.7
colorlog==6.7.0
marshmallow==3.20.1
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment