Recipe Writing

What’s A BitBake Recipe?

A BitBake recipe may be defined as a file that gets parsed by the make like tool called bitbake. This file contains variables functions/task for the task executor (bitbake) to execute.

There are many types of recipes. The core recipes mainly built are

  • System Image recipes core-image-base.bb

  • Target recipes that install files on target system image.

  • Native recipes who’s output are utilized by other recipes.

A recipe can provide many packages. Allowing for seperating of what you do or don’t want in the system image. This can ofcourse later be controlled by the DISTRO_FEATURES variable.

Core Task

Each recipe has a series of task that must be executed to produce a final output.

The reference manual for the list of task and there discription may be found here

https://docs.yoctoproject.org/ref-manual/tasks.html

List of core task most recipes have by default. All task are overriable, but it’s often better to use bitbake class defined task over writing your own.

  1. do_fetch
    Fetches tarballs, github repos, scripts, text files, etc from wherever
    the caller specifies. Typically SRC_URI variable is used to define
    where to pull in a source.
  2. do_unpack
    Will unpack sources from do_fetch task into a given recipes ${WORKDIR}.
  3. do_prepare_recipe_sysroot
    Installs files into a given recipes ${WORKDIR}/recipes-sysroot and
    ${WORKDIR}/recipes-sysroot-native. Normally you’ll find the cross-compiler
    and maybe some version of glibc used by the recipe in ${WORKDIR}/recipes-sysroot-native.
  4. do_patch
    After fetching and unpacking bitbake applies any patch that were
    defined in SRC_URI.
  5. do_populate_lic
    Writes license information for the recipe that is collected later when the image is constructed.
  6. do_configure
    Task built for caller to configure a source. Typically before building you have to configure
    build variables and flags to help the build tool (make,cmake,auto tools,meson,etc..) understand
    what the developer wants to do.
  7. do_compile
    Tasked designed to compile code after caller defined configure flags and variables are set.
  8. do_install
    Tasked designed to install source whether compiled or not into ${D} (Destination).
    Typically set to ${WORKDIR}/image.
  9. do_populate_sysroot
    Stages files into a directory that may be used by other recipes.
  10. do_package
    Based upon what’s defined in FILES variable and what’s stored in ${D}
    splits files stored in ${D} into multiple folders in ${WORKDIR}/packages-split.
    These folder files are later packaged into rpm, deb, and ipk files.
  11. do_packagedata
    Saves package metadata generated by the do_package task in PKGDATA_DIR to make it available globally.
  12. do_package_qa
    Runs QA checks on package files. These may be skipped with the use of the
    INSANE_SKIP variable. Highly recommend you fix the issue versus just ignoring
    it to bypass QA issues.
  13. do_package_write_{ipk,deb,rpm}
    Builds actual package that’ll later be installed onto the system image.
    The package type built is based upon the PACKAGE_CLASSES variable.
    If you want to you can have each recipe build all 3 supported packages.
    Don’t recommend as this dramatically increases build time.
  14. do_clean
    Cleans a recipes ${WORKDIR}. Does not call do_cleansstate.
  15. do_cleansstate
    Cleans a recipes ${WORKDIR} and any shared state cache files created during build.
  16. do_cleanall
    Calls do_cleanssate and do_clean. This also cleans the files the recipe downloaded
    in the do_fetch task that are stored in DL_DIR. Which is usually set in local.conf.

Common Variables

Pro Tip: After you pull up a devshell you can see the recipe defined variables for each task inside of ${WORKDIR}/tmp.

Can even see a few by just typing env command inside the devshell.

Typical variables you’ll see/utilized in a recipe are

  • S
    Source directory to conduct builds out of. Usually either
    set to S = "${WORKDIR}" or S = "${WORKDIR}/git", but
    may litterally be set to anywhere. Still a wise decision to
    specify ${WORKDIR}/some-directory.
  • PN (Package Name)
    Generally set to the file name of the recipe. So, everything before
    _ (underscore) character. If applicable, the PN variable may
    also contains any special suffix or prefix.

    Example:
    ${PN}-native
  • BPN (Base Package Name)
    Set to the file name of the recipe with no special suffix or prefix.
  • PV (Package Version)
    Set to version of package. If not specified in recipe anything after
    the _ (underscore) character in the file name given to recipe
    will be used.
  • WORKDIR
    Set to the working directory of the recipe. Each recipe as a working directory
    that contains everything it needs to build software that’ll run on target.
  • LICENSE
    Used to identify any software licenses associated with code.
    You may add as many licenses as you want as long as you follow the rules:
  • LIC_FILES_CHKSUM
    Checksums (usually md5sum) of the files specified in LICENSE variable.
  • FILESEXTRAPATHS
    Used by OpenEmbedded build system to specify extra paths to search for
    files specified in the SRC_URI variable for a given recipe.
  • SRC_URI
    Used by OpenEmbedded build system in the do_fetch & do_patch task for a
    given recipe to identify the source location of a file or directory.
  • DEPENDS (Build Depends)
    Specify recipes which provide the package files that are used to build.
    The ouput of those packages will either go into ${WORKDIR}/recipe-sysroot or
    ${WORKDIR}/recipes-sysroot-native.

    NOTE:
    Anything with the suffix -native will go into ${WORKDIR}/recipes-sysroot-native.
    This is useful when your recipe requires commands that aren’t architecture specific.
    For instance if a recipe builds a project that requires meson. Adding meson-native
    will allow recipe to use a build host specific architecture implementation to build project.
  • RDEPENDS (Run Depends)
    Specify the recipes which provide the package files that are used at runtime (on target).
  • PACKAGES
    Specify the types of packages the particular recipe provides.
    Default list of provided packages for each recipe may be found
  • FILES
    Using globing allows developers to break the recipe files located
    in do_install task ${D} into multiple packages.

    NOTE:
    If you add a new package to PACKAGES you need to specify the package
    name in suffix after : character.

    EXAMPLE:
    PACKAGES += "mypackage"
    FILES:mypackage += "${bindir}/*myfile* /opt/random"
    
  • INSANE_SKIP
    If you ever have packaging errors or warnings this variable may be utilized to bypass them.
    Don’t recommend using this unless the situation requires or if you want to move on with development.
    See insane class to see list of
  • PROVIDES
    Just a list of names. This names are usually what you want a given recipe
    to provide. This name can then be used by other recipes at build time to
    pull in a given recipes package(s).
  • RPROVIDES
    Just a list of names.
  • RECIPE_SYSROOT
    Set to ${WORKDIR}/recipe-sysroot contains architecture specific binaries
    to utilize during builds.
  • RECIPE_SYSROOT_NATIVE
    Set to ${WORKDIR}/recipe-sysroot-native contains build host architecture
    specific binaries that a recipe may utlize during builds.
  • STAGING_LIBDIR
    Set to ${RECIPE_SYSROOT}/${base_libdir}. base_libdir is set based upon value specified
    in BASE_LIB variable usually prefix with /usr. The prefix may vary depending upon recipe
    and class included.
  • STAGING_INCDIR
    Set to ${RECIPE_SYSROOT}/${includedir}. includedir is generally set to /usr/include.
    But may vary depending upon recipe and class included.
  • STAGING_LIBDIR_NATIVE
    Set to ${RECIPE_SYSROOT_NATIVE}/${base_libdir}. base_libdir is set based upon value specified
    in BASE_LIB variable usually prefix with /usr. The prefix may vary depending upon recipe
    and class included.
  • STAGING_INCDIR_NATIVE
    Set to ${RECIPE_SYSROOT_NATIVE}/${includedir}. includedir is generally set to /usr/include.
    But may vary depending upon recipe and class included.