"""Chroot-based toolchain provider for Fester. Fester can use any distro chroot as a build environment, not just BTC.sh. The chroot provider supports: - **native** — use the host system's toolchain (ccache gcc/g++) - **buildroot** — use a Buildroot output tree as a cross-compiler sysroot - **sourcemage** — use a SourceMage GL test chroot as the build environment - **gentoo** — use a Gentoo stage3 chroot as the build environment - **lede** — use a LEDE/OpenWrt SDK as a cross-compiler (mipsel/arm) - **lunar** — use a Lunar Linux chroot as the build environment - **chroot** — generic distro chroot (auto-detect toolchain inside) Each provider probes for the chroot/sysroot on disk and, if available, returns a compiler environment dict (CC, CXX, CFLAGS, CXXFLAGS, LDFLAGS, PATH) that is merged into the executor environment. The provider is selected via the ``toolchain.provider`` config key or per-node via ``toolchain.`` in node config. Design follows CLFS / Buildroot patterns: - Cross-compilers are detected by their triple prefix in the chroot's /usr/bin/ directory (e.g., arm-linux-gnueabihf-gcc). - The chroot's /lib, /usr/lib, and /usr/include are used as implicit sysroots (via --sysroot or -I/-L flags). - For buildroot, the output/host/ and output/staging/ trees are used directly (Buildroot's standard layout). """ import logging import os import re import shutil from pathlib import Path from typing import Any, Dict, List, Optional, Tuple logger = logging.getLogger(__name__) # --------------------------------------------------------------------------- # Provider registry — maps provider name → probe function # --------------------------------------------------------------------------- # Table-driven provider lookup (PEP 868 / SEI CERT CTR50-JP). _PROVIDERS: Dict[str, "ProviderSpec"] = {} class ProviderSpec: """Metadata and probe function for a toolchain provider.""" def __init__( self, name: str, description: str, probe_fn, env_fn, ): self.name = name self.description = description self.probe = probe_fn # (config) -> Dict[str, Any] self.build_env = env_fn # (config) -> Dict[str, str] def register_provider(spec: ProviderSpec) -> None: """Register a toolchain provider.""" _PROVIDERS[spec.name] = spec def get_provider(name: str) -> Optional[ProviderSpec]: """Look up a registered provider by name. Returns None if unknown.""" return _PROVIDERS.get(name) def list_providers() -> List[Dict[str, str]]: """Return a list of all registered providers with name and description.""" return [ {"name": spec.name, "description": spec.description} for spec in sorted(_PROVIDERS.values(), key=lambda s: s.name) ] # --------------------------------------------------------------------------- # Generic chroot toolchain detection # --------------------------------------------------------------------------- # Regex to detect cross-compiler triple prefixes in /usr/bin/. # Matches patterns like: arm-linux-gnueabihf-gcc, mipsel-openwrt-linux-gcc, # x86_64-pc-linux-gnu-gcc, etc. _CROSS_GCC_RE = re.compile( r"^(.+?)-gcc$" ) # Known cross-compiler triple patterns and their arch family. # Used to classify detected cross-compilers by architecture. _TRIPLE_ARCH_MAP: Dict[str, str] = { "arm": "arm", "aarch64": "arm64", "mipsel": "mipsel", "mips": "mips", "x86_64": "x86_64", "i686": "x86_64", "tilegx": "tilegx", "riscv64": "riscv64", } def _detect_cross_compilers(chroot_path: str) -> List[Dict[str, str]]: """Scan a chroot's /usr/bin/ for cross-compiler triples. Returns a list of dicts with keys: triple, cc, cxx, arch, prefix. Sorted by triple for deterministic ordering. """ bin_dir = os.path.join(chroot_path, "usr", "bin") if not os.path.isdir(bin_dir): return [] compilers = [] for entry in os.listdir(bin_dir): m = _CROSS_GCC_RE.match(entry) if not m: continue triple = m.group(1) gcc_path = os.path.join(bin_dir, entry) gxx_path = os.path.join(bin_dir, f"{triple}-g++") if not os.path.isfile(gcc_path): continue # Classify the arch from the triple's first component. arch = "unknown" for prefix, family in _TRIPLE_ARCH_MAP.items(): if triple.startswith(prefix + "-") or triple.startswith(prefix + "_"): arch = family break compilers.append({ "triple": triple, "cc": gcc_path, "cxx": gxx_path if os.path.isfile(gxx_path) else gcc_path, "arch": arch, "prefix": os.path.join(bin_dir, f"{triple}-"), }) compilers.sort(key=lambda c: c["triple"]) return compilers def _chroot_native_env(chroot_path: str, config: Dict[str, Any]) -> Dict[str, str]: """Build environment for a native chroot toolchain. Sets CC/CXX to the chroot's gcc/g++, adds the chroot's /usr/bin and /bin to PATH, and points -I/-L at the chroot's sysroot directories. """ env: Dict[str, str] = {} # Prefer the chroot's compiler over the host's. chroot_gcc = os.path.join(chroot_path, "usr", "bin", "gcc") chroot_gxx = os.path.join(chroot_path, "usr", "bin", "g++") if os.path.isfile(chroot_gcc): env["CC"] = chroot_gcc else: env["CC"] = "gcc" if os.path.isfile(chroot_gxx): env["CXX"] = chroot_gxx else: env["CXX"] = "g++" # Point include and library paths at the chroot. sysroot_include = os.path.join(chroot_path, "usr", "include") sysroot_lib = os.path.join(chroot_path, "usr", "lib") include_flags = f" -I{sysroot_include}" if os.path.isdir(sysroot_include) else "" lib_flags = f" -L{sysroot_lib}" if os.path.isdir(sysroot_lib) else "" # User-supplied flags from config. extra_cflags = config.get("cflags", "") extra_ldflags = config.get("ldflags", "") env["CFLAGS"] = f"-O2 -pipe{include_flags} {extra_cflags}".rstrip() env["CXXFLAGS"] = env["CFLAGS"] env["LDFLAGS"] = f"-Wl,-O1 -Wl,--as-needed{lib_flags} {extra_ldflags}".rstrip() # Add chroot paths to PATH so the executor can find tools. chroot_bin = os.path.join(chroot_path, "usr", "bin") chroot_sbin = os.path.join(chroot_path, "usr", "sbin") host_path = os.environ.get("PATH", "/usr/bin:/bin") env["PATH"] = f"{chroot_bin}:{chroot_sbin}:{host_path}" env["FESTER_CHROOT"] = chroot_path env["toolchain_source"] = "chroot" return env def _chroot_cross_env( chroot_path: str, compiler: Dict[str, str], config: Dict[str, Any], ) -> Dict[str, str]: """Build environment for a cross-compiler inside a chroot. Uses the detected cross-compiler triple prefix for CC/CXX, and sets sysroot flags to the chroot's include/lib directories. """ env: Dict[str, str] = {} env["CC"] = compiler["cc"] env["CXX"] = compiler["cxx"] triple = compiler["triple"] # Sysroot paths from the chroot. sysroot_include = os.path.join(chroot_path, "usr", "include") sysroot_lib = os.path.join(chroot_path, "usr", "lib") sysroot = config.get("sysroot", chroot_path) sysroot_flags = f" --sysroot={sysroot}" if os.path.isdir(sysroot) else "" include_flags = f" -I{sysroot_include}" if os.path.isdir(sysroot_include) and not sysroot_flags else "" lib_flags = f" -L{sysroot_lib}" if os.path.isdir(sysroot_lib) and not sysroot_flags else "" # User-supplied flags. extra_cflags = config.get("cflags", "") extra_ldflags = config.get("ldflags", "") march = config.get("march", "") march_flag = f" -march={march}" if march else "" env["CFLAGS"] = f"-O2{march_flag}{sysroot_flags}{include_flags} -pipe {extra_cflags}".rstrip() env["CXXFLAGS"] = env["CFLAGS"] env["LDFLAGS"] = f"-Wl,-O1 -Wl,--as-needed{sysroot_flags}{lib_flags} {extra_ldflags}".rstrip() # Cross-compile metadata for executors. env["FESTER_CHROOT"] = chroot_path env["FESTER_CROSS_TRIPLE"] = triple env["FESTER_CROSS_ARCH"] = compiler["arch"] env["toolchain_source"] = "chroot-cross" return env # --------------------------------------------------------------------------- # Provider implementations # --------------------------------------------------------------------------- # --- Generic chroot provider --- def _probe_chroot(config: Dict[str, Any]) -> Dict[str, Any]: """Probe a generic distro chroot for available toolchains.""" path = config.get("path", "") if not path or not os.path.isdir(path): return {"available": False, "reason": "chroot path not found"} # Check for essential chroot structure. has_bin = os.path.isdir(os.path.join(path, "usr", "bin")) has_lib = os.path.isdir(os.path.join(path, "usr", "lib")) if not has_bin or not has_lib: return {"available": False, "reason": "chroot missing usr/bin or usr/lib"} # Detect cross-compilers. cross = _detect_cross_compilers(path) has_native = os.path.isfile(os.path.join(path, "usr", "bin", "gcc")) return { "available": has_native or len(cross) > 0, "path": path, "has_native": has_native, "cross_compilers": cross, "provider": "chroot", } def _env_chroot(config: Dict[str, Any]) -> Dict[str, str]: """Build environment from a generic chroot.""" probe = _probe_chroot(config) if not probe["available"]: return {"toolchain_source": "gcc"} path = probe["path"] # If a specific cross target is requested, use it. target_arch = config.get("target_arch", "") if target_arch and probe["cross_compilers"]: for cc in probe["cross_compilers"]: if cc["arch"] == target_arch: return _chroot_cross_env(path, cc, config) # If there's exactly one cross-compiler, use it. if len(probe["cross_compilers"]) == 1 and not probe["has_native"]: return _chroot_cross_env(path, probe["cross_compilers"][0], config) # Default to native. return _chroot_native_env(path, config) # --- Buildroot provider --- def _probe_buildroot(config: Dict[str, Any]) -> Dict[str, Any]: """Probe a Buildroot output tree for cross-compilers. Buildroot layout (standard): output/host/ — host cross-compiler (bin/, lib/, usr/) output/staging/ — target sysroot (usr/include, usr/lib) output/target/ — root filesystem image """ path = config.get("path", "") if not path or not os.path.isdir(path): return {"available": False, "reason": "buildroot path not found"} # Detect standard Buildroot layout. host_dir = os.path.join(path, "output", "host") staging_dir = os.path.join(path, "output", "staging") if not os.path.isdir(host_dir): return {"available": False, "reason": "buildroot output/host/ not found"} # Scan for cross-compilers in output/host/bin/. cross = _detect_cross_compilers(host_dir) return { "available": len(cross) > 0, "path": path, "host_dir": host_dir, "staging_dir": staging_dir, "cross_compilers": cross, "provider": "buildroot", } def _env_buildroot(config: Dict[str, Any]) -> Dict[str, str]: """Build environment from a Buildroot SDK.""" probe = _probe_buildroot(config) if not probe["available"]: return {"toolchain_source": "gcc"} host_dir = probe["host_dir"] staging_dir = probe["staging_dir"] # Select cross-compiler. target_arch = config.get("target_arch", "") compiler = None if target_arch: for cc in probe["cross_compilers"]: if cc["arch"] == target_arch: compiler = cc break if compiler is None and probe["cross_compilers"]: compiler = probe["cross_compilers"][0] if compiler is None: return {"toolchain_source": "gcc"} env: Dict[str, str] = {} env["CC"] = compiler["cc"] env["CXX"] = compiler["cxx"] # Buildroot uses staging/ as the sysroot for target headers/libs. sysroot = staging_dir if os.path.isdir(staging_dir) else host_dir sysroot_flag = f" --sysroot={sysroot}" # Buildroot host/bin also contains pkg-config and other tools. host_bin = os.path.join(host_dir, "bin") host_path = os.environ.get("PATH", "/usr/bin:/bin") march = config.get("march", "") march_flag = f" -march={march}" if march else "" env["CFLAGS"] = f"-O2{march_flag}{sysroot_flag} -pipe" env["CXXFLAGS"] = env["CFLAGS"] env["LDFLAGS"] = f"-Wl,-O1 -Wl,--as-needed{sysroot_flag}" env["PATH"] = f"{host_bin}:{host_path}" env["PKG_CONFIG_SYSROOT_DIR"] = sysroot env["PKG_CONFIG_PATH"] = os.path.join(sysroot, "usr", "lib", "pkgconfig") env["FESTER_BUILDROOT"] = probe["path"] env["FESTER_CROSS_TRIPLE"] = compiler["triple"] env["FESTER_CROSS_ARCH"] = compiler["arch"] env["toolchain_source"] = "buildroot" return env # --- SourceMage chroot provider --- def _probe_sourcemage(config: Dict[str, Any]) -> Dict[str, Any]: """Probe a SourceMage GL test chroot. SourceMage chroots contain the standard GNU toolchain plus the sorcery spell system. The chroot is typically created via: sorcery cast chroot # or: smgl-chroot create /var/lib/smgl-chroot Detection: look for /var/lib/sorcery inside the chroot path. """ path = config.get("path", "") if not path or not os.path.isdir(path): return {"available": False, "reason": "sourcemage chroot path not found"} # SourceMage marker: sorcery config directory. smgl_marker = os.path.join(path, "var", "lib", "sorcery") if not os.path.isdir(smgl_marker): return {"available": False, "reason": "not a SourceMage chroot (no var/lib/sorcery)"} has_gcc = os.path.isfile(os.path.join(path, "usr", "bin", "gcc")) if not has_gcc: return {"available": False, "reason": "sourcemage chroot missing gcc"} cross = _detect_cross_compilers(path) return { "available": True, "path": path, "has_native": True, "cross_compilers": cross, "provider": "sourcemage", } def _env_sourcemage(config: Dict[str, Any]) -> Dict[str, str]: """Build environment from a SourceMage chroot.""" probe = _probe_sourcemage(config) if not probe["available"]: return {"toolchain_source": "gcc"} path = probe["path"] # SourceMage supports cross-compiles via sorcery's CROSS_COMPILE # variable. If a cross-compiler is present, use it. target_arch = config.get("target_arch", "") if target_arch and probe["cross_compilers"]: for cc in probe["cross_compilers"]: if cc["arch"] == target_arch: return _chroot_cross_env(path, cc, config) env = _chroot_native_env(path, config) env["toolchain_source"] = "sourcemage" # SourceMage-specific: expose sorcery inside the chroot. env["FESTER_SOURCEMAGE"] = path sorcery_bin = os.path.join(path, "usr", "sbin") if os.path.isdir(sorcery_bin): env["PATH"] = f"{sorcery_bin}:{env.get('PATH', '')}" return env # --- Gentoo stage3 chroot provider --- def _probe_gentoo(config: Dict[str, Any]) -> Dict[str, Any]: """Probe a Gentoo stage3 chroot. Gentoo chroots are typically unpacked from a stage3 tarball at a path like /var/lib/gentoo-chroot/. Detection: look for /etc/gentoo-release inside the chroot. Gentoo's cross-compilation is handled by crossdev, which installs cross-compilers to /usr//gcc-bin//. """ path = config.get("path", "") if not path or not os.path.isdir(path): return {"available": False, "reason": "gentoo chroot path not found"} gentoo_marker = os.path.join(path, "etc", "gentoo-release") if not os.path.isfile(gentoo_marker): return {"available": False, "reason": "not a Gentoo chroot (no etc/gentoo-release)"} has_gcc = os.path.isfile(os.path.join(path, "usr", "bin", "gcc")) if not has_gcc: return {"available": False, "reason": "gentoo chroot missing gcc"} # Gentoo crossdev installs to /usr//gcc-bin// cross = _detect_cross_compilers(path) # Also check Gentoo's crossdev-specific layout. usr_contents = os.listdir(os.path.join(path, "usr")) if os.path.isdir(os.path.join(path, "usr")) else [] for entry in usr_contents: crossdev_bin = os.path.join(path, "usr", entry, "gcc-bin") if os.path.isdir(crossdev_bin): for ver in os.listdir(crossdev_bin): ver_dir = os.path.join(crossdev_bin, ver) if os.path.isdir(ver_dir): for tool in os.listdir(ver_dir): if tool.endswith("-gcc"): triple = tool[:-4] gcc_path = os.path.join(ver_dir, tool) gxx_name = tool.replace("-gcc", "-g++") gxx_path = os.path.join(ver_dir, gxx_name) arch = "unknown" for prefix, family in _TRIPLE_ARCH_MAP.items(): if triple.startswith(prefix + "-") or triple.startswith(prefix + "_"): arch = family break cross.append({ "triple": triple, "cc": gcc_path, "cxx": gxx_path if os.path.isfile(gxx_path) else gcc_path, "arch": arch, "prefix": os.path.join(ver_dir, f"{triple}-"), }) # Deduplicate by triple. seen = set() deduped = [] for cc in sorted(cross, key=lambda c: c["triple"]): if cc["triple"] not in seen: seen.add(cc["triple"]) deduped.append(cc) return { "available": True, "path": path, "has_native": True, "cross_compilers": deduped, "provider": "gentoo", } def _env_gentoo(config: Dict[str, Any]) -> Dict[str, str]: """Build environment from a Gentoo stage3 chroot.""" probe = _probe_gentoo(config) if not probe["available"]: return {"toolchain_source": "gcc"} path = probe["path"] target_arch = config.get("target_arch", "") if target_arch and probe["cross_compilers"]: for cc in probe["cross_compilers"]: if cc["arch"] == target_arch: return _chroot_cross_env(path, cc, config) env = _chroot_native_env(path, config) env["toolchain_source"] = "gentoo" env["FESTER_GENTOO"] = path # Gentoo-specific: expose portage. portage_bin = os.path.join(path, "usr", "sbin") if os.path.isdir(portage_bin): env["PATH"] = f"{portage_bin}:{env.get('PATH', '')}" return env # --- LEDE / OpenWrt SDK provider --- def _probe_lede(config: Dict[str, Any]) -> Dict[str, Any]: """Probe a LEDE/OpenWrt SDK for cross-compilers. LEDE/OpenWrt SDK layout: staging_dir/ — target sysroot staging_dir/toolchain/ — cross-compiler (bin/, lib/, include/) staging_dir/target/ — target root filesystem The cross-compiler triple is typically mipsel-openwrt-linux-* or arm-openwrt-linux-*. """ path = config.get("path", "") if not path or not os.path.isdir(path): return {"available": False, "reason": "lede/openwrt path not found"} # Detect LEDE/OpenWrt SDK markers. staging_dir = None for candidate in [ os.path.join(path, "staging_dir"), os.path.join(path, "staging_dir", "toolchain"), os.path.join(path, "openwrt", "staging_dir"), ]: if os.path.isdir(candidate): staging_dir = candidate break if staging_dir is None: return {"available": False, "reason": "no staging_dir/ found (not a LEDE/OpenWrt SDK)"} # LEDE cross-compilers are typically in staging_dir/toolchain/bin/ toolchain_bin = os.path.join(staging_dir, "bin") if not os.path.isdir(toolchain_bin): # Try staging_dir/toolchain/bin/ as a subdirectory toolchain_bin = os.path.join(path, "staging_dir", "toolchain", "bin") cross = _detect_cross_compilers(staging_dir) if os.path.isdir(staging_dir) else [] # Also scan toolchain/bin specifically. if os.path.isdir(toolchain_bin): for entry in os.listdir(toolchain_bin): m = _CROSS_GCC_RE.match(entry) if not m: continue triple = m.group(1) gcc_path = os.path.join(toolchain_bin, entry) if os.path.isfile(gcc_path): # Check for duplicates. existing = [c for c in cross if c["triple"] == triple] if not existing: arch = "unknown" for prefix, family in _TRIPLE_ARCH_MAP.items(): if triple.startswith(prefix + "-") or triple.startswith(prefix + "_"): arch = family break cross.append({ "triple": triple, "cc": gcc_path, "cxx": os.path.join(toolchain_bin, f"{triple}-g++"), "arch": arch, "prefix": os.path.join(toolchain_bin, f"{triple}-"), }) return { "available": len(cross) > 0, "path": path, "staging_dir": staging_dir, "cross_compilers": cross, "provider": "lede", } def _env_lede(config: Dict[str, Any]) -> Dict[str, str]: """Build environment from a LEDE/OpenWrt SDK.""" probe = _probe_lede(config) if not probe["available"]: return {"toolchain_source": "gcc"} target_arch = config.get("target_arch", "") compiler = None if target_arch: for cc in probe["cross_compilers"]: if cc["arch"] == target_arch: compiler = cc break if compiler is None and probe["cross_compilers"]: compiler = probe["cross_compilers"][0] if compiler is None: return {"toolchain_source": "gcc"} staging_dir = probe["staging_dir"] env: Dict[str, str] = {} env["CC"] = compiler["cc"] env["CXX"] = compiler["cxx"] # LEDE sysroot is staging_dir/target/ or staging_dir/ itself. target_dir = os.path.join(staging_dir, "target") sysroot = target_dir if os.path.isdir(target_dir) else staging_dir sysroot_flag = f" --sysroot={sysroot}" # LEDE toolchain/bin/ has wrapper scripts that need to be on PATH. toolchain_bin = os.path.join(staging_dir, "bin") host_path = os.environ.get("PATH", "/usr/bin:/bin") env["CFLAGS"] = f"-O2{sysroot_flag} -pipe" env["CXXFLAGS"] = env["CFLAGS"] env["LDFLAGS"] = f"-Wl,-O1 -Wl,--as-needed{sysroot_flag}" env["PATH"] = f"{toolchain_bin}:{host_path}" env["FESTER_LEDE"] = probe["path"] env["FESTER_CROSS_TRIPLE"] = compiler["triple"] env["FESTER_CROSS_ARCH"] = compiler["arch"] env["toolchain_source"] = "lede" return env # --- Lunar Linux chroot provider --- def _probe_lunar(config: Dict[str, Any]) -> Dict[str, Any]: """Probe a Lunar Linux chroot. Lunar Linux uses the moonbase package system. Chroots are typically at /var/lib/lunar-chroot/. Detection: look for /var/lib/lunar/ inside the chroot path. """ path = config.get("path", "") if not path or not os.path.isdir(path): return {"available": False, "reason": "lunar chroot path not found"} lunar_marker = os.path.join(path, "var", "lib", "lunar") if not os.path.isdir(lunar_marker): return {"available": False, "reason": "not a Lunar Linux chroot (no var/lib/lunar)"} has_gcc = os.path.isfile(os.path.join(path, "usr", "bin", "gcc")) if not has_gcc: return {"available": False, "reason": "lunar chroot missing gcc"} cross = _detect_cross_compilers(path) return { "available": True, "path": path, "has_native": True, "cross_compilers": cross, "provider": "lunar", } def _env_lunar(config: Dict[str, Any]) -> Dict[str, str]: """Build environment from a Lunar Linux chroot.""" probe = _probe_lunar(config) if not probe["available"]: return {"toolchain_source": "gcc"} path = probe["path"] target_arch = config.get("target_arch", "") if target_arch and probe["cross_compilers"]: for cc in probe["cross_compilers"]: if cc["arch"] == target_arch: return _chroot_cross_env(path, cc, config) env = _chroot_native_env(path, config) env["toolchain_source"] = "lunar" env["FESTER_LUNAR"] = path # Lunar-specific: expose moonbase tools. lunar_bin = os.path.join(path, "usr", "sbin") if os.path.isdir(lunar_bin): env["PATH"] = f"{lunar_bin}:{env.get('PATH', '')}" return env # --- Native (host) provider --- def _probe_native(config: Dict[str, Any]) -> Dict[str, Any]: """Probe the host system's native toolchain. Always available as a fallback. Uses the host's gcc/g++ directly. """ has_gcc = shutil.which("gcc") is not None return { "available": has_gcc, "provider": "native", "has_native": has_gcc, "cross_compilers": _detect_cross_compilers("/"), } def _env_native(config: Dict[str, Any]) -> Dict[str, str]: """Build environment using the host's native toolchain. This is the no-chroot, no-BTC, no-Buildroot fallback. Just ccache + distcc + the host gcc. """ env: Dict[str, str] = {} env["CC"] = os.environ.get("CC", "ccache gcc") env["CXX"] = os.environ.get("CXX", "ccache g++") env["CFLAGS"] = os.environ.get("CFLAGS", "-O2 -pipe") env["CXXFLAGS"] = os.environ.get("CXXFLAGS", env["CFLAGS"]) env["LDFLAGS"] = os.environ.get("LDFLAGS", "-Wl,-O1 -Wl,--as-needed") env["toolchain_source"] = "native" # If the host has cross-compilers (e.g., from apt install gcc-arm-linux-gnueabihf), # detect and expose them. target_arch = config.get("target_arch", "") if target_arch: host_cross = _detect_cross_compilers("/") for cc in host_cross: if cc["arch"] == target_arch: env["CC"] = cc["cc"] env["CXX"] = cc["cxx"] env["FESTER_CROSS_TRIPLE"] = cc["triple"] env["FESTER_CROSS_ARCH"] = cc["arch"] env["toolchain_source"] = "native-cross" break return env # --------------------------------------------------------------------------- # Register all providers # --------------------------------------------------------------------------- register_provider(ProviderSpec( name="native", description="Host system toolchain (gcc/g++ with ccache/distcc)", probe_fn=_probe_native, env_fn=_env_native, )) register_provider(ProviderSpec( name="buildroot", description="Buildroot SDK cross-compiler (output/host/ + staging/)", probe_fn=_probe_buildroot, env_fn=_env_buildroot, )) register_provider(ProviderSpec( name="sourcemage", description="SourceMage GL chroot (sorcery spell system)", probe_fn=_probe_sourcemage, env_fn=_env_sourcemage, )) register_provider(ProviderSpec( name="gentoo", description="Gentoo stage3 chroot (portage + crossdev)", probe_fn=_probe_gentoo, env_fn=_env_gentoo, )) register_provider(ProviderSpec( name="lede", description="LEDE/OpenWrt SDK (mipsel/arm router toolchains)", probe_fn=_probe_lede, env_fn=_env_lede, )) register_provider(ProviderSpec( name="lunar", description="Lunar Linux chroot (moonbase package system)", probe_fn=_probe_lunar, env_fn=_env_lunar, )) register_provider(ProviderSpec( name="chroot", description="Generic distro chroot (auto-detect toolchain)", probe_fn=_probe_chroot, env_fn=_env_chroot, ))