Extension Points
This page describes how to extend scaldys-project — either by configuring
new behaviour from a consuming project, or by modifying the codebase itself.
Before reading this page, the Architecture page provides the structural context that makes the extension points below easier to follow.
Design Conventions
Understanding these patterns before modifying the code prevents accidentally breaking the design:
Three-class composition, not inheritance. Each platform has an
Environmentclass (paths + tool discovery), one or more step classes (Compiler,Packager), and a thinBuilderorchestrator. Logic is added to the appropriate class, not to the orchestrator.Steps list as data.
WindowsBuilder.main()keeps its pipeline as a plainlistof(label, callable)pairs. This makes the sequence explicit and easy to extend without subclassing.Config is loaded once.
load_config()is called insideBaseBuildEnvironment.__init__and stored asself.config. No component re-readsscaldys-project.tomlat runtime. Extendconfig.pyif new configuration keys are needed.Platform code stays in its subdirectory. Windows-specific logic lives entirely inside
windows/builder.py. Shared logic (base classes, config, docs, utils) lives incommon/. Adding a new platform means adding a new<platform>/directory without touching existing code.External tools are validated early.
pre_flight_checks()is always the first step in the pipeline. New steps that require external tools must add the corresponding validation there, not inline.
Adding a New Cython Module to a Consuming Project
Compiled modules are declared in the consuming project’s scaldys-project.toml,
not in scaldys-project itself. To add a module:
Add the dotted module name to the
compiled_moduleslist:[cython] compiled_modules = ["mypackage.hot_path", "mypackage.parser"]
Compiler.run_cython()reads this list at build time and passes it tocompile_runner.pyviasetup()→cythonize(). No changes toscaldys-projectare needed.
The source_root key (default src) tells the compiler where your
package lives relative to the project root:
[cython]
source_root = "src"
compiled_modules = ["mypackage.fast_module"]
See Configuration for the full scaldys-project.toml reference.
Adding a New Build Step to WindowsBuilder.main()
main() keeps its pipeline in a plain list of (description, callable)
pairs (windows/builder.py, the steps variable inside main()):
steps = [
("Pre-flight checks", lambda: self.env.pre_flight_checks(...)),
("Cleaning build directories", self.clean),
("Building documentation", self.build_docs),
("Building executable", self.build_exe),
("Building installer", self.build_installer),
]
To insert a new step:
Implement the logic as a method on
WindowsBuilder(or delegate to a new helper class that holds a reference toself.env).Append or insert a
(label, self.new_step)tuple in thestepslist at the appropriate position.If the step requires an external tool, add a corresponding
require_<tool>=Truekwarg to thepre_flight_checks()call at the top ofsteps, and teachWindowsBuildEnvironment.pre_flight_checks()to validate it.
Adding a New Platform Builder
scaldys-project is designed so that a Linux or macOS builder can be added
without touching existing Windows code. You need four things:
A new
BuildEnvironmentsubclass Createsrc/scaldys_project/<platform>/builder.pyand subclassBaseBuildEnvironment:from scaldys_project.common.base import BaseBuildEnvironment class LinuxBuildEnvironment(BaseBuildEnvironment): def __init__(self, project_path: Path, verbose: bool = False) -> None: super().__init__(project_path, verbose) # Discover platform-specific tools here self.appimage_tool_path = self._find_tool("appimagetool", ...) def pre_flight_checks(self, **kwargs: Any) -> None: # Validate platform-specific tools ...
A new
Buildersubclass In the same file, subclassBaseBuilder:from scaldys_project.common.base import BaseBuilder class LinuxBuilder(BaseBuilder): def __init__(self, project_path: Path, verbose: bool = False) -> None: env = LinuxBuildEnvironment(project_path, verbose) super().__init__(env) self.env: LinuxBuildEnvironment = env # narrow type self.doc_builder = DocumentationBuilder(self.env) # Add platform-specific step classes as needed def main(self) -> None: steps = [...] # Same pattern as WindowsBuilder.main() ...
Wire up CLI commands in
__main__.pyAdd a newTypersub-app following the existing pattern:from scaldys_project.linux.builder import LinuxBuilder linux_app = typer.Typer(help="Linux specific build subcommands", no_args_is_help=True) build_app.add_typer(linux_app, name="linux") @linux_app.command("all") def linux_all(verbose: bool = ...) -> None: builder = LinuxBuilder(PROJECT_ROOT, verbose=verbose) builder.main()
Add a
BuildConfigsection if needed If the new platform requires configuration keys beyond whatscaldys-project.tomlalready provides, extendcommon/config.pywith a new section dataclass and register it onBuildConfig.
—
For the contributor workflow (cloning, testing, publishing), see Contributing.