@@ -302,8 +302,28 @@ def extra(self) -> str:
302
302
return "," .join (flags )
303
303
304
304
305
+ def generate_cargo_build_command (
306
+ bins : List [str ],
307
+ examples : List [str ],
308
+ rd : RepositoryDetails ,
309
+ ) -> List [str ]:
310
+ rustflags = rustc_flags .coverage if rd .coverage else []
311
+
312
+ cargo_build = [* rd .cargo ("build" , channel = None , rustflags = rustflags )]
313
+
314
+ for bin in bins :
315
+ cargo_build .extend (["--bin" , bin ])
316
+ for example in examples :
317
+ cargo_build .extend (["--example" , example ])
318
+
319
+ if rd .release_mode :
320
+ cargo_build .append ("--release" )
321
+
322
+ return cargo_build
323
+
324
+
305
325
class CargoBuild (CargoPreImage ):
306
- """A pre-image action that builds a single binary with Cargo."""
326
+ """A pre-image action that builds individual binaries with Cargo."""
307
327
308
328
def __init__ (self , rd : RepositoryDetails , path : Path , config : Dict [str , Any ]):
309
329
super ().__init__ (rd , path )
@@ -314,26 +334,25 @@ def __init__(self, rd: RepositoryDetails, path: Path, config: Dict[str, Any]):
314
334
self .strip = config .pop ("strip" , True )
315
335
self .split_debuginfo = config .pop ("split_debuginfo" , False )
316
336
self .extract = config .pop ("extract" , {})
317
- self .rustflags = config .pop ("rustflags" , [])
318
- self .channel = None
319
- if rd .coverage :
320
- self .rustflags += rustc_flags .coverage
321
337
if len (self .bins ) == 0 and len (self .examples ) == 0 :
322
338
raise ValueError ("mzbuild config is missing pre-build target" )
323
339
324
- def build (self ) -> None :
325
- cargo_build = [
326
- * self .rd .cargo ("build" , channel = self .channel , rustflags = self .rustflags )
327
- ]
340
+ @staticmethod
341
+ def run_common_build (
342
+ cargo_builds : list ["CargoBuild" ], rd : RepositoryDetails
343
+ ) -> None :
344
+ bins = []
345
+ examples = []
346
+ for build in cargo_builds :
347
+ bins .extend (build .bins )
348
+ examples .extend (build .examples )
328
349
329
- for bin in self .bins :
330
- cargo_build .extend (["--bin" , bin ])
331
- for example in self .examples :
332
- cargo_build .extend (["--example" , example ])
350
+ ui .say (f"Common cargo-build for: { bins + examples } " )
333
351
334
- if self .rd .release_mode :
335
- cargo_build .append ("--release" )
336
- spawn .runv (cargo_build , cwd = self .rd .root )
352
+ cargo_build = generate_cargo_build_command (bins , examples , rd )
353
+ spawn .runv (cargo_build , cwd = rd .root )
354
+
355
+ def build (self ) -> None :
337
356
cargo_profile = "release" if self .rd .release_mode else "debug"
338
357
339
358
def copy (exe : Path ) -> None :
@@ -392,6 +411,10 @@ def copy(exe: Path) -> None:
392
411
copy (Path ("examples" ) / example )
393
412
394
413
if self .extract :
414
+ cargo_build = generate_cargo_build_command (
415
+ self .bins , self .examples , self .rd
416
+ )
417
+
395
418
output = spawn .capture (
396
419
cargo_build + ["--message-format=json" ],
397
420
cwd = self .rd .root ,
@@ -580,10 +603,9 @@ def write_dockerfile(self) -> IO[bytes]:
580
603
return f
581
604
582
605
def build (self ) -> None :
583
- """Build the image from source."""
584
- for dep in self .dependencies .values ():
585
- dep .acquire ()
606
+ """Build the image from source. Requires that the caller has already acquired all dependencies."""
586
607
spawn .runv (["git" , "clean" , "-ffdX" , self .image .path ])
608
+
587
609
for pre_image in self .image .pre_images :
588
610
pre_image .run ()
589
611
build_args = {
@@ -605,31 +627,26 @@ def build(self) -> None:
605
627
]
606
628
spawn .runv (cmd , stdin = f , stdout = sys .stderr .buffer )
607
629
608
- def acquire (self ) -> None :
609
- """Download or build the image if it does not exist locally."""
610
- if self .acquired :
611
- return
612
-
630
+ def try_pull (self ) -> bool :
631
+ """Download the image if it does not exist locally. Returns whether it was found."""
613
632
ui .header (f"Acquiring { self .spec ()} " )
614
- try :
615
- spawn .runv (
616
- ["docker" , "pull" , self .spec ()],
617
- stdout = sys .stderr .buffer ,
618
- )
619
- except subprocess .CalledProcessError :
620
- self .build ()
621
- self .acquired = True
622
-
623
- def ensure (self ) -> None :
624
- """Ensure the image exists on Docker Hub if it is publishable.
633
+ if not self .acquired :
634
+ try :
635
+ spawn .runv (
636
+ ["docker" , "pull" , self .spec ()],
637
+ stdout = sys .stderr .buffer ,
638
+ )
639
+ self .acquired = True
640
+ except subprocess .CalledProcessError :
641
+ self .acquired = False
642
+ return self .acquired
625
643
626
- The image is pushed using its spec as its tag.
627
- """
644
+ def is_published_if_necessary ( self ) -> bool :
645
+ """Ensure the image exists on Docker Hub if it is publishable. Returns whether it exists."""
628
646
if self .publish and is_docker_image_pushed (self .spec ()):
629
647
ui .say (f"{ self .spec ()} already exists" )
630
- return
631
- self .build ()
632
- spawn .runv (["docker" , "push" , self .spec ()])
648
+ return True
649
+ return False
633
650
634
651
def run (self , args : List [str ] = [], docker_args : List [str ] = []) -> None :
635
652
"""Run a command in the image.
@@ -756,21 +773,43 @@ def __init__(self, dependencies: Iterable[Image]):
756
773
image .acquired = image .spec () in known_images
757
774
self ._dependencies [d .name ] = image
758
775
776
+ def cargo_common_build (self , deps_to_build : List [ResolvedImage ]) -> None :
777
+ if not deps_to_build :
778
+ return
779
+
780
+ rd : Optional [RepositoryDetails ] = None
781
+ cargo_builds = []
782
+ for resolved_image in deps_to_build :
783
+ if not rd :
784
+ rd = resolved_image .image .rd
785
+ for pre_image in resolved_image .image .pre_images :
786
+ if isinstance (pre_image , CargoBuild ):
787
+ cargo_builds .append (pre_image )
788
+ assert rd
789
+
790
+ CargoBuild .run_common_build (cargo_builds , rd )
791
+
759
792
def acquire (self ) -> None :
760
793
"""Download or build all of the images in the dependency set that do not
761
794
already exist locally.
762
795
"""
763
- for dep in self :
764
- dep .acquire ()
796
+ deps_to_build = [dep for dep in self if not dep .try_pull ()]
797
+ self .cargo_common_build (deps_to_build )
798
+ for dep in deps_to_build :
799
+ dep .build ()
765
800
766
801
def ensure (self ) -> None :
767
802
"""Ensure all publishable images in this dependency set exist on Docker
768
803
Hub.
769
804
770
805
Images are pushed using their spec as their tag.
771
806
"""
772
- for dep in self :
773
- dep .ensure ()
807
+ deps_to_build = [dep for dep in self if not dep .is_published_if_necessary ()]
808
+ self .cargo_common_build (deps_to_build )
809
+ for dep in deps_to_build :
810
+ dep .build ()
811
+ if dep .publish :
812
+ spawn .runv (["docker" , "push" , dep .spec ()])
774
813
775
814
def __iter__ (self ) -> Iterator [ResolvedImage ]:
776
815
return iter (self ._dependencies .values ())
0 commit comments