@@ -25,7 +25,6 @@ import (
2525 "github.com/docker/buildx/controller/pb"
2626 "github.com/docker/buildx/localstate"
2727 "github.com/docker/buildx/util/buildflags"
28- "github.com/docker/buildx/util/cobrautil"
2928 "github.com/docker/buildx/util/cobrautil/completion"
3029 "github.com/docker/buildx/util/confutil"
3130 "github.com/docker/buildx/util/desktop"
@@ -38,24 +37,30 @@ import (
3837 "github.com/moby/buildkit/util/progress/progressui"
3938 "github.com/pkg/errors"
4039 "github.com/spf13/cobra"
40+ "github.com/tonistiigi/go-csvvalue"
4141 "go.opentelemetry.io/otel/attribute"
4242)
4343
4444type bakeOptions struct {
45- files []string
46- overrides []string
47- printOnly bool
48- listTargets bool
49- listVars bool
50- sbom string
51- provenance string
52- allow []string
45+ files []string
46+ overrides []string
47+
48+ sbom string
49+ provenance string
50+ allow []string
5351
5452 builder string
5553 metadataFile string
5654 exportPush bool
5755 exportLoad bool
5856 callFunc string
57+
58+ print bool
59+ list string
60+
61+ // TODO: remove deprecated flags
62+ listTargets bool
63+ listVars bool
5964}
6065
6166func runBake (ctx context.Context , dockerCli command.Cli , targets []string , in bakeOptions , cFlags commonFlags ) (err error ) {
@@ -121,9 +126,13 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
121126 var nodes []builder.Node
122127 var progressConsoleDesc , progressTextDesc string
123128
129+ if in .print && in .list != "" {
130+ return errors .New ("--print and --list are mutually exclusive" )
131+ }
132+
124133 // instance only needed for reading remote bake files or building
125134 var driverType string
126- if url != "" || ! (in .printOnly || in .listTargets || in . listVars ) {
135+ if url != "" || ! (in .print || in .list != "" ) {
127136 b , err := builder .New (dockerCli ,
128137 builder .WithName (in .builder ),
129138 builder .WithContextPathHash (contextPathHash ),
@@ -184,18 +193,23 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
184193 "BAKE_LOCAL_PLATFORM" : platforms .Format (platforms .DefaultSpec ()),
185194 }
186195
187- if in .listTargets || in . listVars {
196+ if in .list != "" {
188197 cfg , pm , err := bake .ParseFiles (files , defaults )
189198 if err != nil {
190199 return err
191200 }
192201 if err = printer .Wait (); err != nil {
193202 return err
194203 }
195- if in .listTargets {
196- return printTargetList (dockerCli .Out (), cfg )
197- } else if in .listVars {
198- return printVars (dockerCli .Out (), pm .AllVariables )
204+ list , err := parseList (in .list )
205+ if err != nil {
206+ return err
207+ }
208+ switch list .Type {
209+ case "targets" :
210+ return printTargetList (dockerCli .Out (), list .Format , cfg )
211+ case "variables" :
212+ return printVars (dockerCli .Out (), list .Format , pm .AllVariables )
199213 }
200214 }
201215
@@ -231,7 +245,7 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba
231245 Target : tgts ,
232246 }
233247
234- if in .printOnly {
248+ if in .print {
235249 if err = printer .Wait (); err != nil {
236250 return err
237251 }
@@ -427,6 +441,13 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
427441 if ! cmd .Flags ().Lookup ("pull" ).Changed {
428442 cFlags .pull = nil
429443 }
444+ if options .list == "" {
445+ if options .listTargets {
446+ options .list = "targets"
447+ } else if options .listVars {
448+ options .list = "variables"
449+ }
450+ }
430451 options .builder = rootOpts .builder
431452 options .metadataFile = cFlags .metadataFile
432453 // Other common flags (noCache, pull and progress) are processed in runBake function.
@@ -439,7 +460,6 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
439460
440461 flags .StringArrayVarP (& options .files , "file" , "f" , []string {}, "Build definition file" )
441462 flags .BoolVar (& options .exportLoad , "load" , false , `Shorthand for "--set=*.output=type=docker"` )
442- flags .BoolVar (& options .printOnly , "print" , false , "Print the options without building" )
443463 flags .BoolVar (& options .exportPush , "push" , false , `Shorthand for "--set=*.output=type=registry"` )
444464 flags .StringVar (& options .sbom , "sbom" , "" , `Shorthand for "--set=*.attest=type=sbom"` )
445465 flags .StringVar (& options .provenance , "provenance" , "" , `Shorthand for "--set=*.attest=type=provenance"` )
@@ -450,13 +470,16 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command {
450470 flags .VarPF (callAlias (& options .callFunc , "check" ), "check" , "" , `Shorthand for "--call=check"` )
451471 flags .Lookup ("check" ).NoOptDefVal = "true"
452472
473+ flags .BoolVar (& options .print , "print" , false , "Print the options without building" )
474+ flags .StringVar (& options .list , "list" , "" , "List targets or variables" )
475+
476+ // TODO: remove deprecated flags
453477 flags .BoolVar (& options .listTargets , "list-targets" , false , "List available targets" )
454- cobrautil .MarkFlagsExperimental (flags , "list-targets" )
455478 flags .MarkHidden ("list-targets" )
456-
479+ flags . MarkDeprecated ( "list-targets" , "list-targets is deprecated, use list=targets instead" )
457480 flags .BoolVar (& options .listVars , "list-variables" , false , "List defined variables" )
458- cobrautil .MarkFlagsExperimental (flags , "list-variables" )
459481 flags .MarkHidden ("list-variables" )
482+ flags .MarkDeprecated ("list-variables" , "list-variables is deprecated, use list=variables instead" )
460483
461484 commonBuildFlags (& cFlags , flags )
462485
@@ -557,10 +580,70 @@ func readBakeFiles(ctx context.Context, nodes []builder.Node, url string, names
557580 return
558581}
559582
560- func printVars (w io.Writer , vars []* hclparser.Variable ) error {
583+ type listEntry struct {
584+ Type string
585+ Format string
586+ }
587+
588+ func parseList (input string ) (listEntry , error ) {
589+ res := listEntry {}
590+
591+ fields , err := csvvalue .Fields (input , nil )
592+ if err != nil {
593+ return res , err
594+ }
595+
596+ if len (fields ) == 1 && fields [0 ] == input && ! strings .HasPrefix (input , "type=" ) {
597+ res .Type = input
598+ }
599+
600+ if res .Type == "" {
601+ for _ , field := range fields {
602+ key , value , ok := strings .Cut (field , "=" )
603+ if ! ok {
604+ return res , errors .Errorf ("invalid value %s" , field )
605+ }
606+ key = strings .TrimSpace (strings .ToLower (key ))
607+ switch key {
608+ case "type" :
609+ res .Type = value
610+ case "format" :
611+ res .Format = value
612+ default :
613+ return res , errors .Errorf ("unexpected key '%s' in '%s'" , key , field )
614+ }
615+ }
616+ }
617+ if res .Format == "" {
618+ res .Format = "table"
619+ }
620+
621+ switch res .Type {
622+ case "targets" , "variables" :
623+ default :
624+ return res , errors .Errorf ("invalid list type %q" , res .Type )
625+ }
626+
627+ switch res .Format {
628+ case "table" , "json" :
629+ default :
630+ return res , errors .Errorf ("invalid list format %q" , res .Format )
631+ }
632+
633+ return res , nil
634+ }
635+
636+ func printVars (w io.Writer , format string , vars []* hclparser.Variable ) error {
561637 slices .SortFunc (vars , func (a , b * hclparser.Variable ) int {
562638 return cmp .Compare (a .Name , b .Name )
563639 })
640+
641+ if format == "json" {
642+ enc := json .NewEncoder (w )
643+ enc .SetIndent ("" , " " )
644+ return enc .Encode (vars )
645+ }
646+
564647 tw := tabwriter .NewWriter (w , 1 , 8 , 1 , '\t' , 0 )
565648 defer tw .Flush ()
566649
@@ -578,12 +661,7 @@ func printVars(w io.Writer, vars []*hclparser.Variable) error {
578661 return nil
579662}
580663
581- func printTargetList (w io.Writer , cfg * bake.Config ) error {
582- tw := tabwriter .NewWriter (w , 1 , 8 , 1 , '\t' , 0 )
583- defer tw .Flush ()
584-
585- tw .Write ([]byte ("TARGET\t DESCRIPTION\n " ))
586-
664+ func printTargetList (w io.Writer , format string , cfg * bake.Config ) error {
587665 type targetOrGroup struct {
588666 name string
589667 target * bake.Target
@@ -602,6 +680,20 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
602680 return cmp .Compare (a .name , b .name )
603681 })
604682
683+ var tw * tabwriter.Writer
684+ if format == "table" {
685+ tw = tabwriter .NewWriter (w , 1 , 8 , 1 , '\t' , 0 )
686+ defer tw .Flush ()
687+ tw .Write ([]byte ("TARGET\t DESCRIPTION\n " ))
688+ }
689+
690+ type targetList struct {
691+ Name string `json:"name"`
692+ Description string `json:"description,omitempty"`
693+ Group bool `json:"group,omitempty"`
694+ }
695+ var targetsList []targetList
696+
605697 for _ , tgt := range list {
606698 if strings .HasPrefix (tgt .name , "_" ) {
607699 // convention for a private target
@@ -610,9 +702,9 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
610702 var descr string
611703 if tgt .target != nil {
612704 descr = tgt .target .Description
705+ targetsList = append (targetsList , targetList {Name : tgt .name , Description : descr })
613706 } else if tgt .group != nil {
614707 descr = tgt .group .Description
615-
616708 if len (tgt .group .Targets ) > 0 {
617709 slices .Sort (tgt .group .Targets )
618710 names := strings .Join (tgt .group .Targets , ", " )
@@ -622,8 +714,17 @@ func printTargetList(w io.Writer, cfg *bake.Config) error {
622714 descr = names
623715 }
624716 }
717+ targetsList = append (targetsList , targetList {Name : tgt .name , Description : descr , Group : true })
625718 }
626- fmt .Fprintf (tw , "%s\t %s\n " , tgt .name , descr )
719+ if format == "table" {
720+ fmt .Fprintf (tw , "%s\t %s\n " , tgt .name , descr )
721+ }
722+ }
723+
724+ if format == "json" {
725+ enc := json .NewEncoder (w )
726+ enc .SetIndent ("" , " " )
727+ return enc .Encode (targetsList )
627728 }
628729
629730 return nil
0 commit comments