@@ -69,41 +69,11 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
69
69
return nil
70
70
}
71
71
imageName := api .GetImageNameOrDefault (service , project .Name )
72
- buildOptions , err := s .toBuildOptions (project , service , imageName , options . SSHs )
72
+ buildOptions , err := s .toBuildOptions (project , service , imageName , options )
73
73
if err != nil {
74
74
return err
75
75
}
76
- buildOptions .Pull = options .Pull
77
76
buildOptions .BuildArgs = mergeArgs (buildOptions .BuildArgs , args )
78
- buildOptions .NoCache = options .NoCache
79
- buildOptions .CacheFrom , err = buildflags .ParseCacheEntry (service .Build .CacheFrom )
80
- if err != nil {
81
- return err
82
- }
83
- if len (service .Build .AdditionalContexts ) > 0 {
84
- buildOptions .Inputs .NamedContexts = toBuildContexts (service .Build .AdditionalContexts )
85
- }
86
- for _ , image := range service .Build .CacheFrom {
87
- buildOptions .CacheFrom = append (buildOptions .CacheFrom , bclient.CacheOptionsEntry {
88
- Type : "registry" ,
89
- Attrs : map [string ]string {"ref" : image },
90
- })
91
- }
92
- buildOptions .Exports = []bclient.ExportEntry {{
93
- Type : "docker" ,
94
- Attrs : map [string ]string {
95
- "load" : "true" ,
96
- "push" : fmt .Sprint (options .Push ),
97
- },
98
- }}
99
- if len (buildOptions .Platforms ) > 1 {
100
- buildOptions .Exports = []bclient.ExportEntry {{
101
- Type : "image" ,
102
- Attrs : map [string ]string {
103
- "push" : fmt .Sprint (options .Push ),
104
- },
105
- }}
106
- }
107
77
opts := map [string ]build.Options {imageName : buildOptions }
108
78
ids , err := s .doBuild (ctx , project , opts , options .Progress )
109
79
if err != nil {
@@ -146,11 +116,14 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
146
116
if quietPull {
147
117
mode = xprogress .PrinterModeQuiet
148
118
}
149
- opts , err := s .getBuildOptions (project , images )
119
+
120
+ err = s .prepareProjectForBuild (project , images )
150
121
if err != nil {
151
122
return err
152
123
}
153
- builtImages , err := s .doBuild (ctx , project , opts , mode )
124
+ builtImages , err := s .build (ctx , project , api.BuildOptions {
125
+ Progress : mode ,
126
+ })
154
127
if err != nil {
155
128
return err
156
129
}
@@ -172,37 +145,45 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
172
145
return nil
173
146
}
174
147
175
- func (s * composeService ) getBuildOptions (project * types.Project , images map [string ]string ) ( map [ string ]build. Options , error ) {
176
- opts := map [ string ]build. Options {}
177
- for _ , service := range project .Services {
148
+ func (s * composeService ) prepareProjectForBuild (project * types.Project , images map [string ]string ) error {
149
+ platform := project . Environment [ "DOCKER_DEFAULT_PLATFORM" ]
150
+ for i , service := range project .Services {
178
151
if service .Image == "" && service .Build == nil {
179
- return nil , fmt .Errorf ("invalid service %q. Must specify either image or build" , service .Name )
152
+ return fmt .Errorf ("invalid service %q. Must specify either image or build" , service .Name )
180
153
}
154
+ if service .Build == nil {
155
+ continue
156
+ }
157
+
181
158
imageName := api .GetImageNameOrDefault (service , project .Name )
159
+ service .Image = imageName
160
+
182
161
_ , localImagePresent := images [imageName ]
162
+ if localImagePresent && service .PullPolicy != types .PullPolicyBuild {
163
+ service .Build = nil
164
+ project .Services [i ] = service
165
+ continue
166
+ }
183
167
184
- if service . Build != nil {
185
- if localImagePresent && service .PullPolicy != types . PullPolicyBuild {
186
- continue
168
+ if platform != "" {
169
+ if len ( service . Build . Platforms ) > 0 && ! utils . StringContains ( service .Build . Platforms , platform ) {
170
+ return fmt . Errorf ( "service %q build.platforms does not support value set by DOCKER_DEFAULT_PLATFORM: %s" , service . Name , platform )
187
171
}
188
- opt , err := s .toBuildOptions (project , service , imageName , []types.SSHKey {})
189
- if err != nil {
190
- return nil , err
191
- }
192
- opt .Exports = []bclient.ExportEntry {{
193
- Type : "docker" ,
194
- Attrs : map [string ]string {
195
- "load" : "true" ,
196
- },
197
- }}
198
- if opt .Platforms , err = useDockerDefaultOrServicePlatform (project , service , true ); err != nil {
199
- opt .Platforms = []specs.Platform {}
172
+ service .Platform = platform
173
+ }
174
+
175
+ if service .Platform == "" {
176
+ // let builder to build for default platform
177
+ service .Build .Platforms = nil
178
+ } else {
179
+ if len (service .Build .Platforms ) > 0 && ! utils .StringContains (service .Build .Platforms , service .Platform ) {
180
+ return fmt .Errorf ("service %q build configuration does not support platform: %s" , service .Name , platform )
200
181
}
201
- opts [imageName ] = opt
202
- continue
182
+ service .Build .Platforms = []string {service .Platform }
203
183
}
184
+ project .Services [i ] = service
204
185
}
205
- return opts , nil
186
+ return nil
206
187
}
207
188
208
189
func (s * composeService ) getLocalImagesDigests (ctx context.Context , project * types.Project ) (map [string ]string , error ) {
@@ -243,7 +224,7 @@ func (s *composeService) doBuild(ctx context.Context, project *types.Project, op
243
224
return s .doBuildBuildkit (ctx , opts , mode )
244
225
}
245
226
246
- func (s * composeService ) toBuildOptions (project * types.Project , service types.ServiceConfig , imageTag string , sshKeys []types. SSHKey ) (build.Options , error ) {
227
+ func (s * composeService ) toBuildOptions (project * types.Project , service types.ServiceConfig , imageTag string , options api. BuildOptions ) (build.Options , error ) {
247
228
var tags []string
248
229
tags = append (tags , imageTag )
249
230
@@ -272,8 +253,8 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
272
253
sessionConfig := []session.Attachable {
273
254
authprovider .NewDockerAuthProvider (s .configFile ()),
274
255
}
275
- if len (sshKeys ) > 0 || len (service .Build .SSH ) > 0 {
276
- sshAgentProvider , err := sshAgentProvider (append (service .Build .SSH , sshKeys ... ))
256
+ if len (options . SSHs ) > 0 || len (service .Build .SSH ) > 0 {
257
+ sshAgentProvider , err := sshAgentProvider (append (service .Build .SSH , options . SSHs ... ))
277
258
if err != nil {
278
259
return build.Options {}, err
279
260
}
@@ -298,20 +279,37 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
298
279
299
280
imageLabels := getImageBuildLabels (project , service )
300
281
282
+ exports := []bclient.ExportEntry {{
283
+ Type : "docker" ,
284
+ Attrs : map [string ]string {
285
+ "load" : "true" ,
286
+ "push" : fmt .Sprint (options .Push ),
287
+ },
288
+ }}
289
+ if len (service .Build .Platforms ) > 1 {
290
+ exports = []bclient.ExportEntry {{
291
+ Type : "image" ,
292
+ Attrs : map [string ]string {
293
+ "push" : fmt .Sprint (options .Push ),
294
+ },
295
+ }}
296
+ }
297
+
301
298
return build.Options {
302
299
Inputs : build.Inputs {
303
300
ContextPath : service .Build .Context ,
304
301
DockerfileInline : service .Build .DockerfileInline ,
305
302
DockerfilePath : dockerFilePath (service .Build .Context , service .Build .Dockerfile ),
303
+ NamedContexts : toBuildContexts (service .Build .AdditionalContexts ),
306
304
},
307
305
CacheFrom : cacheFrom ,
308
306
CacheTo : cacheTo ,
309
- NoCache : service .Build .NoCache ,
310
- Pull : service .Build .Pull ,
307
+ NoCache : service .Build .NoCache || options . NoCache ,
308
+ Pull : service .Build .Pull || options . Pull ,
311
309
BuildArgs : buildArgs ,
312
310
Tags : tags ,
313
311
Target : service .Build .Target ,
314
- Exports : []bclient. ExportEntry {{ Type : "image" , Attrs : map [ string ] string {}}} ,
312
+ Exports : exports ,
315
313
Platforms : plats ,
316
314
Labels : imageLabels ,
317
315
NetworkMode : service .Build .Network ,
0 commit comments