Skip to content

[question] How to build a toolchain package (or how to fix / debug) #18909

@hcw70

Description

@hcw70

What is your question?

I am a conan2 starter, so some concepts may not be clear yet to me.

I am using conan 2.20.1 and trying to build a conan package for our toolchains.

The toolchains should be identified by os.distro = "SW39344E" meaning a part number for a tar.gz containing a buildroot based toolchain.

So os.distro should be the primary key to identify the cross compilation target.

However i am failing to create a toolchain package, since the test_package step always fails:

deuta-toolchain/1.0.0: package(): Packaged 243 '.hpp' files
deuta-toolchain/1.0.0: Created package revision c3dcd9364f09cfa21acbb537e7082d51
deuta-toolchain/1.0.0: Package '9512ff797f287a1542c98c751aa30dd495b02fa4' created
deuta-toolchain/1.0.0: Full package reference: deuta-toolchain/1.0.0#65cf19a8800606e5e842c6ace20cc080:9512ff797f287a1542c98c751aa30dd495b02fa4#c3dcd9364f09cfa21acbb537e7082d51
deuta-toolchain/1.0.0: Package folder /home/hcw/.conan2/p/b/deuta64c91524e200a/p

======== Launching test_package ========

======== Computing dependency graph ========
deuta-toolchain/1.0.0 (test package): Using toolchain build-require: deuta-toolchain/1.0.0#65cf19a8800606e5e842c6ace20cc080%1757490224.9919577 for Linux-SW39-347E-x86_64
Graph root
    deuta-toolchain/1.0.0 (test package): /home/hcw/work/Deuta/deuta-packaging/conan-recipies/toolchain/test_package/conanfile.py
Build requirements
    deuta-toolchain/1.0.0#65cf19a8800606e5e842c6ace20cc080 - Cache

======== Computing necessary packages ========
Build requirements
    deuta-toolchain/1.0.0#65cf19a8800606e5e842c6ace20cc080:63fead0844576fc02943e16909f08fcdddd6f44b - Invalid
ERROR: There are invalid packages:
deuta-toolchain/1.0.0: Invalid: 'settings.os.distro' value not defined

It looks like the test package (or the toolchain package) does not define os.distro, although it does it...?

Maybe i am setting the package_id for the toolchain wrong, so the test_package is unable to find it?
Looks like the package is being built, but later not found when consuming by the test_package.

Here is my

settings.yml:

os:
  Linux:
    distro: [Ubuntu-22.04, Ubuntu-24.04, SW39-347E, SW39-373A, SW39-382A, SW39-388A]
  Windows:
    distro: [SW39-409A]

Profile SW39-347E:

[settings]
os=Linux
os.distro=SW39-347E
arch=armv7
compiler=gcc
compiler.version=11
compiler.libcxx=libstdc++11
build_type=Release

[conf]
tools.cmake.cmaketoolchain:generator=Ninja
tools.cmake.cmaketoolchain:system_name=SW39-347E
tools.cmake.cmaketoolchain:toolchain_file=/opt/deuta/SW39-347E/sdk/share/buildroot/toolchainfile.cmake

my toolchain/conanfile.py:

import os
from conan import ConanFile
from conan.tools.files import get, copy, download
from conan.errors import ConanInvalidConfiguration
from conan.tools.scm import Version

class ToolchainInfo:
    def __init__(self, url, sdk_sysroot_rel, toolchain_file_rel):
        self.url = url
        self.sdk_sysroot_rel = sdk_sysroot_rel
        self.toolchain_file_rel = toolchain_file_rel

# Where to find a toolchain and what properties it has
DISTROS = {
    "SW39-347E": ToolchainInfo("https://XXX.de/repository/device-firmware/DEUTA/DMI%20Image/Linux%204.14.108-ti-rt-r130/released/SW39-347E/sdk/arm-buildroot-linux-gnueabihf_sdk-buildroot.tar.gz",
                                "arm-buildroot-linux-gnueabihf/sysroot",
                                "share/buildroot/toolchainfile.cmake"
                               ),
}

class DeutaToolchain(ConanFile):
    name = "deuta-toolchain"
    version = "1.0.0"

    license = "GPL-3.0-only"
    homepage = "https://git.XXX.de/shared-tools/deuta-packaging"
    description = "Conan package for different toolchains."
    settings = "os", "arch"
    package_type = "application"

    def validate(self):
        if not hasattr(self.settings, "os"):
            raise ConanInvalidConfiguration("'settings.os' needs to be specified")
        if not hasattr(self.settings.os, "distro"):
            raise ConanInvalidConfiguration("'settings.os.distro' needs to be specified")

        if self.settings.os == "Linux" or self.settings.os == "Windows":
            if not hasattr(self.settings.os, 'distro'):
                raise ConanInvalidConfiguration("'settings.os.distro' needs to be specified")
        else:
            raise ConanInvalidConfiguration("'settings.os' must be Linux or Windows")

        if self.settings.os != "Linux":
            raise ConanInvalidConfiguration(f"This toolchain is not compatible with {self.settings.os}-{self.settings.arch}. "
                                            "It can only run on Linux-x86_64.")

        distro = str(self.settings.os.distro);

        if distro not in DISTROS:
            raise ConanInvalidConfiguration(f"Cross toolchain not available for {distro}")



    def source(self):
        pass


    def build(self):
        self.output.info(f"os={self.settings.os}")
        if hasattr(self.settings.os, "distro"):
            self.output.info(f"os.distro={self.settings.os.distro}")
        else:
            self.output.info("no os.distro available")

        toolchain = DISTROS[str(self.settings.os.distro)]

        tgz_url = toolchain.url

        get(self, tgz_url, destination=self.source_folder, strip_root=True, verify=False)

        # Run the relocate script if it exists
        relocate_script = os.path.join(self.source_folder, "relocate-sdk.sh")
        if os.path.exists(relocate_script):
            # Run the relocate script
            self.run(f"cd {self.source_folder} && ./relocate-sdk.sh")


    def package(self):
        # Copy everything to the package folder
        copy(self, "*",
             src=self.source_folder,
             dst=self.package_folder,
             keep_path=True)


    def package_id(self):
        # For toolchain packages, we care about target architecture and specific Linux distro/SDK
        # Remove settings that don't affect the toolchain binary compatibility

        self.info.settings.rm_safe("compiler")
        self.info.settings.rm_safe("build_type")
        # Keep arch as toolchains are architecture-specific
        # Keep os.distro as it encodes the concrete Linux image/SDK
        # Remove other os sub-settings that aren't relevant
        if hasattr(self.info.settings, "os"):
            self.info.settings.os.rm_safe("version")

    def package_info(self):
        distro = str(self.settings.os.distro)
        if distro not in DISTROS:
            raise ConanInvalidConfiguration(f"No toolchain info for distro {distro}")

        toolchain = DISTROS[str(self.settings.os.distro)]

        # Add the bin directory to make executables available
        self.cpp_info.bindirs.append("bin")

        # Set the CMake toolchain file location
        cmake_toolchain_path = os.path.join(self.package_folder, toolchain.toolchain_file_rel)

        self.conf_info.define("tools.cmake.cmaketoolchain:toolchain_file", cmake_toolchain_path)

        # Set environment variables for the toolchain
        self.buildenv_info.define_path("TOOLCHAIN_ROOT", self.package_folder)

    def deploy(self):
        toolchain = DISTROS[str()]
        # Deploy to the specific target directory
        target_path = f"/opt/deuta/{self.settings.os.distro}/sdk"

        # Ensure target directory exists
        os.makedirs(target_path, exist_ok=True)

        # Copy package contents to target location
        copy(self, "*",
             src=self.package_folder,
             dst=target_path,
             keep_path=True)

my toolchain/test_package/conanfile.py:

from io import StringIO
from conan import ConanFile
from conan.errors import ConanInvalidConfiguration
from conan.tools.cmake import CMake, cmake_layout
import os


class TestPackageConan(ConanFile):
    settings = "os", "arch", "compiler", "build_type"
    generators = "CMakeToolchain", "VirtualBuildEnv"

    def validate(self):
        if self.settings.os == "Linux":
            if not hasattr(self.settings.os, "distro"):
                raise ConanInvalidConfiguration("'settings.os.distro' must be defined")

    def configure(self):
        if not hasattr(self.settings.os, "distro"):
            raise ConanInvalidConfiguration("'settings.os.distro' must be defined")
        if self.settings.os == "Linux":
            _ = self.settings.os.distro  # touch the subsetting
        if self.settings.os == "Windows":
            _ = self.settings.os.distro

    def build_requirements(self):
        if not hasattr(self.settings.os, "distro"):
            raise ConanInvalidConfiguration("'settings.os.distro' must be defined")
        distro = str(self.settings.os.distro)
        self.output.info(
            f"Using toolchain build-require: {self.tested_reference_str} "
            f"for {self.settings.os}-{distro}-{self.settings.arch}"
        )
        self.tool_requires(self.tested_reference_str)


    def layout(self):
        cmake_layout(self)

    def build(self):
        if not hasattr(self.settings.os, "distro"):
            raise ConanInvalidConfiguration("'settings.os.distro' must be defined")
        distro = str(self.settings.os.distro)
        self.output.info(f"Building test_package for {self.settings.os}-{distro}-{self.settings.arch}")

        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def test(self):
        if not hasattr(self.settings.os, "distro"):
            raise ConanInvalidConfiguration("'settings.os.distro' must be defined")
        if self.settings.arch in ["armv6", "armv7", "armv7hf"]:
            toolchain = "arm-none-linux-gnueabihf"
        else:
            toolchain = "aarch64-none-linux-gnu"
        self.run(f"{toolchain}-gcc --version")
        test_file = os.path.join(self.cpp.build.bindirs[0], "test_package")
        stdout = StringIO()
        self.run(f"file {test_file}", stdout=stdout)
        if toolchain == "aarch64-none-linux-gnu":
            assert "ELF 64-bit" in stdout.getvalue()
        else:
            assert "ELF 32-bit" in stdout.getvalue()

Aim is to set up something similair to https://docs.conan.io/2/examples/cross_build/toolchain_packages.html
adapted to our toolchain, and our aim to have os.distro as the primary key to identify cross targets.

Have you read the CONTRIBUTING guide?

  • [X ] I've read the CONTRIBUTING guide

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions