Skip to content

Commit b9e8516

Browse files
Buildx builder context manager
1 parent 0fcb2e1 commit b9e8516

File tree

1 file changed

+68
-29
lines changed

1 file changed

+68
-29
lines changed

ci/ci_utils.py

Lines changed: 68 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import subprocess
2020
from abc import ABC, abstractmethod
2121
from enum import Enum
22-
from typing import List, Optional, Tuple
22+
from typing import List, Optional, Tuple, Any
2323
import re
2424

2525

@@ -49,6 +49,42 @@ class CudaSmArchitectures(Enum):
4949
MAX_CONSECUTIVE_IDENTICAL_LOG_LINES = 100
5050

5151

52+
class DockerBuildxBuilder:
53+
"""Context manager for starting and stopping a docker buildx instance.
54+
buildx instance is needed to limit the resources used by docker build."""
55+
56+
def __init__(self, name: str) -> None:
57+
self.builder_name = name
58+
self.builder_cpus = 6 #TODO: Make this adaptive to the number of cores available
59+
self.start_builder()
60+
61+
def __enter__(self) -> Any:
62+
"""Start the builder and return it."""
63+
self.start_builder()
64+
return self
65+
66+
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
67+
"""Stop the builder."""
68+
self.stop_builder()
69+
70+
def start_builder(self) -> None:
71+
"""Start the builder."""
72+
# Make sure it is stopped
73+
self.stop_builder(check=False)
74+
subprocess.run([
75+
'docker', 'buildx', 'create', f'--name={self.builder_name}',
76+
'--driver=docker-container', f'--driver-opt=cpu-shares={self.builder_cpus}', '--use'
77+
],
78+
check=True)
79+
80+
print(f'Started buildx builder: {self.builder_name}')
81+
82+
def stop_builder(self, check: bool = True) -> None:
83+
"""Stop the builder."""
84+
subprocess.run(['docker', 'buildx', 'rm', self.builder_name], check=check)
85+
print(f'Stopped buildx builder: {self.builder_name}')
86+
87+
5288
def _try_parse_gcc_output_line(line: str) -> Tuple[Optional[str], Optional[int]]:
5389
"""Try to extract file path and line number from gcc/clang/cmake-style output"""
5490
# Example: /path/to/file.cpp:LINE:
@@ -227,34 +263,37 @@ def build(self) -> None:
227263
print('=' * 80)
228264
print('', flush=True)
229265

230-
cmd = [
231-
'docker',
232-
'build',
233-
'-f',
234-
self.dockerfile_path(),
235-
'-t',
236-
image_name,
237-
'--network=host',
238-
'--progress=plain',
239-
]
240-
241-
if parent is not None:
242-
parent_name = parent.image_name()
243-
cmd += ['--build-arg', f'BASE_IMAGE={parent_name}']
244-
245-
if self.build_args() is not None:
246-
for arg in self.build_args():
247-
cmd += ['--build-arg', arg]
248-
249-
# Add extra docker args from args if provided
250-
if self.args.user_build_args is not None:
251-
cmd += self.args.user_build_args
252-
253-
cmd += ['.']
254-
255-
print(' '.join(cmd))
256-
257-
_run_and_parse_log(cmd)
266+
with DockerBuildxBuilder(name=f'{self.image_name()}_builder') as builder:
267+
cmd = [
268+
'docker',
269+
'buildx',
270+
'build',
271+
f'--builder={builder.builder_name}',
272+
'-f',
273+
self.dockerfile_path(),
274+
'-t',
275+
image_name,
276+
'--network=host',
277+
'--progress=plain',
278+
]
279+
280+
if parent is not None:
281+
parent_name = parent.image_name()
282+
cmd += ['--build-arg', f'BASE_IMAGE={parent_name}']
283+
284+
if self.build_args() is not None:
285+
for arg in self.build_args():
286+
cmd += ['--build-arg', arg]
287+
288+
# Add extra docker args from args if provided
289+
if self.args.user_build_args is not None:
290+
cmd += self.args.user_build_args
291+
292+
cmd += ['.']
293+
294+
print(' '.join(cmd))
295+
296+
_run_and_parse_log(cmd)
258297

259298
if self.do_validate_image():
260299
self._validate()

0 commit comments

Comments
 (0)