@@ -69,41 +69,11 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
6969 return nil
7070 }
7171 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 )
7373 if err != nil {
7474 return err
7575 }
76- buildOptions .Pull = options .Pull
7776 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- }
10777 opts := map [string ]build.Options {imageName : buildOptions }
10878 ids , err := s .doBuild (ctx , project , opts , options .Progress )
10979 if err != nil {
@@ -146,11 +116,14 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
146116 if quietPull {
147117 mode = xprogress .PrinterModeQuiet
148118 }
149- opts , err := s .getBuildOptions (project , images )
119+
120+ err = s .prepareProjectForBuild (project , images )
150121 if err != nil {
151122 return err
152123 }
153- builtImages , err := s .doBuild (ctx , project , opts , mode )
124+ builtImages , err := s .build (ctx , project , api.BuildOptions {
125+ Progress : mode ,
126+ })
154127 if err != nil {
155128 return err
156129 }
@@ -172,37 +145,45 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
172145 return nil
173146}
174147
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 {
178151 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 )
180153 }
154+ if service .Build == nil {
155+ continue
156+ }
157+
181158 imageName := api .GetImageNameOrDefault (service , project .Name )
159+ service .Image = imageName
160+
182161 _ , localImagePresent := images [imageName ]
162+ if localImagePresent && service .PullPolicy != types .PullPolicyBuild {
163+ service .Build = nil
164+ project .Services [i ] = service
165+ continue
166+ }
183167
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 )
187171 }
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 )
200181 }
201- opts [imageName ] = opt
202- continue
182+ service .Build .Platforms = []string {service .Platform }
203183 }
184+ project .Services [i ] = service
204185 }
205- return opts , nil
186+ return nil
206187}
207188
208189func (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
243224 return s .doBuildBuildkit (ctx , opts , mode )
244225}
245226
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 ) {
247228 var tags []string
248229 tags = append (tags , imageTag )
249230
@@ -272,8 +253,8 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
272253 sessionConfig := []session.Attachable {
273254 authprovider .NewDockerAuthProvider (s .configFile ()),
274255 }
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 ... ))
277258 if err != nil {
278259 return build.Options {}, err
279260 }
@@ -298,20 +279,37 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
298279
299280 imageLabels := getImageBuildLabels (project , service )
300281
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+
301298 return build.Options {
302299 Inputs : build.Inputs {
303300 ContextPath : service .Build .Context ,
304301 DockerfileInline : service .Build .DockerfileInline ,
305302 DockerfilePath : dockerFilePath (service .Build .Context , service .Build .Dockerfile ),
303+ NamedContexts : toBuildContexts (service .Build .AdditionalContexts ),
306304 },
307305 CacheFrom : cacheFrom ,
308306 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 ,
311309 BuildArgs : buildArgs ,
312310 Tags : tags ,
313311 Target : service .Build .Target ,
314- Exports : []bclient. ExportEntry {{ Type : "image" , Attrs : map [ string ] string {}}} ,
312+ Exports : exports ,
315313 Platforms : plats ,
316314 Labels : imageLabels ,
317315 NetworkMode : service .Build .Network ,
0 commit comments