Skip to main content

Manage code dependencies

The Intrinsic SDK uses Bazel, an open-source build and test tool similar to cmake, Maven, and Gradle, as its build system. The Intrinsic Visual Studio Code plugin manages Bazel complexities automatically, allowing developers to focus on code without needing deep knowledge of Bazel. However, there are some situations, such as managing code dependencies, where knowing about Bazel is helpful.

Bazel's handling of software dependencies might be different than you are used to. This page describes how to manage software dependencies when writing your own skills.

Share code within your Bazel workspace

Say you are creating two skills. Both skills inspect parts, and both need to check if a measurement is within tolerance. Don't write the same code twice; reuse it!

Create a reusable library

Create a new bazel package for your inspection code.

  1. Create a folder next to your MODULE.bazel file called inspection.
  2. Create a file inspection/BUILD.

The next steps depend on your chosen programming language.

  1. Create a file inspection/measurements.py

    Your bazel workspace has two new files:

    MODULE.bazel
    inspection/BUILD
    inspection/measurements.py
    ... other files ...
  2. Put the code you want to reuse into measurements.py.

    def within_tolerance(measured, expected, allowed_difference):
    actual_difference = measured - expected
    return abs(actual_difference) <= abs(allowed_difference)
  3. Put the following into BUILD to create a bazel target for your Python library using a py_library rule.

    load("@rules_python//python:defs.bzl", "py_library")

    py_library(
    name = "measurements",
    srcs = ["measurements.py"],
    visibility = ["//visibility:public"],
    )

You now have a reusable Python library.

Use a reusable library

In order to reuse your library, you need to know its bazel label. Your library has the bazel label:

//inspection:measurements

Here's what each part means:

  • // is the root of your bazel workspace
  • inspection is the path to your bazel package
  • :measurements is a target named measurements in that bazel package

How you use the bazel label depends on your chosen programming language.

Locate the py_library target for your skill in its BUILD file. It should be above py_skill bazel target. Add the bazel label of your inspection library to the deps attribute of your skill's py_library target.

py_library(
name = "inspect_brackets_skill_py",
srcs = ["inspect_brackets_skill.py"],
deps = [
"//inspection:measurements",
# ... other dependencies omitted ...
],
)

Import the within_tolerance function in your skill's python file.

from inspection.measurements import within_tolerance

Add an external dependency

Bazel manages external dependencies with bazel repository rules. They tell bazel how to get external dependencies.

Get Python packages from the Python Package Index

The Python Package Index (PyPI) hosts many python packages. Use the bazel repository rules provided by rules_python to download python packages from PyPI using the Python package installer pip.

Create bazel repository rules for NumPy and OpenCV by following these instructions:

  1. Create a file requirements.in next to your MODULE.bazel file.

  2. Put the following content (the Python package names for NumPy and OpenCV) into requirements.in:

    numpy
    opencv-python-headless
  3. Create an empty file requirements_lock.txt next to requirements.in.

  4. Create a file BUILD next to requirements.in.

  5. Add the following content to BUILD:

    load("@rules_python//python:pip.bzl", "compile_pip_requirements")

    compile_pip_requirements(
    name = "requirements",
    src = ":requirements.in",
    requirements_txt = ":requirements_lock.txt",
    )
  6. Ensure a dependency on rules_python exists in MODULE.bazel. If you are using the MODULE.bazel generated by inctl bazel init, this rule will already be included

    bazel_dep(name = "rules_python", version = "0.31.0")
    python = use_extension("@rules_python//python/extensions:python.bzl", "python")
  7. Run the following command to update requirements_lock.txt:

    bazel run //:requirements.update
  8. Create a repo rule In your MODULE.bazel file, add a dependency to rules_python and load the pip extension if not already present.

    ## Toolchain is declared once per MODULE
    python.toolchain(
    is_default = True,
    python_version = "3.11",
    )
    pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")

    # Each skill or service can declare their own set of requirements.
    # In this example, use a single top-level requirements file
    pip.parse(
    hub_name = "pypi_deps",
    python_version = "3.11",
    requirements_lock = "//:requirements_lock.txt",
    )
    use_repo(pip, "pypi_deps")

    Note that the hub_name specified in the MODULE.bazel file will dictate how dependencies appear in the BUILD.bazel files. PyPI-derived rules will appear in the fomat @<hub_name>//<pkg_name>:pkg. In this case numpy will appear as @pypi_deps//numpy:pkg.

Your bazel workspace now has bazel repository rules that tell bazel how to get NumPy and OpenCV from PyPI. Make your skill's py_library target depend on them with the following instructions:

  1. Locate the BUILD file for your skill. For example:

    my_skill/BUILD
  2. Add dependencies on NumPy and OpenCV to the deps attribute of your skill's py_library bazel target.

    py_library(
    name = "my_skill_py",
    srcs = ["my_skill.py"],
    deps = [
    "@pypi_deps//numpy:pkg",
    "@pypi_deps//opencv_python_headless:pkg",
    # ... other dependencies omitted ...
    ],
    )
  3. Import NumPy and OpenCV in your skill's Python file.

    import numpy as np
    import cv2