Skip to content

Building

Learn how to build gi from source and create distributable packages.

Prerequisites

System Requirements

  • Python: 3.10 or higher
  • Operating System: Linux, macOS, or Windows
  • Build Tools:
  • pip (latest version)
  • setuptools and wheel
  • build (for PEP 517 builds)

Development Dependencies

Install development dependencies:

# Clone the repository
git clone https://github.com/sempervent/gi.git
cd gi

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate

# Install in development mode
pip install -e ".[dev]"

Building from Source

Standard Build

Build the package using the standard Python build tools:

# Install build dependencies
pip install build

# Build the package
python -m build

# The built packages will be in dist/
ls dist/
# gi-0.1.0-py3-none-any.whl
# gi-0.1.0.tar.gz

Development Build

For development and testing:

# Install in editable mode
pip install -e .

# Or with development dependencies
pip install -e ".[dev]"

Creating Executables

Using PyInstaller

Create standalone executables for distribution:

# Install PyInstaller
pip install pyinstaller

# Build executable
pyinstaller gi.spec

# The executable will be in dist/gi
ls dist/
# gi  (executable)

Using the Build Script

Use the provided build script for convenience:

# Make the script executable
chmod +x scripts/build-local.sh

# Run the build
./scripts/build-local.sh

Cross-Platform Builds

Build for multiple platforms:

# Build for current platform
python scripts/build.py

# The script will create platform-specific executables
ls dist/
# darwin-arm64/gi
# linux-x86_64/gi
# windows-x86_64/gi.exe

Build Configuration

PyInstaller Spec File

The gi.spec file configures PyInstaller:

# gi.spec
a = Analysis(
    ['gi_standalone.py'],
    pathex=[],
    binaries=[],
    datas=[('gi', 'gi')],
    hiddenimports=[
        'gi.cli',
        'gi.combine', 
        'gi.fetch',
        'gi.names',
        'gi.util',
        'typer',
        'rich',
        'requests',
        'platformdirs',
    ],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=['gi'],  # Exclude PyGObject
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

Build Script Configuration

The scripts/build.py script handles the build process:

# scripts/build.py
def build_executables():
    """Build executables for all platforms."""
    platforms = [
        ("linux", "x86_64"),
        ("windows", "x86_64"), 
        ("darwin", "x86_64"),
        ("darwin", "arm64"),
    ]

    for platform, arch in platforms:
        build_for_platform(platform, arch)

Testing Builds

Local Testing

Test the built executable locally:

# Test the executable
./dist/gi --help

# Test basic functionality
./dist/gi list | head -5

# Test template combination
./dist/gi python node -o test.gitignore
cat test.gitignore
rm test.gitignore

Automated Testing

Run automated tests on built packages:

# Test the wheel package
pip install dist/gi-*.whl
gi --help

# Test the source distribution
pip install dist/gi-*.tar.gz
gi --help

# Run the test suite
pytest tests/

Distribution

PyPI Upload

Upload to PyPI (requires PyPI account):

# Install twine
pip install twine

# Upload to PyPI
twine upload dist/*

# Or upload to Test PyPI first
twine upload --repository testpypi dist/*

GitHub Releases

Create GitHub releases with executables:

# Use the release script
python scripts/release.py tag --version 1.0.0

# This will:
# 1. Build executables for all platforms
# 2. Create a Git tag
# 3. Push to GitHub
# 4. Trigger GitHub Actions to create a release

Build Optimization

Size Optimization

Optimize executable size:

# In gi.spec
exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='gi',
    debug=False,
    bootloader_ignore_signals=False,
    strip=True,  # Strip debug symbols
    upx=True,    # Compress with UPX
    upx_exclude=[],
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

Performance Optimization

Optimize for performance:

# In gi.spec
a = Analysis(
    ['gi_standalone.py'],
    pathex=[],
    binaries=[],
    datas=[('gi', 'gi')],
    hiddenimports=[
        # Only include necessary modules
        'gi.cli',
        'gi.combine',
        'gi.fetch', 
        'gi.names',
        'gi.util',
        'typer',
        'rich',
        'requests',
        'platformdirs',
    ],
    hookspath=[],
    hooksconfig={},
    runtime_hooks=[],
    excludes=[
        'gi',           # PyGObject
        'tkinter',      # GUI toolkit
        'matplotlib',   # Plotting library
        'numpy',        # Scientific computing
        'pandas',       # Data analysis
    ],
    win_no_prefer_redirects=False,
    win_private_assemblies=False,
    cipher=block_cipher,
    noarchive=False,
)

Troubleshooting

Common Build Issues

Import Errors

If you get import errors in the built executable:

# Add missing modules to hiddenimports in gi.spec
hiddenimports=[
    'gi.cli',
    'gi.combine',
    'gi.fetch',
    'gi.names', 
    'gi.util',
    'typer',
    'rich',
    'requests',
    'platformdirs',
    'pkg_resources',  # Add if needed
    'importlib_metadata',  # Add if needed
]

Missing Dependencies

If dependencies are missing:

# Check what's included
pyinstaller --debug=all gi.spec

# Add missing packages to datas
datas=[
    ('gi', 'gi'),
    ('typer', 'typer'),  # Add if needed
    ('rich', 'rich'),    # Add if needed
]

Large Executable Size

To reduce executable size:

# Use UPX compression
pip install upx-ucl

# Enable UPX in spec file
upx=True

# Exclude unnecessary modules
excludes=['tkinter', 'matplotlib', 'numpy', 'pandas']

Debug Builds

Create debug builds for troubleshooting:

# In gi.spec
exe = EXE(
    pyz,
    a.scripts,
    a.binaries,
    a.zipfiles,
    a.datas,
    [],
    name='gi-debug',
    debug=True,  # Enable debug mode
    bootloader_ignore_signals=False,
    strip=False,  # Don't strip symbols
    upx=False,    # Don't compress
    console=True,
    disable_windowed_traceback=False,
    argv_emulation=False,
    target_arch=None,
    codesign_identity=None,
    entitlements_file=None,
)

Continuous Integration

GitHub Actions

The project includes GitHub Actions workflows for automated building:

# .github/workflows/build.yml
name: Build Cross-Platform Executables

on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:

jobs:
  build:
    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            platform: linux
            arch: x86_64
          - os: windows-latest
            platform: windows
            arch: x86_64
          - os: macos-latest
            platform: macos
            arch: x86_64
          - os: macos-latest
            platform: macos
            arch: arm64

Local CI Testing

Test the CI build process locally:

# Install act (GitHub Actions runner)
# https://github.com/nektos/act

# Run the build workflow
act -j build

# Run specific job
act -j build --matrix os:ubuntu-latest,platform:linux,arch:x86_64

Advanced Build Options

Custom Entry Points

Create custom entry points:

# In pyproject.toml
[project.scripts]
gi = "gi.__main__:main"
gi-debug = "gi.__main__:main_debug"

# In gi/__main__.py
def main_debug():
    """Debug entry point with verbose logging."""
    import logging
    logging.basicConfig(level=logging.DEBUG)
    main()

Plugin System

Build with plugin support:

# In gi.spec
datas=[
    ('gi', 'gi'),
    ('gi/plugins', 'gi/plugins'),  # Include plugins
]

hiddenimports=[
    'gi.cli',
    'gi.combine',
    'gi.fetch',
    'gi.names',
    'gi.util',
    'gi.plugins',  # Include plugin system
]

This comprehensive guide covers all aspects of building gi from source, creating executables, and distributing the package.