Override build steps

You can override and customise steps of a part’s lifecycle (pull, build, stage, and prime) using overrides; shell scripts directly sourced from snapcraft.yaml. These scripts are run with /bin/sh, which by default on Ubuntu is dash.

An override is declared with the following syntax:

parts:
  <part name>:
    override-<step name>: <shell script>

You can use a pipe on the first line to declare a multi-line script:

parts:
  <part name>:
    override-<step name>: |
      <multi-line>
      <shell script>

Overriding the pull step

This can be done by utilising the override-pull override. Its working directory is the part’s source directory in parts/<part name>/src/. In order to run the default pull step, call snapcraftctl pull from within the override itself.

Example

Let’s say you want to patch the source code of the part you’re pulling:

parts:
  foo:
    plugin: dump
    # ...
    override-pull: |
      snapcraftctl pull
      patch -p1 < $SNAPCRAFT_STAGE/my.patch

Overriding the build step

This can be done by utilising the override-build override. Its working directory is the part’s base build directory in parts/<part name>/build/. In order to run the default build step, call snapcraftctl build from within the override itself.

Example

Let’s say the default build/install process ends up installing files with absolute paths in them, which need to be fixed up to look inside the snap:

parts:
  foo:
    plugin: dump
    # ...
    override-build: |
      snapcraftctl build
      sed -i 's|/usr/bin|$SNAP/usr/bin|g' $SNAPCRAFT_PART_INSTALL/my-bin-artifact.sh

Overriding the stage step

This can be done by utilising the override-stage override. Its working directory is the staging area in stage/. In order to run the default stage step, call snapcraftctl stage from within the override itself.

Example

Let’s say you wanted to tweak a file installed by another part:

parts:
  foo:
    plugin: dump
    # ...
    after: [other-part]
    override-stage: |
      snapcraftctl stage
      sed -i 's|/usr/bin|$SNAP/usr/bin|g' other/parts/file

Overriding the prime step

This can be done by utilising the override-prime override. Its working directory is the primeing area in prime/. In order to run the default prime step, call snapcraftctl prime from within the override itself.

Example

Let’s say you wanted to compile gsetting schemas for the entire priming area

parts:
  foo:
    plugin: nil
    after: [all, other, parts]
    override-prime: |
      snapcraftctl prime
      glib-compile-schemas usr/share/glib-2.0/schemas
1 Like

Should this mention that the scriptlet is dash (not bash)?

1 Like

What, that matters? Aren’t these all the same? cough fixed thanks :slight_smile: .

Please add a list of all the steps and what overriding each one will prevent from occurring unless you add the snapcraftctl $step to reinstate the default behaviour of the plugin.

Thanks for the docs, Kyle! We should link into this from somewhere in the publication docs.

Another idea: can we rename “snapcraftctl” to “craftctl” perhaps? The former seems a bit over-verbose.

@niemeyer you mean from the outline topic? Yeah, we should chat about how to best organize that, I figured I’d start with just adding the info for now.

Regarding the name, we wanted to mirror the snap -> snapctl relationship with snapcraft -> snapcraftctl. It’s a bit long, perhaps, but it’s intuitive: in snap, use snapctl to communicate with the orchestrator of the snap (snapd), in snapcraft, use snapcraftctl to communicate with the orchestrator of the build (snapcraft).

When I see *ctl, I think “it controls something.” What does it control? Intuitively, the *. So for snapctl, it controls the snap (its services, its config, etc.). For snapcraftctl, it controls snapcraft. For craftctl, I ask “what is craft?”

I can see the relationship, but these tools are different, and unless you know how to use it and where you can use it, there’s no way to just guess. Having something nice to read and write sounds more important than just mimicking the etymology.

How to find out project root or location of snapcraft.yml? I don’t see any environment variables pointing to it. Why? There is a custom version file in project root, maintained by other people, and all parts should be built for version specified there.

I am doing out of tree builds, and usual “parent of stage” doesn’t work here.

1 Like

Added SNAPCRAFT_PROJECT_DIR is a part of “out of tree build” PR.

https://github.com/snapcore/snapcraft/pull/2463/commits/85f9815483cdda206b5acd0a7b1cb94744f95c39

Tests are not easy.

I would like to ask if the shell environment of the scriptlet has defined any shell options like errexit and nounset?

UPDATE: It seems that only errexit is enforced:

while we should stop execution on errors, nounset should really be in the hands of the developer (i personally am for example not a fan of it since it makes scripts a lot bigger and harder to read if you have to zero-init variables everywhere)

1 Like

@degville hey there! Just had a thought about making this less about scriptlets and more about overriding steps.

Good idea! I’ve linked to this from the new Snapcraft build, debug and publishing docs roadmap plan we have so we can update this page to reflect that ideal.

This is helpful, but for me at least left me with more questions. The content by Dan at Proposal: Expose SNAPCRAFT_SNAP and SNAPCRAFT_PARTS environment variables completed the picture on how to apply a patch.

@degville hey, just a mention that the prime example is missing a call to snapcraftctl prime. I would fix it myself, but I have no edit button :slight_smile:

1 Like

Thanks! I’ve made this a wiki and added the missing command.

1 Like

The proper way to apply a patch is to install them to the /stage tree by a different part and applying them in the consuming part’s override-{pull,build} scriptlets, refer the following one for an example:

What is the scope of a variable I define arbitrarily inside a scriptlet? I’m assuming it’s local to just that scriptlet but I don’t see that written down explicitly anywhere.

Issue

Noticed this in another doc page, but it looks like the anchor is broken here too. Like the other doc page, the problem seems to be with how the anchor is rendered in the document. When you click on the anchor, nothing seems to happen on the page.

Potential solution

I found that adding an extra space between the anchor and the heading text fixes the issue:

Instead of:

  • ## Overriding the **build** step[:anchor:](#heading--overriding-the-build-step)

do:

  • ## Overriding the **build** step [:anchor:](#heading--overriding-the-build-step)

Thanks for letting us know! I’ve updated all the headings in this doc to use the new anchor scheme.

1 Like