Skip to content

Commit 725d2c1

Browse files
authored
Loosen compiler version restriction (#354)
1 parent 450b5e2 commit 725d2c1

3 files changed

Lines changed: 96 additions & 7 deletions

File tree

.github/workflows/sanity-test.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ jobs:
8585
strategy:
8686
fail-fast: false
8787
matrix:
88-
host: [ubuntu-22.04, macos-14, macos-15, windows-2025]
88+
host: [ubuntu-22.04, macos-14, macos-15, macos-26, windows-2025]
8989
needs: package
9090
runs-on: ${{ matrix.host }}
9191
steps:
@@ -142,6 +142,31 @@ jobs:
142142
run: |
143143
python builder.pyz build --project aws-c-common
144144
145+
# If we explicitly set the compiler version, the compiler version should be in the
146+
# version list.
147+
compiler_version_explicit_out_of_list_fails:
148+
name: Explicit appleclang-21 fails
149+
needs: package
150+
runs-on: macos-26
151+
steps:
152+
- name: Checkout Source
153+
uses: actions/checkout@v4
154+
155+
- name: Install builder
156+
uses: actions/download-artifact@v4
157+
with:
158+
name: builder
159+
path: .
160+
161+
- name: Build with explicit appleclang-21 should fail
162+
run: |
163+
if python3 builder.pyz build --project tests --compiler=appleclang-21; then
164+
echo "ERROR: Expected builder to fail with appleclang-21 but it succeeded"
165+
exit 1
166+
else
167+
echo "PASSED: builder correctly rejected explicit appleclang-21 (not in known versions list)"
168+
fi
169+
145170
# Make sure cross compiling works
146171
cross_compile:
147172
runs-on: ubuntu-24.04
@@ -387,6 +412,7 @@ jobs:
387412
needs:
388413
- unit_test
389414
- sanity_test
415+
- compiler_version_explicit_out_of_list_fails
390416
- cross_compile
391417
- compilers
392418
- release_notes

builder/core/spec.py

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,51 @@
77
from builder.core.host import current_host, current_os, current_arch, normalize_arch
88

99

10-
def validate_spec(build_spec):
10+
def _parse_version(version_str):
11+
"""Parse a version string into a list of integers for comparison.
12+
The version string can be in format X, X.Y, X.Y.Z, etc."""
13+
try:
14+
return [int(x) for x in version_str.split('.')]
15+
except (ValueError, AttributeError):
16+
return None
17+
18+
19+
def _compiler_meets_minimum_version(compiler_version, compiler):
20+
"""Check if the compiler version meets the minimum supported version requirement.
21+
Returns True if compiler_version >= minimum known version, False otherwise."""
22+
# Sort compiler version keys and find the lowest (minimal) version
23+
versions = [v for v in compiler['versions'].keys() if (v != 'default' and v != 'latest')]
24+
25+
# Filter out versions that cannot be parsed
26+
versions = [_parse_version(v) for v in versions if _parse_version(v) is not None]
27+
28+
# Versions are not specified or none are parseable, return True
29+
if not versions:
30+
return True
31+
32+
# Sort and find the minimum version
33+
versions.sort()
34+
minimal_version = versions[0]
35+
36+
# Parse compiler_version
37+
parsed_compiler = _parse_version(compiler_version)
38+
if parsed_compiler is None:
39+
return False
40+
41+
# Compare using Python's native list comparison
42+
return parsed_compiler >= minimal_version
43+
44+
45+
def validate_spec(build_spec, allow_higher_version=False):
46+
"""Validate the build spec against known hosts, targets, architectures, and compilers.
47+
48+
Args:
49+
allow_higher_version: If True, skip validation for compiler versions higher than
50+
what's in the known versions list (e.g. when a newer compiler is detected on
51+
the system), but still assert that the version is not lower than the minimum
52+
supported version. If False, the compiler version must exactly match one of
53+
the known versions.
54+
"""
1155

1256
assert build_spec.host in HOSTS, "Host name {} is invalid".format(
1357
build_spec.host)
@@ -21,8 +65,15 @@ def validate_spec(build_spec):
2165
build_spec.compiler)
2266
compiler = COMPILERS[build_spec.compiler]
2367

24-
assert build_spec.compiler_version in compiler['versions'], "Compiler version {} is invalid for compiler {}".format(
25-
build_spec.compiler_version, build_spec.compiler)
68+
if not allow_higher_version:
69+
assert build_spec.compiler_version in compiler['versions'], "Compiler version {} is invalid for compiler {}".format(
70+
build_spec.compiler_version, build_spec.compiler)
71+
else:
72+
assert _compiler_meets_minimum_version(
73+
build_spec.compiler_version, compiler), \
74+
"Compiler version {} is lower than the minimum supported " \
75+
"version for compiler {}".format(
76+
build_spec.compiler_version, build_spec.compiler)
2677

2778
supported_hosts = compiler['hosts']
2879
assert build_spec.host in supported_hosts or current_os() in supported_hosts, "Compiler {} does not support host {}".format(
@@ -81,6 +132,8 @@ def __init__(self, **kwargs):
81132
if self.downstream:
82133
self.name += "-downstream"
83134

135+
# Strict validation at construction time: compiler version must exactly match
136+
# a known version in the data dictionary.
84137
validate_spec(self)
85138

86139
def __str__(self):
@@ -90,6 +143,13 @@ def __repr__(self):
90143
return self.name
91144

92145
def update_compiler(self, compiler, compiler_version):
146+
"""Update the spec's compiler and version after resolving the system toolchain.
147+
148+
This is called after the Toolchain detects the actual compiler installed on the
149+
system, and validates the detected compiler version.
150+
The function allows the spec to be set to a higher version than what's in the
151+
known versions list, but still rejects versions below the minimum supported.
152+
"""
93153
self.compiler = compiler
94154
self.compiler_version = compiler_version
95155

@@ -98,4 +158,7 @@ def update_compiler(self, compiler, compiler_version):
98158
if self.downstream:
99159
self.name += "-downstream"
100160

101-
validate_spec(self)
161+
# Validate with allow_higher_version=True to allow system-detected compiler
162+
# newer than what's in our known versions list. We still reject versions
163+
# below the minimum supported version.
164+
validate_spec(self, allow_higher_version=True)

builder/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ def build_consumers(env):
9797
def default_spec(env):
9898
target, arch = current_platform().split('-')
9999
host = current_host()
100-
compiler, version = Toolchain.default_compiler(target, arch)
101-
return BuildSpec(host=host, compiler=compiler, compiler_version='{}'.format(version), target=target, arch=arch)
100+
return BuildSpec(host=host, target=target, arch=arch)
102101

103102

104103
def inspect_host(spec):
@@ -295,6 +294,7 @@ def main():
295294
sys.exit(1)
296295

297296
if env.config.get('needs_compiler', True):
297+
# Resolve the actual compiler from the system toolchain and update the spec.
298298
env.toolchain = Toolchain(spec=env.spec)
299299
env.spec.update_compiler(env.toolchain.compiler, env.toolchain.compiler_version)
300300
if env.spec.compiler == 'default' or env.spec.compiler_version == 'default':

0 commit comments

Comments
 (0)