@@ -28,6 +28,7 @@ use crate::{
2828    utils:: { format_image_manifest_uri,  format_image_repository_uri} , 
2929} ; 
3030
31+ pub  const  COMMON_TARGET_NAME :  & str  = "common--target" ; 
3132pub  const  ENTRY_TARGET_NAME_PREFIX :  & str  = "entry--" ; 
3233
3334#[ derive( Debug ,  Snafu ) ]  
@@ -232,6 +233,22 @@ impl Bakefile {
232233            . collect ( ) 
233234    } 
234235
236+     /// Creates the common target, containing shared data, which will be inherited by other targets. 
237+ fn  common_target ( args :  & cli:: BuildArguments ,  config :  Config )  -> Result < BakefileTarget ,  Error >  { 
238+         let  revision = Self :: git_head_revision ( ) . context ( GetRevisionSnafu ) ?; 
239+         let  date_time = Self :: now ( ) ?; 
240+ 
241+         let  target = BakefileTarget :: common ( 
242+             date_time, 
243+             revision, 
244+             config, 
245+             args. docker_build_arguments . clone ( ) , 
246+             args. image_version . base_prerelease ( ) , 
247+         ) ; 
248+ 
249+         Ok ( target) 
250+     } 
251+ 
235252    fn  from_targets ( 
236253        targets :  Targets , 
237254        args :  & cli:: BuildArguments , 
@@ -240,23 +257,15 @@ impl Bakefile {
240257        let  mut  bakefile_targets = BTreeMap :: new ( ) ; 
241258        let  mut  groups:  BTreeMap < String ,  BakefileGroup >  = BTreeMap :: new ( ) ; 
242259
243-         let  revision = Self :: git_head_revision ( ) . context ( GetRevisionSnafu ) ?; 
244-         let  date_time = Self :: now ( ) ?; 
245- 
246-         // TODO (@Techassi): Can we somehow optimize this to come by with minimal amount of 
247-         // cloning, because we also need to clone on every loop iteration below. 
248-         let  mut  docker_build_arguments = config. build_arguments ; 
249-         docker_build_arguments. extend ( args. docker_build_arguments . clone ( ) ) ; 
250-         docker_build_arguments. insert ( BuildArgument :: new ( 
251-             "RELEASE_VERSION" . to_owned ( ) , 
252-             args. image_version . base_prerelease ( ) , 
253-         ) ) ; 
260+         // Create a common target, which contains shared data, like annotations, arguments, labels, etc... 
261+         let  common_target = Self :: common_target ( args,  config) ?; 
262+         bakefile_targets. insert ( COMMON_TARGET_NAME . to_owned ( ) ,  common_target) ; 
254263
255-         for  ( image_name,  image_versions)  in  targets. 0 . into_iter ( )  { 
264+         for  ( image_name,  image_versions)  in  targets. into_iter ( )  { 
256265            for  ( image_version,  ( image_options,  is_entry) )  in  image_versions { 
257266                // TODO (@Techassi): Clean this up 
258267                // TODO (@Techassi): Move the arg formatting into functions 
259-                 let  mut  docker_build_arguments  = docker_build_arguments . clone ( ) ; 
268+                 let  mut  build_arguments  = BuildArguments :: new ( ) ; 
260269
261270                let  local_version_docker_args:  Vec < _ >  = image_options
262271                    . local_images 
@@ -272,9 +281,10 @@ impl Bakefile {
272281                    } ) 
273282                    . collect ( ) ; 
274283
275-                 docker_build_arguments. extend ( image_options. build_arguments . clone ( ) ) ; 
276-                 docker_build_arguments. extend ( local_version_docker_args) ; 
277-                 docker_build_arguments. insert ( BuildArgument :: new ( 
284+                 build_arguments. extend ( image_options. build_arguments ) ; 
285+                 build_arguments. extend ( local_version_docker_args) ; 
286+                 // TODO (@Techassi): Rename this to IMAGE_VERSION 
287+                 build_arguments. insert ( BuildArgument :: new ( 
278288                    "PRODUCT_VERSION" . to_owned ( ) , 
279289                    image_version. to_string ( ) , 
280290                ) ) ; 
@@ -318,24 +328,20 @@ impl Bakefile {
318328                    } ) 
319329                    . collect ( ) ; 
320330
321-                 let  annotations = BakefileTarget :: annotations ( 
322-                     & date_time, 
323-                     & revision, 
324-                     & image_version, 
325-                     & args. image_version , 
326-                     & config. metadata , 
327-                 ) ; 
328-                 let  labels = BakefileTarget :: labels ( date_time. clone ( ) ,  revision. clone ( ) ) ; 
331+                 let  annotations =
332+                     BakefileTarget :: image_version_annotation ( & image_version,  & args. image_version ) ; 
329333
330334                let  target = BakefileTarget  { 
331335                    tags :  vec ! [ image_manifest_uri] , 
332-                     arguments :  docker_build_arguments , 
336+                     arguments :  build_arguments , 
333337                    platforms :  vec ! [ args. target_platform. clone( ) ] , 
334-                     context :  PathBuf :: from ( "." ) , 
338+                     // NOTE (@Techassi): Should this instead be scoped to the folder of the image we build 
339+                     context :  Some ( PathBuf :: from ( "." ) ) , 
340+                     dockerfile :  Some ( dockerfile) , 
341+                     inherits :  vec ! [ COMMON_TARGET_NAME . to_owned( ) ] , 
335342                    annotations, 
336-                     dockerfile, 
337343                    contexts, 
338-                     labels , 
344+                     .. Default :: default ( ) 
339345                } ; 
340346
341347                bakefile_targets. insert ( target_name,  target) ; 
@@ -404,63 +410,112 @@ impl Bakefile {
404410
405411// TODO (@Techassi): Figure out of we can use borrowed data in here. This would avoid a whole bunch 
406412// of cloning. 
407- #[ derive( Debug ,  Serialize ) ]  
413+ #[ derive( Debug ,  Default ,   Serialize ) ]  
408414pub  struct  BakefileTarget  { 
415+     /// Defines build arguments for the target. 
416+ #[ serde( rename = "args" ,  skip_serializing_if = "BuildArguments::is_empty" ) ]  
417+     pub  arguments :  BuildArguments , 
418+ 
419+     /// Adds annotations to images built with bake. 
420+ #[ serde( skip_serializing_if = "Vec::is_empty" ) ]  
409421    pub  annotations :  Vec < String > , 
410-     pub  context :  PathBuf , 
411422
423+     /// Specifies the location of the build context to use for this target. 
424+ /// 
425+ /// Accepts a URL or a directory path. 
426+ #[ serde( skip_serializing_if = "Option::is_none" ) ]  
427+     pub  context :  Option < PathBuf > , 
428+ 
429+     /// Additional build contexts. 
430+ /// 
431+ /// This attribute takes a map, where keys result in named contexts that you can reference in 
432+ /// your builds. 
412433#[ serde( skip_serializing_if = "BTreeMap::is_empty" ) ]  
413434    pub  contexts :  BTreeMap < String ,  String > , 
414-     pub  dockerfile :  PathBuf , 
415435
416-     #[ serde( rename = "args" ,  skip_serializing_if = "BuildArguments::is_empty" ) ]  
417-     pub  arguments :  BuildArguments , 
436+     /// Name of the Dockerfile to use for the build. 
437+ #[ serde( skip_serializing_if = "Option::is_none" ) ]  
438+     pub  dockerfile :  Option < PathBuf > , 
439+ 
440+     /// A target can inherit attributes from other targets. 
441+ #[ serde( skip_serializing_if = "Vec::is_empty" ) ]  
442+     pub  inherits :  Vec < String > , 
418443
444+     /// Assigns image labels to the build. 
445+ #[ serde( skip_serializing_if = "BTreeMap::is_empty" ) ]  
419446    pub  labels :  BTreeMap < String ,  String > , 
420-     pub  tags :  Vec < String > , 
447+ 
448+     // TODO (@Techassi): Explore how we can build multiple platforms at once 
449+     /// Set target platforms for the build target. 
450+ /// 
451+ /// Technically, multiple architectures can be listed in here, but boil chooses to build only 
452+ /// one architecture at a time. 
453+ #[ serde( skip_serializing_if = "Vec::is_empty" ) ]  
421454    pub  platforms :  Vec < TargetPlatform > , 
455+ 
456+     /// Image names and tags to use for the build target. 
457+ #[ serde( skip_serializing_if = "Vec::is_empty" ) ]  
458+     pub  tags :  Vec < String > , 
422459} 
423460
424461impl  BakefileTarget  { 
425-     fn  annotations ( 
426-         date_time :  & str , 
427-         revision :  & str , 
428-         image_version :   & str , 
429-         sdp_image_version :   & Version , 
430-         global_metadata :   & config :: Metadata , 
431-     )  -> Vec < String >  { 
462+     fn  common ( 
463+         date_time :  String , 
464+         revision :  String , 
465+         config :   Config , 
466+         docker_build_arguments :   Vec < BuildArgument > , 
467+         release_version :   String , 
468+     )  -> Self  { 
432469        let  config:: Metadata  { 
433470            documentation, 
434471            licenses, 
435472            authors, 
436473            source, 
437474            vendor, 
438-         }  = global_metadata ; 
475+         }  = config . metadata ; 
439476
440477        // Annotations describe OCI image components. 
441-         vec ! [ 
478+         let  annotations =  vec ! [ 
442479            format!( "{ANNOTATION_CREATED}={date_time}" ) , 
443480            format!( "{ANNOTATION_AUTHORS}={authors}" ) , 
444481            format!( "{ANNOTATION_DOCUMENTATION}={documentation}" ) , 
445482            format!( "{ANNOTATION_SOURCE}={source}" ) , 
446-             // TODO (@Techassi): Move this version formatting into a function 
447-             // TODO (@Techassi): Make this vendor agnostic, don't hard-code stackable here 
448-             format!( "{ANNOTATION_VERSION}={image_version}-stackable{sdp_image_version}" ) , 
449483            format!( "{ANNOTATION_REVISION}={revision}" ) , 
450484            format!( "{ANNOTATION_VENDOR}={vendor}" ) , 
451485            format!( "{ANNOTATION_LICENSES}={licenses}" ) , 
452-         ] 
453-     } 
486+         ] ; 
487+ 
488+         let  mut  arguments = config. build_arguments ; 
489+         arguments. extend ( docker_build_arguments) ; 
490+         arguments. insert ( BuildArgument :: new ( 
491+             "RELEASE_VERSION" . to_owned ( ) , 
492+             release_version, 
493+         ) ) ; 
454494
455-     fn  labels ( date_time :  String ,  revision :  String )  -> BTreeMap < String ,  String >  { 
456495        // Labels describe Docker resources, and con be considered legacy. We 
457496        // should use annotations instead. These labels are only added to be 
458497        // consistent with `bake`. 
459-         BTreeMap :: from ( [ 
498+         let  labels =  BTreeMap :: from ( [ 
460499            ( ANNOTATION_CREATED . to_owned ( ) ,  date_time. clone ( ) ) , 
461500            ( ANNOTATION_REVISION . to_owned ( ) ,  revision) , 
462501            ( "build-date" . to_owned ( ) ,  date_time) , 
463-         ] ) 
502+         ] ) ; 
503+ 
504+         Self  { 
505+             annotations, 
506+             arguments, 
507+             labels, 
508+             ..Default :: default ( ) 
509+         } 
510+     } 
511+ 
512+     fn  image_version_annotation ( image_version :  & str ,  sdp_image_version :  & Version )  -> Vec < String >  { 
513+         // Annotations describe OCI image components. 
514+         vec ! [ 
515+             // TODO (@Techassi): Move this version formatting into a function 
516+             // TODO (@Techassi): Make this vendor agnostic, don't hard-code stackable here 
517+             format!( "{ANNOTATION_VERSION}={image_version}-stackable{sdp_image_version}" ) , 
518+         ] 
464519    } 
465520} 
466521
0 commit comments