|
19 | 19 | import subprocess |
20 | 20 | from abc import ABC, abstractmethod |
21 | 21 | from enum import Enum |
22 | | -from typing import List, Optional, Tuple |
| 22 | +from typing import List, Optional, Tuple, Any |
23 | 23 | import re |
24 | 24 |
|
25 | 25 |
|
@@ -49,6 +49,42 @@ class CudaSmArchitectures(Enum): |
49 | 49 | MAX_CONSECUTIVE_IDENTICAL_LOG_LINES = 100 |
50 | 50 |
|
51 | 51 |
|
| 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 | + |
52 | 88 | def _try_parse_gcc_output_line(line: str) -> Tuple[Optional[str], Optional[int]]: |
53 | 89 | """Try to extract file path and line number from gcc/clang/cmake-style output""" |
54 | 90 | # Example: /path/to/file.cpp:LINE: |
@@ -227,34 +263,37 @@ def build(self) -> None: |
227 | 263 | print('=' * 80) |
228 | 264 | print('', flush=True) |
229 | 265 |
|
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) |
258 | 297 |
|
259 | 298 | if self.do_validate_image(): |
260 | 299 | self._validate() |
|
0 commit comments