@@ -135,7 +135,8 @@ func resolveStates(ctx context.Context, bopts *BOpts, platform ocispecs.Platform
135
135
defer wg .Done ()
136
136
137
137
shlex := shell .NewLex (dockerfile .EscapeToken )
138
- resolvedBaseStageName , err := shlex .ProcessWordWithMatches (stage .BaseName , utils .NewMapGetter (bopts .BuildArgs ))
138
+ resolvedGlobalArgs := globalArgs (bopts .BuildPlatforms [0 ], platform , bopts .BuildArgs , bopts .Target )
139
+ resolvedBaseStageName , err := shlex .ProcessWordWithMatches (stage .BaseName , resolvedGlobalArgs )
139
140
if err != nil {
140
141
errCh <- fmt .Errorf ("invalid arg for stage[%s]: %v" , stage .BaseName , err )
141
142
return
@@ -145,6 +146,22 @@ func resolveStates(ctx context.Context, bopts *BOpts, platform ocispecs.Platform
145
146
return
146
147
}
147
148
149
+ // if platform is specified for the stage, parse and use as the target platform
150
+ stagePlatform := platform
151
+ if stage .Platform != "" {
152
+ resolvedStagePlatformStr , err := shlex .ProcessWordWithMatches (stage .Platform , resolvedGlobalArgs )
153
+ if err != nil {
154
+ errCh <- fmt .Errorf ("invalid platform for stage[%s]: %v" , stage .BaseName , err )
155
+ return
156
+ }
157
+ resolvedStagePlatform , err := platforms .Parse (resolvedStagePlatformStr .Result )
158
+ if err != nil {
159
+ errCh <- fmt .Errorf ("invalid platform for stage[%s]: %v" , stage .BaseName , err )
160
+ return
161
+ }
162
+ stagePlatform = resolvedStagePlatform
163
+ }
164
+
148
165
// if there's another stage with this name before the current stage, that will be used as the source
149
166
namedIndex , hasNamedStage := instructions .HasStage (stages , resolvedBaseStageName .Result )
150
167
if hasNamedStage && namedIndex < i {
@@ -164,7 +181,7 @@ func resolveStates(ctx context.Context, bopts *BOpts, platform ocispecs.Platform
164
181
clog ("[resolver] fetching image...%s" , ref .String ())
165
182
166
183
resolverOpts := sourceresolver.Opt {
167
- Platform : & platform ,
184
+ Platform : & stagePlatform ,
168
185
}
169
186
resolverOpts .OCILayoutOpt = & sourceresolver.ResolveOCILayoutOpt {
170
187
Store : sourceresolver.ResolveImageConfigOptStore {
@@ -196,7 +213,7 @@ func resolveStates(ctx context.Context, bopts *BOpts, platform ocispecs.Platform
196
213
fqdn = fqdn + "@" + digest .String ()
197
214
}
198
215
logrus .WithField ("ref" , fqdn ).Infof ("creating llb" )
199
- st := llb .OCILayout (fqdn , llb .OCIStore ("" , "container" ), llb .Platform (platform ))
216
+ st := llb .OCILayout (fqdn , llb .OCIStore ("" , "container" ), llb .Platform (stagePlatform ))
200
217
201
218
named , err := dref .ParseNormalizedNamed (ref .String ())
202
219
if err != nil {
@@ -206,7 +223,7 @@ func resolveStates(ctx context.Context, bopts *BOpts, platform ocispecs.Platform
206
223
name := strings .TrimSuffix (dref .FamiliarString (named ), ":latest" )
207
224
208
225
// pname constructs a platform-qualified image reference in the format buildkit requires for digest resolution
209
- pname := name + "::" + platforms .FormatAll (platforms .Normalize (platform ))
226
+ pname := name + "::" + platforms .FormatAll (platforms .Normalize (stagePlatform ))
210
227
imgMetaMap := map [string ][]byte {
211
228
exptypes .ExporterImageConfigKey : img ,
212
229
}
@@ -221,7 +238,7 @@ func resolveStates(ctx context.Context, bopts *BOpts, platform ocispecs.Platform
221
238
defer stateLock .Unlock ()
222
239
223
240
states [pname ] = stateMeta {
224
- state : st .Platform (platform ),
241
+ state : st .Platform (stagePlatform ),
225
242
imgMeta : imgMeta ,
226
243
}
227
244
}(stage )
@@ -317,7 +334,7 @@ func solvePlatform(ctx context.Context, bopts *BOpts, pl ocispecs.Platform, c ga
317
334
Client : cl ,
318
335
}
319
336
320
- convertOpt .BuildPlatforms = utils .BuildPlatforms ()
337
+ convertOpt .BuildPlatforms = bopts .BuildPlatforms
321
338
convertOpt .TargetPlatforms = bopts .Platforms
322
339
convertOpt .BuildArgs = bopts .BuildArgs
323
340
convertOpt .Labels = bopts .Labels
@@ -377,3 +394,26 @@ func solvePlatform(ctx context.Context, bopts *BOpts, pl ocispecs.Platform, c ga
377
394
}
378
395
return ref , cfgJSON , nil
379
396
}
397
+
398
+ func globalArgs (buildPlatform , targetPlatform ocispecs.Platform , buildArgs map [string ]string , target string ) utils.MapGetter {
399
+ if target == "" {
400
+ target = "default"
401
+ }
402
+ args := map [string ]string {
403
+ "BUILDPLATFORM" : platforms .Format (buildPlatform ),
404
+ "BUILDOS" : buildPlatform .OS ,
405
+ "BUILDOSVERSION" : buildPlatform .OSVersion ,
406
+ "BUILDARCH" : buildPlatform .Architecture ,
407
+ "BUILDVARIANT" : buildPlatform .Variant ,
408
+ "TARGETPLATFORM" : platforms .FormatAll (targetPlatform ),
409
+ "TARGETOS" : targetPlatform .OS ,
410
+ "TARGETOSVERSION" : targetPlatform .OSVersion ,
411
+ "TARGETARCH" : targetPlatform .Architecture ,
412
+ "TARGETVARIANT" : targetPlatform .Variant ,
413
+ "TARGETSTAGE" : target ,
414
+ }
415
+ for k , v := range buildArgs {
416
+ args [k ] = v
417
+ }
418
+ return utils .NewMapGetter (args )
419
+ }
0 commit comments