Skip to content

Buildout Tutorial

1. Introduction to Buildout

What is Buildout?

Buildout is a Python-based build system primarily used for:

  • Assembling applications from multiple parts, which may or may not be Python-based.
  • Deploying applications in a consistent and repeatable manner.
  • Managing dependencies and ensuring that the correct versions of software components are used.

Why Use Buildout?

  • Repeatability: Buildout ensures that you can reproduce the same build environment every time, eliminating inconsistencies between development, testing, and production environments.
  • Componentization: It encourages breaking down your application into smaller, manageable parts, making it easier to develop, test, and maintain.
  • Automation: Buildout automates many of the tedious tasks involved in setting up and deploying applications, such as downloading dependencies, compiling code, and generating configuration files.
  • Isolation: Buildout can create isolated environments for your applications, preventing conflicts with system-wide packages or other projects.
  • Flexibility: It supports a wide range of software components and can be extended to handle custom build requirements.

Core Concepts

  • Buildout Configuration File (buildout.cfg): The main configuration file that defines the structure and components of your application.
  • Parts: Sections within the buildout.cfg file that represent individual components of your application (e.g., a web server, a database, a Python application).
  • Recipes: Python scripts that perform specific tasks during the build process, such as downloading files, installing packages, or generating configuration files.
  • Eggs: Python packages distributed in a specific format (.egg). Buildout can install and manage eggs as part of your application.
  • Extensions: A mechanism for reusing Buildout configurations across multiple projects.

2. Installation and Setup

Prerequisites

  • Python: Buildout requires Python to be installed on your system. Python 3.8 or later is recommended.
  • pip: The standard package installer for Python. It is usually included with Python installations.
  • virtualenv (Recommended): A tool for creating isolated Python environments.

Installing Buildout

It’s strongly recommended to install Buildout within a virtual environment:

  1. Create a virtual environment:

    bash python3 -m venv .venv

  2. Activate the virtual environment:

    • Linux/macOS:

      bash source .venv/bin/activate

    • Windows:

      bash .venv\Scripts\activate

  3. Install Buildout using pip:

    bash pip install zc.buildout

Bootstrapping a Project

Bootstrapping initializes a new Buildout project by creating the necessary directory structure and a basic buildout.cfg file.

  1. Create a project directory:

    bash mkdir my-buildout-project cd my-buildout-project

  2. Run the bootstrap command:

    • If you used virtualenv in step 3, install also setuptools:

      bash pip install -U setuptools

    • Then create a bootstrap.py file and download a version from the web:

      bash curl -O https://bootstrap.pypa.io/bootstrap-buildout.py

    • Run the bootstrap script:

      bash python bootstrap.py

This will create the following:

  • bin/buildout: The main Buildout executable.
  • eggs/, develop-eggs/, parts/, downloads/: Directories for storing eggs, development eggs, parts and downloads respectively.
  • buildout.cfg: The main Buildout configuration file.

3. Basic Buildout Configuration (buildout.cfg)

The buildout.cfg file is the heart of your Buildout project. Let’s examine its key sections:

[buildout] Section

This section contains global settings for your Buildout.

[buildout]
# Specifies the parts to be built. The order here determines the execution order.
parts =
    python-interpreter
    my-application

# Specifies a list of directories where Buildout will look for eggs.
find-links =
    https://example.com/eggs

# Specifies a section containing pinned versions of eggs.
versions = versions

# Specifies directories to be created.
directory =
    ${buildout:parts-directory}/my-application
    ${buildout:parts-directory}/data

# Other common options:
# new-st-with-cache:  If set to true, Buildout uses the newest packages found on `find-links` URLs.
# offline = true:  Forces Buildout to work offline, using only cached resources.
# develop = src/my_package:  Specifies a list of Python packages to be developed in-place (useful during development).
# eggs-directory = /path/to/eggs: Specifies a directory where downloaded eggs will be cached.
# download-cache = /path/to/downloads: Specifies a directory where downloaded files will be cached.

parts

The parts option in the [buildout] section lists the parts that Buildout will build, and their order of execution. Each part corresponds to a section in the buildout.cfg file (e.g., [python-interpreter], [my-application]).

[versions] Section

This section is used to pin specific versions of eggs to ensure repeatable builds.

[versions]
my-application = 1.2.3
requests = 2.28.1

Extending Buildout Configurations

You can extend other Buildout configuration files using the extends option in the [buildout] section.

[buildout]
extends =
    base.cfg
    https://example.com/more.cfg

This is useful for reusing common configurations across multiple projects or for creating different build profiles (e.g., development, production).

Creating a Simple Buildout

Let’s create a simple Buildout that installs a Python interpreter and a custom Python script:

[buildout]
parts =
    python-interpreter
    my-script

[python-interpreter]
recipe = zc.recipe.egg
interpreter = python
eggs =
    zc.recipe.egg

[my-script]
recipe = zc.recipe.egg:scripts
eggs =
    my-application
interpreter = python
scripts = my-script

[my-application]
# A dummy egg for demonstration purposes.
recipe = zc.recipe.egg
eggs =
    setuptools

To build this, run:

bin/buildout

This will:

  1. Install a Python interpreter (bin/python) using the zc.recipe.egg recipe.
  2. Install the setuptools and a dummy egg called my-application.
  3. Create a script called bin/my-script using the specified interpreter.

4. Recipes: The Workhorses of Buildout

Recipes are the core of Buildout’s functionality. They are Python scripts that perform specific tasks during the build process.

Commonly Used Recipes

zc.recipe.egg

This is one of the most commonly used recipes. It’s used for:

  • Installing Python eggs.
  • Creating scripts.
  • Specifying an interpreter for scripts.

Example:

[my-app]
recipe = zc.recipe.egg:scripts
eggs =
    requests
    my-other-app
interpreter = my-python
scripts =
    my-script
    another-script

hexagonit.recipe.download

This recipe is used for downloading files from URLs.

Example:

[my-download]
recipe = hexagonit.recipe.download
url = https://example.com/myfile.zip
download-directory = ${buildout:downloads-directory}/myfiles

plone.recipe.command

This recipe executes a shell command.

Example:

[my-command]
recipe = plone.recipe.command
command = echo "Hello from Buildout!"

plone.recipe.makedirs

This recipe creates directories.

Example:

[my-directories]
recipe = plone.recipe.makedirs
paths =
    ${buildout:parts-directory}/my-app/data
    ${buildout:parts-directory}/my-app/logs

Using Recipes in Your buildout.cfg

To use a recipe, you specify it in the recipe option of a part section. You can then provide options to the recipe within that section.

5. Creating a Multi-Part Buildout

Let’s create a more complex Buildout for a typical web application:

Building a Web Application

[buildout]
parts =
    python
    web-app
    database
    web-server

[python]
recipe = zc.recipe.egg
interpreter = python
eggs =
    zc.recipe.egg

[web-app]
recipe = zc.recipe.egg:scripts
eggs =
    Flask
    my-web-app
interpreter = python
scripts = web-app-script

[my-web-app]
# Dummy egg for demonstration. Replace with your actual web app.
recipe = zc.recipe.egg
eggs =
    setuptools

[versions]
Flask = 2.2.2

Adding a Database (e.g., PostgreSQL)

[database]
recipe = plone.recipe.command
command =
    mkdir -p ${buildout:parts-directory}/pgsql && \
    initdb ${buildout:parts-directory}/pgsql && \
    pg_ctl -D ${buildout:parts-directory}/pgsql -l ${buildout:parts-directory}/pgsql/log start

Note:
- You’ll need to install initdb and pg_ctl using a command such as sudo apt install postgresql-client-common.
- It is recommended to use a recipe that handles database installation for you, such as bda.recipe.postgresql.

Configuring a Web Server (e.g., Nginx)

[web-server]
recipe = plone.recipe.command
command =
    echo "server { listen 80; server_name example.com; location / { proxy_pass http://localhost:8080; } }" > ${buildout:parts-directory}/nginx.conf && \
    nginx -c ${buildout:parts-directory}/nginx.conf

Note:
- It is recommended to use a recipe that handles webserver installation for you, such as bda.recipe.nginx.
- You’ll need to have nginx installed on your machine using a command such as sudo apt install nginx.

6. Working with Extensions

Extensions allow you to reuse Buildout configurations across multiple projects or to create different build profiles.

Creating an Extension

An extension is simply a Buildout configuration file that can be extended by other configuration files.

Example (base.cfg):

[buildout]
parts =
    python

[python]
recipe = zc.recipe.egg
interpreter = python
eggs =
    zc.recipe.egg

[versions]
zc.recipe.egg = 2.0.7

Using an Extension

To use an extension, use the extends option in your buildout.cfg:

[buildout]
extends = base.cfg
parts +=
    my-app

[my-app]
recipe = zc.recipe.egg:scripts
eggs = my-app
interpreter = python
scripts = my-app-script

[my-app]
# Dummy egg. Replace with your application.
recipe = zc.recipe.egg
eggs = setuptools

7. Advanced Buildout Features

Conditional Sections

You can use conditional expressions in your buildout.cfg to include or exclude parts based on certain conditions. This is done using the < operator.

Example:

[buildout]
parts =
    base
    <production

[base]
recipe = zc.recipe.egg
eggs = my-app

[production]
recipe = zc.recipe.egg
eggs =
    production-settings

In this example, the production part will only be included if you run Buildout with the -s production option:

bin/buildout -s production

Buildout Profiles

Profiles are a way to manage different configurations for the same project. You can define profiles using the extends option and conditional sections.

Example:

[buildout]
extends =
    base.cfg
    profiles/development.cfg
parts =

[development]
# Development-specific settings
debug = true

[production]
# Production-specific settings
debug = false

You can then choose a profile using the -c option when running Buildout:

bin/buildout -c development.cfg

Environment Variables

You can use environment variables in your buildout.cfg using the ${:} syntax.

Example:

[my-app]
recipe = zc.recipe.egg:scripts
eggs = my-app
interpreter = python
scripts = my-app-script
environment =
    DATABASE_URL=${:DATABASE_URL}

You can then set the DATABASE_URL environment variable before running Buildout.

8. Buildout in the SlapOS Context

While Buildout is a general-purpose build system, SlapOS uses it in specific ways and extends its functionality.

Key Differences

  • Software Releases and Instances: SlapOS introduces the concepts of Software Releases (defined by software.cfg) and Software Instances (defined by instance.cfg.in), which are built on top of Buildout’s core concepts.
  • Promises: SlapOS heavily relies on Promises for monitoring and ensuring the reliability of Software Instances.
  • Central Master: SlapOS uses a central Master server to manage deployments, whereas vanilla Buildout is typically used in a standalone manner.
  • SlapOS-Specific Recipes: SlapOS provides its own set of recipes, often prefixed with slapos.cookbook, to handle tasks specific to the SlapOS environment.

SlapOS-Specific Recipes

Here are some examples of SlapOS-specific recipes:

  • slapos.cookbook:component: Used for defining reusable components in a Software Release.
  • slapos.cookbook:cron: Used for deploying cron services.
  • slapos.cookbook:jinja2: Used to render Jinja2 templates.
  • slapos.cookbook:logrotate: Used for deploying logrotate.
  • slapos.cookbook:makedirectory: Used for creating directories.
  • slapos.cookbook:publish: Used to publish parameters.
  • slapos.cookbook:wrapper: Used for creating wrappers to manage the execution of software or services.
  • slapos.cookbook:promise.plugin: Used for integrating Promises into a Software Release.

Refer to the SlapOS documentation for a complete list and detailed information about these recipes.

9. Troubleshooting

  • Read the Logs: Buildout provides detailed logs during the build process. Pay close attention to any errors or warnings.
  • Check Pinned Versions: If you encounter unexpected errors, verify that you have pinned the correct versions of all dependencies in your [versions] section.
  • Use the -v or -vv Options: Run Buildout with the -v (verbose) or -vv (very verbose) options to get more detailed output.
  • Isolate the Problem: If a specific part is failing, try commenting out other parts to isolate the issue.
  • Consult the Documentation: Refer to the official Buildout documentation (https://buildout.readthedocs.io/) and the documentation for any recipes you are using.
  • Search Online: Many Buildout users have encountered similar problems, and you can often find solutions on forums or Q\&A sites like Stack Overflow.

10. Conclusion

This tutorial has covered the fundamentals of Buildout, from installation and basic configuration to advanced features and its use within the SlapOS ecosystem. By understanding these concepts and practicing with the examples, you’ll be well-equipped to use Buildout for your own projects and to explore the capabilities of SlapOS. Remember to consult the official documentation for both Buildout and SlapOS for more in-depth information and specific use cases.

Page last modified: 2025-01-03 08:36:57