@@ -138,75 +138,68 @@ func filterAvailableNodes(nodes []builder.Node) ([]builder.Node, error) {
138138 return nil , err
139139}
140140
141- func toRepoOnly (in string ) (string , error ) {
142- m := map [string ]struct {}{}
143- p := strings .Split (in , "," )
144- for _ , pp := range p {
145- n , err := reference .ParseNormalizedNamed (pp )
146- if err != nil {
147- return "" , err
141+ // findNonMobyDriver returns the first non-moby based driver.
142+ func findNonMobyDriver (nodes []builder.Node ) * driver.DriverHandle {
143+ for _ , n := range nodes {
144+ if ! n .Driver .IsMobyDriver () {
145+ return n .Driver
148146 }
149- m [n .Name ()] = struct {}{}
150- }
151- out := make ([]string , 0 , len (m ))
152- for k := range m {
153- out = append (out , k )
154147 }
155- return strings .Join (out , "," ), nil
156- }
157-
158- func Build (ctx context.Context , nodes []builder.Node , opts map [string ]Options , docker * dockerutil.Client , cfg * confutil.Config , w progress.Writer ) (resp map [string ]* client.SolveResponse , err error ) {
159- return BuildWithResultHandler (ctx , nodes , opts , docker , cfg , w , nil )
148+ return nil
160149}
161150
162- func BuildWithResultHandler (ctx context.Context , nodes []builder.Node , opts map [string ]Options , docker * dockerutil.Client , cfg * confutil.Config , w progress.Writer , resultHandleFunc func (driverIndex int , rCtx * ResultHandle )) (resp map [string ]* client.SolveResponse , err error ) {
163- if len (nodes ) == 0 {
164- return nil , errors .Errorf ("driver required for build" )
151+ // warnOnNoOutput will check if the given nodes and options would result in an output
152+ // and prints a warning if it would not.
153+ func warnOnNoOutput (ctx context.Context , nodes []builder.Node , opts map [string ]Options ) {
154+ // Return immediately if default load is explicitly disabled or a call
155+ // function is used.
156+ if noDefaultLoad () || ! noCallFunc (opts ) {
157+ return
165158 }
166159
167- nodes , err = filterAvailableNodes (nodes )
168- if err != nil {
169- return nil , errors .Wrapf (err , "no valid drivers found" )
160+ // Find the first non-moby driver and return if it either doesn't exist
161+ // or if the driver has default load enabled.
162+ noMobyDriver := findNonMobyDriver (nodes )
163+ if noMobyDriver == nil || noMobyDriver .Features (ctx )[driver .DefaultLoad ] {
164+ return
170165 }
171166
172- var noMobyDriver * driver. DriverHandle
173- for _ , n := range nodes {
174- if ! n . Driver . IsMobyDriver () {
175- noMobyDriver = n . Driver
176- break
167+ // Produce a warning describing the targets affected.
168+ var noOutputTargets [] string
169+ for name , opt := range opts {
170+ if ! opt . Linked && len ( opt . Exports ) == 0 {
171+ noOutputTargets = append ( noOutputTargets , name )
177172 }
178173 }
179174
180- if noMobyDriver != nil && ! noDefaultLoad () && noCallFunc (opts ) {
181- var noOutputTargets []string
182- for name , opt := range opts {
183- if noMobyDriver .Features (ctx )[driver .DefaultLoad ] {
184- continue
185- }
186-
187- if ! opt .Linked && len (opt .Exports ) == 0 {
188- noOutputTargets = append (noOutputTargets , name )
189- }
190- }
191- if len (noOutputTargets ) > 0 {
192- var warnNoOutputBuf bytes.Buffer
193- warnNoOutputBuf .WriteString ("No output specified " )
194- if len (noOutputTargets ) == 1 && noOutputTargets [0 ] == "default" {
195- warnNoOutputBuf .WriteString (fmt .Sprintf ("with %s driver" , noMobyDriver .Factory ().Name ()))
196- } else {
197- warnNoOutputBuf .WriteString (fmt .Sprintf ("for %s target(s) with %s driver" , strings .Join (noOutputTargets , ", " ), noMobyDriver .Factory ().Name ()))
198- }
199- logrus .Warnf ("%s. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load" , warnNoOutputBuf .String ())
200- }
175+ if len (noOutputTargets ) == 0 {
176+ return
201177 }
202178
203- drivers , err := resolveDrivers (ctx , nodes , opts , w )
204- if err != nil {
205- return nil , err
179+ var warnNoOutputBuf bytes.Buffer
180+ warnNoOutputBuf .WriteString ("No output specified " )
181+ if len (noOutputTargets ) == 1 && noOutputTargets [0 ] == "default" {
182+ warnNoOutputBuf .WriteString (fmt .Sprintf ("with %s driver" , noMobyDriver .Factory ().Name ()))
183+ } else {
184+ warnNoOutputBuf .WriteString (fmt .Sprintf ("for %s target(s) with %s driver" , strings .Join (noOutputTargets , ", " ), noMobyDriver .Factory ().Name ()))
206185 }
186+ logrus .Warnf ("%s. Build result will only remain in the build cache. To push result image into registry use --push or to load image into docker use --load" , warnNoOutputBuf .String ())
187+ }
207188
189+ func newBuildRequests (ctx context.Context , docker * dockerutil.Client , cfg * confutil.Config , drivers map [string ][]* resolvedNode , w progress.Writer , opts map [string ]Options ) (_ map [string ][]* reqForNode , _ func (), retErr error ) {
208190 reqForNodes := make (map [string ][]* reqForNode )
209- eg , ctx := errgroup .WithContext (ctx )
191+
192+ var releasers []func ()
193+ releaseAll := func () {
194+ for _ , fn := range releasers {
195+ fn ()
196+ }
197+ }
198+ defer func () {
199+ if retErr != nil {
200+ releaseAll ()
201+ }
202+ }()
210203
211204 for k , opt := range opts {
212205 multiDriver := len (drivers [k ]) > 1
@@ -226,17 +219,17 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
226219 opt .Platforms = np .platforms
227220 gatewayOpts , err := np .BuildOpts (ctx )
228221 if err != nil {
229- return nil , err
222+ return nil , nil , err
230223 }
231224 localOpt := opt
232225 so , release , err := toSolveOpt (ctx , np .Node (), multiDriver , & localOpt , gatewayOpts , cfg , w , docker )
233226 opts [k ] = localOpt
234227 if err != nil {
235- return nil , err
228+ return nil , nil , err
236229 }
237- defer release ( )
230+ releasers = append ( releasers , release )
238231 if err := saveLocalState (so , k , opt , np .Node (), cfg ); err != nil {
239- return nil , err
232+ return nil , nil , err
240233 }
241234 addGitAttrs (so )
242235 reqn = append (reqn , & reqForNode {
@@ -261,15 +254,17 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
261254 for _ , e := range np .so .Exports {
262255 if e .Type == "moby" {
263256 if ok , _ := strconv .ParseBool (e .Attrs ["push" ]); ok {
264- return nil , errors .Errorf ("multi-node push can't currently be performed with the docker driver, please switch to a different driver" )
257+ return nil , nil , errors .Errorf ("multi-node push can't currently be performed with the docker driver, please switch to a different driver" )
265258 }
266259 }
267260 }
268261 }
269262 }
270263 }
264+ return reqForNodes , releaseAll , nil
265+ }
271266
272- // validate that all links between targets use same drivers
267+ func validateTargetLinks ( reqForNodes map [ string ][] * reqForNode , drivers map [ string ][] * resolvedNode , opts map [ string ] Options ) error {
273268 for name := range opts {
274269 dps := reqForNodes [name ]
275270 for i , dp := range dps {
@@ -279,8 +274,9 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
279274 k2 := strings .TrimPrefix (v , "target:" )
280275 dps2 , ok := drivers [k2 ]
281276 if ! ok {
282- return nil , errors .Errorf ("failed to find target %s for context %s" , k2 , strings .TrimPrefix (k , "context:" )) // should be validated before already
277+ return errors .Errorf ("failed to find target %s for context %s" , k2 , strings .TrimPrefix (k , "context:" )) // should be validated before already
283278 }
279+
284280 var found bool
285281 for _ , dp2 := range dps2 {
286282 if dp2 .driverIndex == dp .driverIndex {
@@ -289,12 +285,63 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
289285 }
290286 }
291287 if ! found {
292- return nil , errors .Errorf ("failed to use %s as context %s for %s because targets build with different drivers" , k2 , strings .TrimPrefix (k , "context:" ), name )
288+ return errors .Errorf ("failed to use %s as context %s for %s because targets build with different drivers" , k2 , strings .TrimPrefix (k , "context:" ), name )
293289 }
294290 }
295291 }
296292 }
297293 }
294+ return nil
295+ }
296+
297+ func toRepoOnly (in string ) (string , error ) {
298+ m := map [string ]struct {}{}
299+ p := strings .Split (in , "," )
300+ for _ , pp := range p {
301+ n , err := reference .ParseNormalizedNamed (pp )
302+ if err != nil {
303+ return "" , err
304+ }
305+ m [n .Name ()] = struct {}{}
306+ }
307+ out := make ([]string , 0 , len (m ))
308+ for k := range m {
309+ out = append (out , k )
310+ }
311+ return strings .Join (out , "," ), nil
312+ }
313+
314+ func Build (ctx context.Context , nodes []builder.Node , opts map [string ]Options , docker * dockerutil.Client , cfg * confutil.Config , w progress.Writer ) (resp map [string ]* client.SolveResponse , err error ) {
315+ return BuildWithResultHandler (ctx , nodes , opts , docker , cfg , w , nil )
316+ }
317+
318+ func BuildWithResultHandler (ctx context.Context , nodes []builder.Node , opts map [string ]Options , docker * dockerutil.Client , cfg * confutil.Config , w progress.Writer , resultHandleFunc func (driverIdx int , rCtx * ResultHandle )) (resp map [string ]* client.SolveResponse , err error ) {
319+ if len (nodes ) == 0 {
320+ return nil , errors .Errorf ("driver required for build" )
321+ }
322+
323+ nodes , err = filterAvailableNodes (nodes )
324+ if err != nil {
325+ return nil , errors .Wrapf (err , "no valid drivers found" )
326+ }
327+ warnOnNoOutput (ctx , nodes , opts )
328+
329+ drivers , err := resolveDrivers (ctx , nodes , opts , w )
330+ if err != nil {
331+ return nil , err
332+ }
333+
334+ eg , ctx := errgroup .WithContext (ctx )
335+ reqForNodes , release , err := newBuildRequests (ctx , docker , cfg , drivers , w , opts )
336+ if err != nil {
337+ return nil , err
338+ }
339+ defer release ()
340+
341+ // validate that all links between targets use same drivers
342+ if err := validateTargetLinks (reqForNodes , drivers , opts ); err != nil {
343+ return nil , err
344+ }
298345
299346 sharedSessions , err := detectSharedMounts (ctx , reqForNodes )
300347 if err != nil {
@@ -311,7 +358,6 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
311358
312359 for k , opt := range opts {
313360 err := func (k string ) (err error ) {
314- opt := opt
315361 dps := drivers [k ]
316362 multiDriver := len (drivers [k ]) > 1
317363
@@ -441,51 +487,27 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
441487 req .FrontendOpt ["requestid" ] = "frontend." + opt .CallFunc .Name
442488 }
443489
444- res , err := c . Solve (ctx , req )
490+ res , err := solve (ctx , c , req )
445491 if err != nil {
446- req , ok := fallbackPrintError (err , req )
447- if ok {
448- res2 , err2 := c .Solve (ctx , req )
449- if err2 != nil {
450- return nil , err
451- }
452- res = res2
453- } else {
454- return nil , err
455- }
492+ return nil , err
456493 }
494+
457495 if opt .CallFunc != nil {
458496 callRes = res .Metadata
459497 }
460498
461499 rKey := resultKey (dp .driverIndex , k )
462500 results .Set (rKey , res )
463501
464- if children , ok := childTargets [rKey ]; ok && len (children ) > 0 {
465- // wait for the child targets to register their LLB before evaluating
466- _ , err := results .Get (ctx , children ... )
467- if err != nil {
468- return nil , err
469- }
470- // we need to wait until the child targets have completed before we can release
471- eg , ctx := errgroup .WithContext (ctx )
472- eg .Go (func () error {
473- return res .EachRef (func (ref gateway.Reference ) error {
474- return ref .Evaluate (ctx )
475- })
476- })
477- eg .Go (func () error {
478- _ , err := results .Get (ctx , children ... )
479- return err
480- })
481- if err := eg .Wait (); err != nil {
502+ if children := childTargets [rKey ]; len (children ) > 0 {
503+ if err := waitForChildren (ctx , res , results , children ); err != nil {
482504 return nil , err
483505 }
484506 }
485-
486507 return res , nil
487508 }
488509 buildRef := fmt .Sprintf ("%s/%s/%s" , node .Builder , node .Name , so .Ref )
510+
489511 var rr * client.SolveResponse
490512 if resultHandleFunc != nil {
491513 var resultHandle * ResultHandle
@@ -496,6 +518,7 @@ func BuildWithResultHandler(ctx context.Context, nodes []builder.Node, opts map[
496518 rr , err = c .Build (ctx , * so , "buildx" , buildFunc , ch )
497519 tracing .FinishWithError (span , err )
498520 }
521+
499522 if ! so .Internal && desktop .BuildBackendEnabled () && node .Driver .HistoryAPISupported (ctx ) {
500523 if err != nil {
501524 return & desktop.ErrorWithBuildRef {
@@ -1146,3 +1169,40 @@ func ReadSourcePolicy() (*spb.Policy, error) {
11461169
11471170 return & pol , nil
11481171}
1172+
1173+ func solve (ctx context.Context , c gateway.Client , req gateway.SolveRequest ) (* gateway.Result , error ) {
1174+ res , err := c .Solve (ctx , req )
1175+ if err != nil {
1176+ req , ok := fallbackPrintError (err , req )
1177+ if ok {
1178+ res2 , err2 := c .Solve (ctx , req )
1179+ if err2 != nil {
1180+ return nil , err
1181+ }
1182+ res = res2
1183+ } else {
1184+ return nil , err
1185+ }
1186+ }
1187+ return res , nil
1188+ }
1189+
1190+ func waitForChildren (ctx context.Context , res * gateway.Result , results * waitmap.Map , children []string ) error {
1191+ // wait for the child targets to register their LLB before evaluating
1192+ _ , err := results .Get (ctx , children ... )
1193+ if err != nil {
1194+ return err
1195+ }
1196+ // we need to wait until the child targets have completed before we can release
1197+ eg , ctx := errgroup .WithContext (ctx )
1198+ eg .Go (func () error {
1199+ return res .EachRef (func (ref gateway.Reference ) error {
1200+ return ref .Evaluate (ctx )
1201+ })
1202+ })
1203+ eg .Go (func () error {
1204+ _ , err := results .Get (ctx , children ... )
1205+ return err
1206+ })
1207+ return eg .Wait ()
1208+ }
0 commit comments