Using parts

Parts, together with plugins, are one of the key components to any snapcraft project, what follows intends to explore what parts can do after a quick recap on some concepts.

bases

A base in snapcraft defines where the building of a snap should take place and once it is built, where it should run.

As an example, two prominent bases are core and core18.When building, these use a stack based out Ubuntu 16.04LTS for the former and Ubuntu 18.04LTS for the latter. This information is encoded in the project’s snapcraft.yaml as a top level keyword base. Invoking snapcraft will trigger the correct behaviour.

Read more about bases here

lifecycle

A part is the basic building block for a snapcraft project, it defines the recipe to build a specific source, a basic example of a part can be:

parts:
  hello-part:
    source: gnu-hello.tar.gz
    plugin: autotools

The plugins defined in a part have the knowledge on how to build that specific source, to get a list of the available plugins run snapcraft list-plugins.

Find more about using the different language specific plugin guides here

All parts, by means of the logic encoded in the plugins, go through a lifecycle. These are the lifecycle steps each part goes through irrespective of the chosen plugin:

  • pull
  • build
  • stage
  • prime

Each of these lifecycle steps is exposed through the command line and can be part specific or apply to all parts in a project:

  • snapcraft pull [<part-name>]
  • snapcraft build [<part-name>]
  • snapcraft stage [<part-name>]
  • snapcraft prime [<part-name>]

For completion, the resulting snap is generated by calling snapcraft snap which is also the implicit command, so usually this is invoked as just snapcraft.

Each lifecycle step depend on the previous, so to reach the desired lifecycle step, all prior lifecycle steps need to have run.

Parts can also depend on each other, this is usually exposed through the after keyword in snapcraft.yaml. This adds a new dependency to the lifecycle in that all dependant parts must be staged prior for the depending parts’ lifecycle can run.

part directories

Each part when run through its lifecycle (the lifecycle itself depends on the selected plugin) will have different working directories:

  • parts/<part-name>/src used to pull the source during the pull step.
  • parts/<part-name>/build used to build the pulled sources during the build step.
  • parts/<part-name>/install a location to privately install built artifacts during the build step
  • stage a shared place where parts can expose artifacts to other parts such as development libraries, headers or others (e.g.; pkgconfig files).
  • prime a shared place where the final artifacts are to be placed, this directory is the reflection of the resulting snap.

overriding the lifecycle

Sometimes the default behaviour of a plugin is not enough or not desired, for this purpose there are two methods:

  • Using the override-<step-name> keyword (read more on overriding steps)
  • Using a local plugin, by inheriting from the parent or scaffolding from the original (read more on local plugins)

part environment

When a part is run, snapcraft sets the following environment variables so they can be accessed through overrides or build specific logic for a project that contemplates snapcraft in its code path (e.g.; a Makefile that has an install target that transparently contemplate SNAPCRAFT_PART_INSTALL)

directories

These are the exposed directory related environment variables, cosideration on how the lifecycle works should be taken when using some of these:

  • SNAPCRAFT_PROJECT_DIR: the absolute path to the root of the snapcraft project
  • SNAPCRAFT_PART_SRC: absolute path to where a part’s sources are pulled, it is also the part’s working directory for the pull step
  • SNAPCRAFT_PART_BUILD: absolute path to where a part’s sources are built, it is also the part’s working directory for the build step
  • SNAPCRAFT_PART_INSTALL: absolute path to where a part’s is to install artifacts to be staged.
  • SNAPCRAFT_STAGE: absolute path to where files are staged.
  • SNAPCRAFT_PRIME: absolute path to where files are primed.

facilities

Certain facilitating environment variables are also set:

  • SNAPCRAFT_ARCH_TRIPLET: the architecture triplet used for the selected base.
  • SNAPCRAFT_PARALLEL_BUILD_COUNT: the preferred amount of jobs the project is to be built with
  • SNAPCRAFT_PROJECT_NAME: the snapcraft project name (i.e.; name from snapcraft.yaml)
  • SNAPCRAFT_PROJECT_VERSION: the snapcraft project version as set in snapcraft.yaml
  • SNAPCRAFT_PROJECT_GRADE: the snapcraft project grade as set in snapcraft.yaml

build flags

And lastly, specific build flags are also set:

  • CFLAGS: empty unless after is used in the part and headers are staged in the common include paths for which they will be included (i.e.; paths added with -I)
  • CPPFLAGS: same behavior as CFLAGS
  • CXXFLAGS: same behavior as CFLAGS
  • LDFLAGS: empty unless after is used in the part and headers are staged in the common library paths (i.e.; paths added with -L)
  • PKG_CONFIG_PATH: empty unless after is used in the part and .pc files are staged in the common pkgconfig paths.

A plugin can also add its own set of environment variables or expand on these existing build related ones.

It is sometimes useful to override or expand on the defaults, for that, the build-environment keyword can be used, here’s a basic example:

parts:
  hello-part:
    source: gnu-hello.tar.gz
    plugin: autotools
    build-environment:
    - CFLAGS: "$CFLAGS -O3"  # add -O3 to the existing flags
    - LDFLAG: "-L$SNAPCRAFT_STAGE/non-standard/lib"  # override default flags and search for libraries in a non-standard path

hardening

When considering the GNU build tools, the same applies and when using different bases, different build flags could be applied depending on the selected base.

On Ubuntu based bases you can verify which hardening flag has been applied to your built artifacts by running hardening-check against your built binaries in the priming area.

As an example:

hardening-check prime/usr/bin/indicator-sensors
prime/usr/bin/indicator-sensors:
 Position Independent Executable: yes
 Stack protected: yes
 Fortify Source functions: yes
 Read-only relocations: yes
 Immediate binding: yes