Developing and building RatioLab
For normal installation, use the bundled installers. This page is for contributors and maintainers.
Work on a checkout
Install the native GTK/Python bindings for your OS as described below, then create a development environment using that same interpreter. Linux example:
python3 -m venv --system-site-packages .venv
.venv/bin/python -m pip install -e .
.venv/bin/ratiolab --diagnose
.venv/bin/ratiolab
.venv/bin/python -m unittest discover -s tests -p 'test_*.py' -v
src/ contains application modules and runtime resources. tests/ contains
automated checks; tests are part of the source distribution and are excluded
from the installed wheel and native application. GUI checks need a working
desktop/GL context; Linux CI uses Xvfb and Mesa.
Hosted macOS CI sets GSK_RENDERER=cairo for GTK shell composition after an
observed GSK software-GPU compositor stall. This does not change RatioLab's
product defaults or its Gtk.GLArea: the preview still has to initialize and
render with native OpenGL and pass the same framebuffer/workspace checks and
180-second workspace deadline. CI results must confirm the runner-specific fix.
For a quick source launch use ./launch.sh. A headless source command from
the repository root can use PYTHONPATH=src python3 -m ratiolab_cli --diagnose.
On PowerShell, set $env:PYTHONPATH = "src" before the corresponding command.
The editable installation above avoids that extra environment setting.
Building the native installers
These commands are for the build machine. They are not required on a computer receiving the installer. Each OS is built natively; Linux does not cross-compile Windows Setup or a macOS application.
The shared build is scripts/build-bundle.py, with
packaging/RatioLab.spec and project hooks using PyInstaller's official GI
helpers configured for Gtk/Gdk 4.0. It creates an onedir application, avoiding
a temporary unpacked GTK runtime at every launch. Native libraries and code are
separate files and third-party notices remain accessible. GPU drivers are not
bundled. The build fails if a collected native file has no package attribution
or its required license documents cannot be collected.
Build on Ubuntu 24.04 to retain the glibc 2.39 baseline:
sudo apt install python3-venv libpython3.12 python3-gi python3-gi-cairo gir1.2-gtk-4.0 gir1.2-girepository-2.0 libepoxy0 libgl1 libegl1 binutils dpkg-dev
/usr/bin/python3 -m venv --system-site-packages .bundle-env
.bundle-env/bin/python -m pip install -r packaging/requirements-build.txt
.bundle-env/bin/python scripts/build-bundle.py
.bundle-env/bin/python scripts/package-linux.py --bundle dist/bundles/linux-x86_64/RatioLab --deb
The shared libpython3.12 package is required by PyInstaller in addition to the
Python interpreter/venv package. gir1.2-girepository-2.0 supplies the build-time
introspection namespace used by PyInstaller's hooks. Use --gui with build-bundle.py on a desktop
to require actual GL rendering and full workspace exports. In CI, Xvfb and Mesa
provide this check on the Ubuntu 24.04 runner, with explicit Mesa DRI/GLX/EGL
and fontconfig packages for repeatable headless rendering.
On Windows, use the MSYS2 UCRT64 build environment and its native PyInstaller package (not python.org Python or a PyPI PyInstaller wheel):
pacman -S --needed mingw-w64-ucrt-x86_64-python mingw-w64-ucrt-x86_64-pyinstaller mingw-w64-ucrt-x86_64-gtk4 mingw-w64-ucrt-x86_64-python-gobject mingw-w64-ucrt-x86_64-python-cairo mingw-w64-ucrt-x86_64-libepoxy mingw-w64-ucrt-x86_64-angleproject
export PYGI_DLL_PATH="$(cygpath -w /ucrt64/bin)"
/ucrt64/bin/python.exe scripts/build-bundle.py
Install Inno Setup 6.3+ on the Windows build machine. From PowerShell in this
source folder run ./scripts/package-windows.ps1. It verifies the native
bundle hashes and executes a frozen self-test without MSYS2 on PATH before
compiling scripts/package-windows.iss. Setup installs under
%LOCALAPPDATA%/Programs/RatioLab with per-user shortcuts and an uninstaller.
An optional -CertificateThumbprint selects an existing code-signing
certificate in the maintainer's CurrentUser/My certificate store to sign the
installer; by default it remains unsigned.
MSYS2 file ownership is read once from its installed-file inventory. For the
specific package versions that omit license texts, verified upstream notices
are kept in packaging/notices/msys2/registry.json and adjacent text files.
The build checks their versions, license declarations and file hashes. A new
dependency version requires reviewing its exact source archive and updating
the registry; an unrecognized version or changed notice remains a build error.
On macOS 15+, select a Homebrew Python matching the installed GI bindings:
brew install pygobject3 gtk4 gobject-introspection
. scripts/macos-python.sh
python=$(choose_macos_python)
"$python" -m venv --system-site-packages .bundle-env
.bundle-env/bin/python -m pip install -r packaging/requirements-build.txt
MACOSX_DEPLOYMENT_TARGET=15.0 .bundle-env/bin/python scripts/build-bundle.py
RATIOLAB_BUILD_PYTHON="$PWD/.bundle-env/bin/python" sh scripts/package-macos.sh
Build separately on Intel and Apple Silicon. PyInstaller constructs a real
RatioLab.app, rewrites the native library paths and signs its code ad hoc when
no identity is provided. The builder checks the minimum OS load commands of all
included Mach-O binaries against LSMinimumSystemVersion. The DMG packager
verifies the app signature, creates the Applications shortcut and uses
hdiutil to produce the compressed image. It does not change the signed app.
To use an existing Developer ID identity, set RATIOLAB_CODESIGN_IDENTITY for
both the bundle build and DMG packaging. PyInstaller handles the app's
signing order. RATIOLAB_ENTITLEMENTS may point to an existing entitlements file
if that identity/build needs one. To submit the finished DMG to Apple's notary
service, set RATIOLAB_NOTARY_PROFILE to an existing notarytool keychain
profile when running the packager; it submits, waits, staples and validates the
ticket. These options require the maintainer's actual credentials and were not
run here.
Outputs are under dist/bundles/<system>-<arch>/RatioLab on Linux/Windows and
dist/bundles/macos-<arch>/RatioLab.app on macOS. Final installers and SHA-256
checksums go to dist/installers/. Every runtime has a bundle-manifest.json
containing version, architecture, OS baseline and file hashes. The manifest is
inside the onedir folder on Linux/Windows and beside the signed app on macOS.
License notices and components.json are inside _internal/notices or the
macOS app's Resources/notices directory.
The builder's default self-test runs the frozen executable from a neutral
directory with Python/GTK environment paths removed and no MSYS2/Homebrew on
PATH. It checks local typelibs, all catalog families, STL/STEP, UTF-8 configurations
and preferences in a temporary user profile. --gui also verifies nonblank
pixels and complete workspace exports. To repeat on a built folder:
python scripts/build-bundle.py --verify-only /path/to/RatioLab --gui
For a windowed executable, use RatioLab.exe --self-test --report report.json
or the app bundle's Contents/MacOS/RatioLab executable. The JSON report and
exit status are usable even with no console. Normal windowed launches write
Python errors to the user's RatioLab data directory under logs/ratiolab.log.
.github/workflows/installers.yml builds Linux x86-64, Windows x86-64 and both
macOS architectures on their native runners. It creates real installers and
tests their installed/copied applications with package-manager runtime paths
removed. Linux GUI/GL is mandatory; Windows/macOS GUI checks are always
attempted, with explicit lack of runner graphics capability reported separately.
Use native_gui=true to require that capability as well. The
workflow uploads build artifacts; publication is a separate maintainer step.
the completed workflow runs records completed native runs and the checks actually performed.
Build configuration follows PyInstaller's GI hook settings, PyInstaller's external program guidance, PyInstaller's macOS signing and bundles, MSYS2's native PyInstaller package, Inno Setup's per-user installation mode and Apple's distribution signing documentation.
Source and wheel installation (development)
RatioLab 0.1 is a Python/GTK 4 desktop application. Its Python wheel and source archive can be installed on another computer; GTK, PyGObject, Cairo and libepoxy come from that computer's native package manager. The wheel is not a self-contained Windows installer, macOS application bundle or Linux AppImage. An Internet connection is needed to install dependencies, not to generate or export models.
Required: Python 3.10 or newer, GTK 4.12 or newer, PyGObject, Pycairo, libepoxy and a graphics
driver providing desktop OpenGL 3.3+ or OpenGL ES 3.0+. GTK negotiates
an available API; Windows includes ANGLE as a GLES backend. The same Python must load the
native bindings and run RatioLab. A virtual environment uses
--system-site-packages so it can see those bindings. The wheel's py3-none-any
tag describes its Python code and resources, not the native runtime.
Linux source/wheel
On Debian 13+/Ubuntu 24.04+ (with GTK >=4.12), install native packages:
sudo apt update
sudo apt install python3 python3-venv python3-pip python3-gi python3-gi-cairo gir1.2-gtk-4.0 libepoxy0
On Fedora use sudo dnf install python3 python3-pip python3-gobject gtk4 libepoxy;
on Arch use sudo pacman -S python python-pip python-gobject python-cairo gtk4 libepoxy.
Use the distribution's Python, rather than a separate pyenv/Conda interpreter.
The default GTK packages on Debian 12/Ubuntu 22.04 are too old. The runtime
diagnostic stops with the required version instead of failing during UI setup.
From an unpacked source archive or this checkout:
sh scripts/install-linux.sh
This creates a user virtual environment under
~/.local/share/ratiolab/venv, installs RatioLab, checks its runtime and registers
the Linux application menu/desktop launcher. Choose another location with
RATIOLAB_VENV=/absolute/path sh scripts/install-linux.sh. Choose the native
Python with RATIOLAB_PYTHON=/usr/bin/python3 if needed. The registration keeps
local.solidcraft.reducers, so it matches existing GTK window grouping.
To install a wheel instead (replace the example wheel path):
/usr/bin/python3 -m venv --system-site-packages "$HOME/.local/share/ratiolab/venv"
"$HOME/.local/share/ratiolab/venv/bin/python" -m pip install --no-deps /path/to/ratiolab-0.1.3-py3-none-any.whl
"$HOME/.local/share/ratiolab/venv/bin/ratiolab" --diagnose
"$HOME/.local/share/ratiolab/venv/bin/ratiolab-install-desktop"
Windows source/wheel (x86-64, MSYS2 UCRT64)
Install MSYS2 using its x86-64 installer. Open MSYS2
UCRT64 from the Start menu, run pacman -Syu, and follow any request to close,
reopen and update the terminal again. Then install one coherent native runtime:
pacman -S --needed mingw-w64-ucrt-x86_64-python mingw-w64-ucrt-x86_64-python-pip mingw-w64-ucrt-x86_64-python-setuptools mingw-w64-ucrt-x86_64-python-wheel mingw-w64-ucrt-x86_64-gtk4 mingw-w64-ucrt-x86_64-python-gobject mingw-w64-ucrt-x86_64-python-cairo mingw-w64-ucrt-x86_64-libepoxy mingw-w64-ucrt-x86_64-angleproject
Unpack the RatioLab source archive in a folder you own, navigate there in that
UCRT64 terminal (cd /c/Users/YourName/Downloads/ratiolab-0.1.3 for example), then:
bash scripts/install-windows-msys2.sh
"$HOME/.local/share/ratiolab/venv/bin/python.exe" -m ratiolab_cli --diagnose-gui
"$HOME/.local/share/ratiolab/venv/bin/python.exe" -m ratiolab_cli
MSYS2's Python has bin inside its virtual environments. It is a separate build
from python.org/Windows Store Python; their compiled extension modules are
incompatible. Always use /ucrt64/bin/python.exe to create this environment and
its bin/python.exe to run/install RatioLab. Do not combine python.org pip with
MSYS2's GTK bindings. Keep the MSYS2 runtime installed; this is a source/wheel
installation, not a portable executable.
For a wheel, create the same environment with
/ucrt64/bin/python.exe -m venv --system-site-packages "$HOME/.local/share/ratiolab/venv",
then run its bin/python.exe -m pip install --no-deps /c/path/to/ratiolab-0.1.3-py3-none-any.whl.
Run from the UCRT64 terminal so GTK's native resource paths remain available.
RatioLab also adds that Python installation's bin to DLL discovery before
importing PyGObject. Windows ARM64 is not covered by this UCRT64 x86-64 recipe.
macOS source/wheel (Homebrew)
Install Homebrew, then:
brew install pygobject3 gtk4
sh scripts/install-macos.sh
"$HOME/Library/Application Support/RatioLab/venv/bin/ratiolab" --diagnose-gui
"$HOME/Library/Application Support/RatioLab/venv/bin/ratiolab"
The script inspects the installed pygobject3 formula's Python dependencies and
selects an installed Homebrew interpreter that can actually import both GTK 4
and Cairo. It does not assume that python3 on your PATH is compatible. If none
works, it stops before creating the environment. Homebrew may change the Python
versions its bindings support; reinstall/recreate the environment after a native
Python upgrade. The script works with Intel or Apple Silicon Homebrew prefixes
when the native formulae are available for that macOS release. Run from a normal
logged-in macOS desktop session; no XQuartz is needed for GTK's Quartz backend.
For a wheel, source scripts/macos-python.sh, set
python=$(choose_macos_python), create a --system-site-packages environment with
that interpreter, then use its bin/python -m pip install --no-deps /path/to/ratiolab-0.1.3-py3-none-any.whl.
For a self-contained app, use the DMGs described above. Those packages
use ad-hoc signing, without a Developer ID identity or Apple notarization.
Check the installation
ratiolab --diagnose prints the actual Python, GTK/Cairo versions, libepoxy,
resource paths and default data locations. It needs no graphical display.
ratiolab --diagnose-gui opens a small window, compiles the real preview shaders
and renders a gear using the active driver. Run it on the target computer before
assuming 3D support. An unavailable/old GL driver is reported as a failed check.
Remote desktop, virtual machines and CI runners can have different graphics
capabilities from a local session.
After installation you can remove or move the original checkout/source archive.
Launch the installed environment's ratiolab, not its former launch.sh.
Source development remains supported with ./launch.sh or python3 src/app.py;
python3 src/install_desktop.py registers that particular checkout instead.
User data and existing configurations
| System | Preferences | Default exports and generated STEP assets |
|---|---|---|
| Linux | $XDG_CONFIG_HOME/ratiolab or ~/.config/ratiolab |
$XDG_DATA_HOME/ratiolab/exports or ~/.local/share/ratiolab/exports |
| Windows | %APPDATA%/RatioLab |
%LOCALAPPDATA%/RatioLab/exports |
| macOS | ~/Library/Application Support/RatioLab |
~/Library/Application Support/RatioLab/exports |
The UI's Change folder… overrides the export location. Preferences are UTF-8
JSON in ui-preferences.json, saved atomically, and never written into the
installed code/resources. If that file does not exist, RatioLab reads the old
preferenze-ui.json in the user directory, then the source checkout's old file
if needed. The next preference save writes the English filename, preserving
the original file and settings. A present but invalid new file takes precedence
over legacy files. For a fresh
installation, select an existing export folder in the UI. Previously generated
files are not copied, moved or deleted by installation/uninstallation. The
solidcraft-riduttori/1 configuration format and local.solidcraft.reducers
application ID are preserved.
Catalogs and runtime branding live in src/ratiolab_resources/. The source
launcher is launch.sh; reusable project configurations are in examples/. Generated reports use .calculations.json,
.calculations.txt and .machining.json; generated STEP assets default to a
solids/ subdirectory. These names do not change project-format identifiers,
parameter keys or the ability to reopen earlier configurations. Existing user
exports are retained as saved.
The GUI uses the same application ID for GTK, the GLib program name, Linux
desktop-file matching and the Windows taskbar/installer shortcuts. The approved
logo is included as a 128/256 px hicolor icon theme for native window icons;
the original branding PNG and executable ICO/ICNS artwork are unchanged.
Source launches, installed wheels and frozen bundles resolve this theme from
their own resources, independently of the working directory. On GNOME/Wayland,
the application menu entry supplies the dock icon: register the selected source
installation with python3 src/install_desktop.py, or an installed wheel with
ratiolab-install-desktop (use --dry-run to inspect the paths first). Run the
source command from the checkout root with the Python interpreter used to launch
RatioLab. Registration generates the desktop entry using that installation's
absolute executable and icon paths; a copied static launcher cannot resolve
those paths reliably. Native Linux installers register their own entry. Development
demo windows should keep the canonical ID and use NON_UNIQUE when a separate
process is needed; changing the ID also requires a corresponding desktop entry.
Optional Autodesk Fusion connection
The Fusion integration guide describes the user workflow. The
standalone fusion_connection client prepares persistent per-part STEP packages
and submits a hashed manifest to the add-in under
src/ratiolab_resources/fusion_addin/RatioLabBridge/. This directory is plain
resource data in wheels and frozen bundles because Fusion loads it with its own
Python and SDK. It must not be imported by RatioLab's Python environment.
The add-in owns a localhost listener and dispatches the two protocol methods through a Fusion CustomEvent. CAD operations run on Fusion's main thread. Tests cover the real client/server transport with a simulated dispatcher and a mocked SDK separately; a live Fusion import is additional evidence, not implied by those tests. On Linux the native client can connect to the Windows SDK host running through Wine. It discovers the selected prefix/profile and stages transfer packages on its C: drive, avoiding a dependency on the Z: mapping. Wine path/protocol fixtures do not constitute a live Fusion-on-Linux test.
Optional SolidCraft connection
STL generation and export are local and require no CAD installation. RatioLab
does not install SolidCraft or guarantee a compatible SolidCraft build exists
on each platform. If you have one with the authenticated local control protocol,
start it with --control 7878 and configure its actual token directory using
the absolute environment variable SOLIDCRAFT_CONTROL_DIR.
On Linux the existing ~/.local/share/solidcraft/control (or XDG equivalent)
and ~/AppImages/solidcraft.appimage defaults remain. On Windows/macOS the token
directory must be explicitly configured; no AppImage path is guessed. To allow
launching a compatible CAD executable, set SOLIDCRAFT_EXECUTABLE to its full
path (for a macOS .app, its actual executable under Contents/MacOS, not the
bundle directory). Paths with spaces are passed as one argument, without a
shell. A running compatible CAD instance is reused first.
Build and verification
Use a development environment with build, setuptools>=77 and wheel:
python -m pip install build 'setuptools>=77' wheel
python -m build
python scripts/verify-artifacts.py dist
python -m build creates the sdist, then builds the wheel from that sdist.
Both include explicit code/resource lists; no preferences, credentials, work/,
design/, outputs/, .git or generated models are distributed. Runtime assets are packaged from src/ratiolab_resources/; the small
build-support/ hook adds canonical user documentation and legal notices.
It does not copy developer notes or test files into the installed application.
Install the wheel into a fresh native environment outside the checkout. Copy
scripts/installed_smoke.py into a neutral directory, change there, and run it
with that environment's Python, using --forbid-root /absolute/source/path and
optionally --gui. It verifies installed imports/resources, UTF-8 configuration
round trips, generated STEP, standalone STL and preferences under a disposable
profile. The GUI option also opens the full workspace and tests its save/load
path. A read-only installed package must pass these checks.
.github/workflows/portable.yml defines Linux, MSYS2 Windows and Homebrew macOS
jobs that build through the sdist and test an installed wheel outside the
checkout. Linux includes software-rendered GUI/GL checks with Xvfb. Windows and
macOS always check native imports/loader and data/export paths; their GUI/driver
check is opt-in via the workflow's native_gui input. The workflow logs record
which checks actually ran; a skipped GUI/driver check is not a validation of
graphics hardware. See the completed workflow runs for the recorded native runs.
Installation recipes follow PyGObject's platform instructions, MSYS2's Python ABI and virtual-environment documentation, Homebrew's PyGObject formula, GTK's macOS backend documentation and libepoxy's OpenGL dispatch documentation.
Native GUI verification
The installed self-test now checks all six new feature areas: standalone STEP and SVG/DXF, project history/recovery, generated tooth profiles, interference distances, finite combination search and component picking/selected export. It writes only to a disposable profile. The GUI self-test opens the three new tools, exports the complete workspace and decodes animated preview PNGs for all catalog families to reject blank captures.
The Windows bundle explicitly includes libEGL.dll and libGLESv2.dll from
MSYS2 ANGLE.
GTK loads these dynamically, so they must be collected even without a static
DLL import. Their dependent libraries and licenses are included by the normal
native inventory. GPU drivers and Windows Direct3D remain OS components.
The preview allows both APIs and tries desktop GL 3.3, then GLES 3.0, with
separate minimum-version requests. It validates the realized context before
selecting GLSL 330 core or GLSL ES 300 with explicit precision. The shader math
is shared across both APIs.
Preview captures read portable RGBA bytes and convert to native Cairo ARGB32.
The native workflow always attempts installed GUI verification on Windows and
macOS. Only explicit absence of a graphical desktop or GL context may be
recorded as unavailable; missing EGL libraries, crashes, timeouts, shader
errors and blank images fail the job. A successful renderer must also pass
the full GUI self-test. Use native_gui=true to require graphics capability
instead of allowing an explicit runner limitation. Reports are uploaded as
separate GUI verification artifacts.