Parts lifecycle

A snap build consists of the processing one or more software parts. Each part follows an ordered series of five steps, which forms the lifecycle, ensuring a coherent and reproducible build process.

This page aims to help anyone who seeks to build a snap and understand the inner workings of parts – including their steps, commands, directories, and dependencies.

Lifecycle

A part goes through five steps:

  1. Pull. Downloads or otherwise retrieves the components needed to build the part.

    You can use the source-* keys of a part to specify which components to retrieve. For instance, if source points to a Git repository, the pull step will clone that repository.

  2. Build. Constructs the part from the previously-pulled components.

    The plugin of a part specifies how it’s constructed. The meson plugin, for example, executes meson and ninja to compile source code.

    Each part is built in a separate directory, but it can use the contents of the staging area if it specifies a dependency on other parts using the after key. See Processing order and dependencies for more information.

    To rename or move files, you can use the organize key.

  3. Stage. Copies the built components into the staging area.

    This is the first time all the different parts that make up the snap are actually placed in the same directory. If multiple parts provide the same file with differing contents, you will get a conflict. You can avoid these conflicts by using the stage key to enable or block files coming from the part. You can also use this key to filter out files that aren’t required in the snap itself, for example build files specific to a single part.

  4. Prime. Copies the staged components into the priming area.

    This is very similar to the stage step, but files go into the priming area instead of the staging area. The prime step exists because the staging area might still contain files that are required for the build but not for the snap. For example, if you have a part that downloads and installs a compiler, then you stage this part so other parts can use the compiler during building. You can then use the prime filter key to make sure that it doesn’t get copied to the priming area, so it doesn’t take up space in the snap.

    To apply specific permissions to files, you can use the permissions key.

    Some extra checks are also run during this step to ensure that all dependencies are satisfied for a proper run time. If the snap is classically-confined, then files will be scanned and, if needed, patched to work with this confinement mode.

  5. Pack. Takes the entire contents of the /prime directory and packs it into a snap file.

Commands

Each of the lifecycle steps can be run from the command line, and the command can be part-specific or applied to all parts in a project.

  1. snapcraft pull [<part-name>]
  2. snapcraft build [<part-name>]
  3. snapcraft stage [<part-name>]
  4. snapcraft prime [<part-name>]
  5. snapcraft pack

Each command also executes the previous lifecycle steps, therefore snapcraft pack executes all the lifecycle steps chained together.

It’s possible to access the part environment at any stage by running one of the lifecycle commands with the --shell argument. For example, snapcraft prime --shell processes the parts up to the prime step, then opens a shell. See Iterating over a build for more details.

Part directories

When running through its lifecycle steps, a part will use different working directories inside the snap. The directory names closely follow the step names.

Environment variable Directory Purpose
CRAFT_PART_SRC /parts/<part-name>/src The location of the source after the pull step
CRAFT_PART_BUILD /parts/<part-name>/build The working directory during the build step
CRAFT_PART_INSTALL /parts/<part-name>/install Contains the results of the build step and the stage packages. It’s also the directory where the organize event renames the built files
CRAFT_STAGE /stage Shared by all parts, this directory contains the contents of each part’s CRAFT_PART_INSTALL after the stage step. It can contain development libraries, headers, and other components (e.g. pkgconfig files) that need to be accessible from other parts
CRAFT_PRIME /prime Shared by all parts, this directory holds the final components after the prime step
CRAFT_PROJECT_DIR Path to the current project’s subtree in the filesystem Used to access resources from the project’s subtree, such as an icon or version file

Overriding a step

Each plugin defines the default actions that happen during a step. This behavior can be changed in two ways:

  • By using override-<step-name> in the project file. See Overriding steps to see how.
  • By using a local plugin. This can inherit the parent plugin or scaffolding from the original. See Local plugins for a practical guide. Using a local plugin was deprecated in core20 and removed in core22.

Processing order and dependencies

By default, snapcraft pack runs the same lifecycle step of all parts before moving to the next step. However, you can change this behavior using the after key in the definition of a part in the project file. This creates a dependency chain from one part to another.

The ordering rules are as follows:

  • Parts are ordered alphabetically by name.
  • When a part requires another part (using the after key), the required part will go through the build and stage steps before the initial part can run its build step.

Note that each lifecycle step depends on the completion of the previous step for that part, so to reach a desired step, all prior steps need to have successfully run. Renaming or adding parts and modifying or removing after keys can change that order.

Example of default ordering

parts:
  A:
    plugin: go
  C:
    plugin: go
  B:
    plugin: go
Pulling A
Pulling B
Pulling C
Building A
Building B
Building C
Staging A
Staging B
Staging C
...

Example of dependent ordering

parts:
  A:
    plugin: go
    after:
      - C
  C:
    plugin: go
  B:
    plugin: go
Pulling C
Pulling A
Pulling B
Building C
Skipping pull for C (already ran)
Skipping build for C (already ran)
Staging C (required to build 'A')
Building A
Building B
Skipping stage for C (already ran)
Staging A
Staging B
...

In the above example, part A is built after part C is successfully built and staged.

Lifecycle processing diagram

Diagram explaining the lifecycle processing order for building a snap

Further reading

If you want to learn more about the Snapcraft ecosystem, see the following resources:

The keyword is organize according to Snapcraft reference, shouldn’t the step be the same?

You’re right - thanks for mentioning it. I’ve updated the spelling.

In the last table showing “which directories each step uses”, the step prime might have a wrong explanation:

copies the staged files from SNAPCRAFT_PART_INSTALL to the shared SNAPCRAFT_PRIME

Should be replace with

copies the staged files from the shared SNAPCRAFT_STAGE to the shared SNAPCRAFT_PRIME

BTW, is there any place that I’m missing to send a pull request of this document?

Thanks so much for taking the trouble to let us know - I’ve updated the document. Leaving a comment is a great thing to do, but equally, everyone is also free to edit these documents directly.

Shouldn’t this appear on the ToC on the left?

This page should note that SC7 variables are of the shorter “CRAFT_*” form.

That’s a very good point. I’ve updated this doc, but I know there’s a wider (logged) issue with updating other references too. I’ll make sure this is done.

The longer forms are still supported by SC7, might be worth mentioning.

It is maybe implied, but I found how the build directory starts populated with the pulled source surprising. Could that be clarified better here? I might be missing it, but I don’t see where that is really spelled out. For example, I do see that pull puts stuff in CRAFT_PART_SRC and build builds from CRAFT_PART_BUILD, but I don’t see anywhere here mentioning when the cp -r $CRAFT_PART_SRC $CRAFT_PART_BUILD is happening.

I think I noticed one major important difference between core20 and core22 bases. It seems so that in core20, the pull step would be delayed until the dependent parts are fully staged. But in core22, the pull is executed without dependency resolution and the build step waits instead. This changes a lot in our usage of Snapcraft because we had a part to download some patches and apply them on the upcoming parts from the stage area. Would you confirm this @degville and what would be your solution to address these cases? I really liked the previous method and in my opinion it made much more sense to delay pull instead of just build.