Skip to content

Commit 9fc8c7f

Browse files
[CDRIVER-4644] [CDRIVER-4602] Additional Earthly platforms, functionality, and cleanup (#1537)
* Remove ArchLinux matrix definitions that used ArchLinux distros * Refactor of Earthly task generation, and parametrize on the compiler * Missing alpine3.19 env * Additional util scripts for container building * Significant Earthly refactoring: - Reusable build utils in tools/build.earth - New ways of installing individual components - Cleanup of earthly.py config generation - New environment targets for Alpine 3.16, 3.17, and Ubuntu 20.04 * Drop archlinux distros from distros.py * Aside: Remove the early-termination config * Bump Earthly version * Add Ubuntu 18.04 targets
1 parent d8df329 commit 9fc8c7f

File tree

18 files changed

+869
-362
lines changed

18 files changed

+869
-362
lines changed

.evergreen/config.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ pre_error_fails_task: true
2020
# teardown_group commands of appropriate task groups.
2121
post_error_fails_task: false
2222

23-
# Unlikely, but document loudly when task failure is due to early termination.
24-
early_termination:
25-
- func: early-termination
26-
2723
# TODO: move into timeout commands of appropriate task groups.
2824
timeout:
2925
- func: backtrace

.evergreen/config_generator/components/c_std_compile.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
# pylint: disable=line-too-long
1717
# fmt: off
1818
MATRIX = [
19-
('archlinux', 'clang', None, [11, ]),
2019
('debian92', 'clang', None, [11, ]),
2120
('ubuntu1604', 'clang', 'i686', [11, ]),
2221
('ubuntu1604', 'clang', None, [11, ]),

.evergreen/config_generator/components/early_termination.py

Lines changed: 0 additions & 22 deletions
This file was deleted.
Lines changed: 123 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,118 @@
11
from __future__ import annotations
22

33
import functools
4-
import itertools
5-
from typing import Any, Iterable, Literal, TypeVar, get_args, NamedTuple, get_type_hints
4+
import re
5+
from typing import Iterable, Literal, Mapping, NamedTuple, TypeVar
6+
67
from shrub.v3.evg_build_variant import BuildVariant
8+
from shrub.v3.evg_command import EvgCommandType, subprocess_exec
79
from shrub.v3.evg_task import EvgTaskRef
8-
from ..etc.utils import Task
9-
from shrub.v3.evg_command import subprocess_exec, EvgCommandType
10+
11+
from ..etc.utils import Task, all_possible
1012

1113
T = TypeVar("T")
1214

1315
_ENV_PARAM_NAME = "MONGOC_EARTHLY_ENV"
14-
"The name of the EVG expansion parameter used to key the Earthly build env"
15-
16-
EnvKey = Literal["u22", "alpine3.18", "archlinux"]
17-
"Identifiers for environments. These correspond to special '*-env' targets in the Earthfile."
18-
19-
_ENV_NAMES: dict[EnvKey, str] = {
20-
"u22": "Ubuntu 22.04",
21-
"alpine3.18": "Alpine 3.18",
22-
"archlinux": "Arch Linux",
23-
}
24-
"A mapping from environment keys to 'pretty' environment names"
16+
_CC_PARAM_NAME = "MONGOC_EARTHLY_C_COMPILER"
17+
"The name of the EVG expansion for the Earthly c_compiler argument"
18+
19+
20+
EnvKey = Literal[
21+
"u18",
22+
"u20",
23+
"u22",
24+
"alpine3.16",
25+
"alpine3.17",
26+
"alpine3.18",
27+
"alpine3.19",
28+
"archlinux",
29+
]
30+
"Identifiers for environments. These correspond to special 'env.*' targets in the Earthfile."
31+
CompilerName = Literal["gcc", "clang"]
32+
"The name of the compiler program that is used for the build. Passed via --c_compiler to Earthly."
2533

2634
# Other options: SSPI (Windows only), AUTO (not reliably test-able without more environments)
2735
SASLOption = Literal["Cyrus", "off"]
2836
"Valid options for the SASL configuration parameter"
2937
TLSOption = Literal["LibreSSL", "OpenSSL", "off"]
3038
"Options for the TLS backend configuration parameter (AKA 'ENABLE_SSL')"
31-
CxxVersion = Literal["master", "r3.8.0"]
39+
CxxVersion = Literal["r3.8.0", "r3.9.0"]
3240
"C++ driver refs that are under CI test"
3341

34-
# A Unicode non-breaking space character
35-
_BULLET = "\N{Bullet}"
42+
# A separator character, since we cannot use whitespace
43+
_SEPARATOR = "\N{no-break space}\N{bullet}\N{no-break space}"
44+
45+
46+
def os_split(env: EnvKey) -> tuple[str, None | str]:
47+
"""Convert the environment key into a pretty name+version pair"""
48+
match env:
49+
# match 'alpine3.18' 'alpine53.123' etc.
50+
case alp if mat := re.match(r"alpine(\d+\.\d+)", alp):
51+
return ("Alpine", mat[1])
52+
case "archlinux":
53+
return "ArchLinux", None
54+
# Match 'u22', 'u20', 'u71' etc.
55+
case ubu if mat := re.match(r"u(\d\d)", ubu):
56+
return "Ubuntu", f"{mat[1]}.04"
57+
case _:
58+
raise ValueError(
59+
f"Failed to split OS env key {env=} into a name+version pair (unrecognized)"
60+
)
61+
62+
63+
class EarthlyVariant(NamedTuple):
64+
"""
65+
Define a "variant" that runs under a set of Earthly parameters. These are
66+
turned into real EVG variants later on. The Earthly arguments are passed via
67+
expansion parameters.
68+
"""
69+
70+
env: EnvKey
71+
c_compiler: CompilerName
72+
73+
@property
74+
def display_name(self) -> str:
75+
"""The pretty name for this variant"""
76+
base: str
77+
match os_split(self.env):
78+
case name, None:
79+
base = name
80+
case name, version:
81+
base = f"{name} {version}"
82+
toolchain: str
83+
match self.c_compiler:
84+
case "clang":
85+
toolchain = "LLVM/Clang"
86+
case "gcc":
87+
toolchain = "GCC"
88+
return f"{base} ({toolchain})"
89+
90+
@property
91+
def task_selector_tag(self) -> str:
92+
"""
93+
The task tag that is used to select the tasks that want to run on this
94+
variant.
95+
"""
96+
return f"{self.env}-{self.c_compiler}"
97+
98+
@property
99+
def expansions(self) -> Mapping[str, str]:
100+
"""
101+
Expansion values that are defined for the build variant that is generated
102+
from this object.
103+
"""
104+
return {
105+
_CC_PARAM_NAME: self.c_compiler,
106+
_ENV_PARAM_NAME: self.env,
107+
}
108+
109+
def as_evg_variant(self) -> BuildVariant:
110+
return BuildVariant(
111+
name=f"{self.task_selector_tag}",
112+
tasks=[EvgTaskRef(name=f".{self.task_selector_tag}")],
113+
display_name=self.display_name,
114+
expansions=dict(self.expansions),
115+
)
36116

37117

38118
class Configuration(NamedTuple):
@@ -42,63 +122,44 @@ class Configuration(NamedTuple):
42122
43123
Adding/removing fields will add/remove dimensions on the task matrix.
44124
45-
The 'env' parameter is not encoded here, but is managed separately.
46-
47-
Keep this in sync with the 'PartialConfiguration' class defined below!
125+
Some Earthly parameters are not encoded here, but are rather part of the variant (EarthlyVariant).
48126
"""
49127

50128
sasl: SASLOption
51129
tls: TLSOption
52130
test_mongocxx_ref: CxxVersion
53131

54-
@classmethod
55-
def all(cls) -> Iterable[Configuration]:
56-
"""
57-
Generate all configurations for all options of our parameters.
58-
"""
59-
# Iter each configuration parameter:
60-
fields: Iterable[tuple[str, type]] = get_type_hints(Configuration).items()
61-
# Generate lists of pairs of parameter names their options:
62-
all_pairs: Iterable[Iterable[tuple[str, str]]] = (
63-
# Generate a (key, opt) pair for each option in parameter 'key'
64-
[(key, opt) for opt in get_args(typ)]
65-
# Over each parameter and type thereof:
66-
for key, typ in fields
67-
)
68-
# Now generate the cross product of all alternative for all options:
69-
matrix: Iterable[dict[str, Any]] = map(dict, itertools.product(*all_pairs))
70-
for items in matrix:
71-
# Convert each item to a Configuration:
72-
yield Configuration(**items)
73-
74132
@property
75133
def suffix(self) -> str:
76-
return f"{_BULLET}".join(f"{k}={v}" for k, v in self._asdict().items())
134+
return f"{_SEPARATOR}".join(f"{k}={v}" for k, v in self._asdict().items())
77135

78136

79-
def task_filter(env: EnvKey, conf: Configuration) -> bool:
137+
def task_filter(env: EarthlyVariant, conf: Configuration) -> bool:
80138
"""
81139
Control which tasks are actually defined by matching on the platform and
82140
configuration values.
83141
"""
84142
match env, conf:
85143
# We only need one task with "sasl=off"
86-
case "u22", ("off", "OpenSSL", "master"):
144+
case ["u22", "gcc"], ("off", "OpenSSL", "r3.8.0"):
87145
return True
146+
# The Ubuntu 18.04 GCC has a bug that fails to build the 3.8.0 C++ driver
147+
case ["u18", "gcc"], [_, _, "r3.8.0"]:
148+
return False
88149
# Other sasl=off tasks we'll just ignore:
89150
case _, ("off", _tls, _cxx):
90151
return False
91152
# Ubuntu does not ship with a LibreSSL package:
92-
case e, (_sasl, "LibreSSL", _cxx) if _ENV_NAMES[e].startswith("Ubuntu"):
153+
case e, (_sasl, "LibreSSL", _cxx) if e.display_name.startswith("Ubuntu"):
93154
return False
94155
# Anything else: Allow it to run:
95156
case _:
96157
return True
97158

98159

99-
def envs_for(config: Configuration) -> Iterable[EnvKey]:
100-
"""Get all environment keys that are not excluded for the given configuration"""
101-
all_envs: tuple[EnvKey, ...] = get_args(EnvKey)
160+
def variants_for(config: Configuration) -> Iterable[EarthlyVariant]:
161+
"""Get all Earthly variants that are not excluded for the given build configuration"""
162+
all_envs = all_possible(EarthlyVariant)
102163
allow_env_for_config = functools.partial(task_filter, conf=config)
103164
return filter(allow_env_for_config, all_envs)
104165

@@ -109,16 +170,26 @@ def earthly_task(
109170
targets: Iterable[str],
110171
config: Configuration,
111172
) -> Task | None:
112-
# Attach "earthly-xyz" tags to the task to allow build variants to select
173+
"""
174+
Create an EVG task which executes earthly using the given parameters. If this
175+
function returns `None`, then the task configuration is excluded from executing
176+
and no task should be defined.
177+
"""
178+
# Attach tags to the task to allow build variants to select
113179
# these tasks by the environment of that variant.
114-
env_tags = sorted(f"earthly-{e}" for e in sorted(envs_for(config)))
180+
env_tags = sorted(e.task_selector_tag for e in sorted(variants_for(config)))
115181
if not env_tags:
116182
# All environments have been excluded for this configuration. This means
117183
# the task itself should not be run:
118184
return
119185
# Generate the build-arg arguments based on the configuration options. The
120186
# NamedTuple field names must match with the ARG keys in the Earthfile!
121187
earthly_args = [f"--{key}={val}" for key, val in config._asdict().items()]
188+
# Add arguments that come from parameter expansions defined in the build variant
189+
earthly_args += [
190+
f"--env=${{{_ENV_PARAM_NAME}}}",
191+
f"--c_compiler=${{{_CC_PARAM_NAME}}}",
192+
]
122193
return Task(
123194
name=name,
124195
commands=[
@@ -131,7 +202,6 @@ def earthly_task(
131202
args=[
132203
"tools/earthly.sh",
133204
"+env-warmup",
134-
f"--env=${{{_ENV_PARAM_NAME}}}",
135205
*earthly_args,
136206
],
137207
working_dir="mongoc",
@@ -144,7 +214,6 @@ def earthly_task(
144214
"tools/earthly.sh",
145215
"+run",
146216
f"--targets={' '.join(targets)}",
147-
f"--env=${{{_ENV_PARAM_NAME}}}",
148217
*earthly_args,
149218
],
150219
working_dir="mongoc",
@@ -170,7 +239,7 @@ def earthly_task(
170239

171240

172241
def tasks() -> Iterable[Task]:
173-
for conf in Configuration.all():
242+
for conf in all_possible(Configuration):
174243
task = earthly_task(
175244
name=f"check:{conf.suffix}",
176245
targets=("test-example", "test-cxx-driver"),
@@ -181,15 +250,4 @@ def tasks() -> Iterable[Task]:
181250

182251

183252
def variants() -> list[BuildVariant]:
184-
envs: tuple[EnvKey, ...] = get_args(EnvKey)
185-
return [
186-
BuildVariant(
187-
name=f"earthly-{env}",
188-
tasks=[EvgTaskRef(name=f".earthly-{env}")],
189-
display_name=_ENV_NAMES[env],
190-
expansions={
191-
_ENV_PARAM_NAME: env,
192-
},
193-
)
194-
for env in envs
195-
]
253+
return [ev.as_evg_variant() for ev in all_possible(EarthlyVariant)]

.evergreen/config_generator/components/sasl/openssl.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
# pylint: disable=line-too-long
1616
# fmt: off
1717
COMPILE_MATRIX = [
18-
('archlinux', 'clang', None, ['cyrus']),
19-
('archlinux', 'gcc', None, ['cyrus']),
2018
('debian10', 'gcc', None, ['cyrus']),
2119
('debian11', 'gcc', None, ['cyrus']),
2220
('debian92', 'clang', None, ['cyrus']),

.evergreen/config_generator/etc/distros.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@ def validate_os_ver(cls, value):
4242
# See: https://evergreen.mongodb.com/distros
4343
# pylint: disable=line-too-long
4444
#fmt: off
45-
ARCHLINUX_DISTROS = [
46-
Distro(name='archlinux-large', os='archlinux', os_type='linux', size='large'),
47-
Distro(name='archlinux-small', os='archlinux', os_type='linux', size='small'),
48-
Distro(name='archlinux-new-large', os='archlinux', os_type='linux', size='large'),
49-
Distro(name='archlinux-new-small', os='archlinux', os_type='linux', size='small'),
50-
]
51-
5245
DEBIAN_DISTROS = [
5346
Distro(name='debian10-large', os='debian', os_type='linux', os_ver='10', size='large'),
5447
Distro(name='debian10-small', os='debian', os_type='linux', os_ver='10', size='small'),
@@ -155,7 +148,6 @@ def validate_os_ver(cls, value):
155148

156149
# Ensure no-arch distros are ordered before arch-specific distros.
157150
ALL_DISTROS = [] + \
158-
ARCHLINUX_DISTROS + \
159151
DEBIAN_DISTROS + \
160152
MACOS_DISTROS + \
161153
MACOS_ARM64_DISTROS + \

0 commit comments

Comments
 (0)