#!/usr/bin/env bash # nRF Connect SDK toolchain environment. # # Normally you do not source this directly. It is wired into Zephyr's own # entry point via ~/.zephyrrc (see scripts/install-zephyrrc.sh), so that # # source /opt/ncs/v3.4.0/zephyr/zephyr-env.sh # # sets up ZEPHYR_BASE *and* the toolchain in one step. Sourcing zephyr-env.sh # without this leaves you on the system cmake and python, and the build fails: # # Could NOT find Python3: Found unsuitable version "3.10.6", # but required is at least "3.12" # # because /usr/local/bin/python3.10 (and the host's 3.14) are found before the # Python 3.12 that the SDK toolchain bundle ships. # # Nothing is hardcoded: the bundle directory is discovered, and the variables # are read from the bundle's own environment.json, so toolchain or SDK updates # are picked up instead of silently rotting. # # Overrides, if the defaults guess wrong: # NCS_ROOT SDK install root (default /opt/ncs) # NCS_VERSION version dir under NCS_ROOT (default: newest vX.Y.Z) # NCS_TOOLCHAIN toolchain bundle directory (default: most recent) _ncs_warn() { echo "ncs-toolchain: $*" >&2; } : "${NCS_ROOT:=/opt/ncs}" if [ ! -d "$NCS_ROOT" ]; then _ncs_warn "SDK root not found: $NCS_ROOT (set NCS_ROOT)" else # --- locate the SDK version directory --------------------------------------- # Prefer the tree that zephyr-env.sh is actually being sourced from, so the # toolchain always matches the SDK in use rather than guessing. if [ -z "${NCS_VERSION:-}" ] && [ -n "${ZEPHYR_BASE:-}" ]; then case "$ZEPHYR_BASE" in "$NCS_ROOT"/*) NCS_VERSION="${ZEPHYR_BASE#"$NCS_ROOT"/}"; NCS_VERSION="${NCS_VERSION%%/*}" ;; esac fi if [ -z "${NCS_VERSION:-}" ]; then NCS_VERSION="$( for d in "$NCS_ROOT"/v*/; do [ -d "$d/zephyr" ] && basename "$d" done | sort -V | tail -1 )" fi # --- locate the toolchain bundle -------------------------------------------- if [ -z "${NCS_TOOLCHAIN:-}" ]; then # Bundles are keyed by an opaque id and manifest.json carries no plain SDK # version to match on, so take the most recently installed. NCS_TOOLCHAIN="$( for d in "$NCS_ROOT"/toolchains/*/; do [ -f "$d/environment.json" ] && printf '%s %s\n' "$(stat -c %Y "$d")" "${d%/}" done | sort -n | tail -1 | cut -d' ' -f2- )" fi if [ -z "${NCS_TOOLCHAIN:-}" ] || [ ! -f "$NCS_TOOLCHAIN/environment.json" ]; then _ncs_warn "no toolchain bundle with environment.json under $NCS_ROOT/toolchains (set NCS_TOOLCHAIN)" elif [ "${_NCS_TOOLCHAIN_APPLIED:-}" = "$NCS_TOOLCHAIN" ]; then : # already applied in this shell; re-prepending PATH would just pile up else # --- apply the bundle's declared environment -------------------------------- # environment.json declares each variable as either a "string" or a list of # paths relative to the bundle root, plus how to combine with any existing # value ("prepend_to" or "overwrite"). Honour what it declares rather than # copying today's values, so added or changed variables are picked up too. # # Parsed with the host python: the bundle's python is only usable once # PYTHONHOME points at it, and a PYTHONHOME/PYTHONPATH left over from an # earlier sourcing would break it -- hence `env -u`. _ncs_env="$(env -u PYTHONHOME -u PYTHONPATH python3 - "$NCS_TOOLCHAIN" <<'PY' import json, os, shlex, sys root = sys.argv[1] with open(os.path.join(root, "environment.json")) as fh: spec = json.load(fh) for var in spec.get("env_vars", []): key = var["key"] if var["type"] == "string": value = var["value"] elif var["type"] == "relative_paths": value = os.pathsep.join(os.path.join(root, v) for v in var["values"]) else: continue if var.get("existing_value_treatment") == "prepend_to": existing = os.environ.get(key, "") if existing: value = value + os.pathsep + existing print(f"export {key}={shlex.quote(value)}") PY )" if [ -z "$_ncs_env" ]; then _ncs_warn "failed to read $NCS_TOOLCHAIN/environment.json" else eval "$_ncs_env" # Some bundles declare this as "zephyr/gnu", which the Zephyr build does # not accept; the SDK variant is plain "zephyr". case "${ZEPHYR_TOOLCHAIN_VARIANT:-}" in zephyr/*) export ZEPHYR_TOOLCHAIN_VARIANT=zephyr ;; esac export NCS_ROOT NCS_TOOLCHAIN [ -n "${NCS_VERSION:-}" ] && export NCS_VERSION NCS_WORKSPACE="$NCS_ROOT/$NCS_VERSION" # west resolves projects from the nearest .west found walking up from the # current directory. A repo outside the SDK tree has none, so `west build` # must run from inside the workspace with absolute -d and source paths. [ -n "${NCS_WORKSPACE:-}" ] && export WEST_TOPDIR="$NCS_WORKSPACE" export _NCS_TOOLCHAIN_APPLIED="$NCS_TOOLCHAIN" fi unset _ncs_env fi fi unset -f _ncs_warn