@@ -474,7 +474,10 @@ Setting up
sudo mkdir -p /toolchains
sudo mv ~/.buildman-toolchains/*/* /toolchains/
- Buildman should now be set up to use your new toolchain.
+ Buildman should now be set up to use your new toolchain. Downloaded
+ toolchains are given priority over system-installed toolchains, so if you
+ have both a downloaded toolchain and one installed via your
+ distribution's package manager, the downloaded one will be used.
At the time of writing, U-Boot has these architectures:
@@ -938,13 +941,31 @@ a set of (tag, value) pairs.
'[toolchain-prefix]' section
This can be used to provide the full toolchain-prefix for one or more
- architectures. The full CROSS_COMPILE prefix must be provided. These
- typically have a higher priority than matches in the '[toolchain]', due to
- this prefix.
+ architectures. The full CROSS_COMPILE prefix must be provided.
The tilde character ``~`` is supported in paths, to represent the home
directory.
+Toolchain priority
+ When multiple toolchains are available for an architecture, buildman
+ selects the one with the highest priority (lowest priority number).
+
+ Note: Lower numbers indicate higher priority, so a toolchain with
+ priority 3 is preferred over one with priority 6.
+
+ The priority levels are:
+
+ - 0: Full prefix path from '[toolchain-prefix]' that exists as a file
+ - 1: Prefix from '[toolchain-prefix]' with 'gcc' appended that exists
+ - 2: Prefix from '[toolchain-prefix]' found in PATH
+ - 3: Downloaded toolchains (from ``--fetch-arch``)
+ - 4+: Toolchains found by scanning '[toolchain]' paths (priority
+ calculated from filename, e.g. '-linux' variants get priority 6)
+
+ This means that downloaded toolchains are preferred over system-installed
+ toolchains (e.g. from a distribution package), but explicit
+ '[toolchain-prefix]' entries take the highest priority.
+
'[toolchain-alias]' section
This converts toolchain architecture names to U-Boot names. For example,
if an x86 toolchains is called i386-linux-gcc it will not normally be
@@ -701,6 +701,49 @@ class TestBuild(TestBuildBase):
'crosstool/files/bin/x86_64/.*/'
'x86_64-gcc-.*-nolibc[-_]arm-.*linux-gnueabi.tar.xz')
+ def test_toolchain_download_priority(self):
+ """Test that downloaded toolchains have priority over system ones"""
+ # Create a temp directory structure with two toolchains for same arch
+ with tempfile.TemporaryDirectory() as tmpdir:
+ # Create 'system' toolchain path (simulating /usr/bin)
+ system_path = os.path.join(tmpdir, 'system')
+ os.makedirs(os.path.join(system_path, 'bin'))
+ system_gcc = os.path.join(system_path, 'bin', 'aarch64-linux-gcc')
+ tools.write_file(system_gcc, b'#!/bin/sh\necho gcc')
+ os.chmod(system_gcc, 0o755)
+
+ # Create 'download' toolchain path
+ download_path = os.path.join(tmpdir, 'download')
+ os.makedirs(os.path.join(download_path, 'bin'))
+ download_gcc = os.path.join(download_path, 'bin',
+ 'aarch64-linux-gcc')
+ tools.write_file(download_gcc, b'#!/bin/sh\necho gcc')
+ os.chmod(download_gcc, 0o755)
+
+ # Check system toolchain priority (not in download_paths)
+ sys_tc = toolchain.Toolchain(system_gcc, test=False)
+ self.assertEqual(toolchain.PRIORITY_CALC + 2, sys_tc.priority)
+
+ # Set up toolchains with download path tracked
+ tcs = toolchain.Toolchains()
+ tcs.paths = [system_path, download_path]
+ tcs.download_paths = {download_path}
+
+ # Scan and check which toolchain is selected
+ with terminal.capture():
+ tcs.scan(False, raise_on_error=False)
+
+ # The downloaded toolchain should be selected
+ tc = tcs.toolchains.get('aarch64')
+ self.assertIsNotNone(tc)
+ self.assertTrue(tc.gcc.startswith(download_path),
+ f"Expected downloaded toolchain from {download_path}, "
+ f"got {tc.gcc}")
+ self.assertEqual(toolchain.PRIORITY_DOWNLOADED, tc.priority)
+
+ # Verify downloaded priority beats system priority
+ self.assertLess(toolchain.PRIORITY_DOWNLOADED, sys_tc.priority)
+
def test_get_env_args(self):
"""Test the GetEnvArgs() function"""
tc = self.toolchains.select('arm')
@@ -17,9 +17,17 @@ from u_boot_pylib import command
from u_boot_pylib import terminal
from u_boot_pylib import tools
+# Toolchain priority levels (lower number = higher priority):
+# PRIORITY_FULL_PREFIX: Explicit [toolchain-prefix] path exists as a file
+# PRIORITY_PREFIX_GCC: [toolchain-prefix] path + 'gcc' exists as a file
+# PRIORITY_PREFIX_GCC_PATH: [toolchain-prefix] path + 'gcc' found in PATH
+# PRIORITY_DOWNLOADED: Toolchain downloaded via --fetch-arch
+# PRIORITY_CALC: Toolchain found by scanning [toolchain] paths; actual
+# priority is PRIORITY_CALC + offset based on toolchain name
(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH,
- PRIORITY_CALC) = list(range(4))
+ PRIORITY_DOWNLOADED, PRIORITY_CALC) = list(range(5))
+# Environment variable / argument types for get_env_args()
(VAR_CROSS_COMPILE, VAR_PATH, VAR_ARCH, VAR_MAKE_ARGS) = range(4)
class MyHTMLParser(HTMLParser):
@@ -290,6 +298,7 @@ class Toolchains:
self.toolchains = {}
self.prefixes = {}
self.paths = []
+ self.download_paths = set()
self.override_toolchain = override_toolchain
self._make_flags = dict(bsettings.get_items('make-flags'))
@@ -330,6 +339,15 @@ class Toolchains:
self.prefixes = bsettings.get_items('toolchain-prefix')
self.paths += self.get_path_list(show_warning)
+ # Track which paths are from downloaded toolchains
+ for name, value in bsettings.get_items('toolchain'):
+ if name == 'download':
+ fname = os.path.expanduser(value)
+ if '*' in value:
+ self.download_paths.update(glob.glob(fname))
+ else:
+ self.download_paths.add(fname)
+
# pylint: disable=too-many-arguments,too-many-positional-arguments
def add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
arch=None):
@@ -435,8 +453,10 @@ class Toolchains:
if verbose:
print(f" - scanning path '{path}'")
fnames = self.scan_path(path, verbose)
+ priority = (PRIORITY_DOWNLOADED if path in self.download_paths
+ else PRIORITY_CALC)
for fname in fnames:
- self.add(fname, True, verbose)
+ self.add(fname, True, verbose, priority)
def list(self):
"""List out the selected toolchains for each architecture"""