@@ -159,6 +159,9 @@ type BuildOptions struct {
159159 // Process type that will be used when setting container start command.
160160 DefaultProcessType string
161161
162+ // Platform is the desired platform to build on (e.g., linux/amd64)
163+ Platform string
164+
162165 // Strategy for updating local images before a build.
163166 PullPolicy image.PullPolicy
164167
@@ -320,32 +323,54 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
320323 return errors .Wrapf (err , "invalid builder '%s'" , opts .Builder )
321324 }
322325
323- rawBuilderImage , err := c .imageFetcher .Fetch (ctx , builderRef .Name (), image.FetchOptions {Daemon : true , PullPolicy : opts .PullPolicy })
326+ requestedTarget := func () * dist.Target {
327+ if opts .Platform == "" {
328+ return nil
329+ }
330+ parts := strings .Split (opts .Platform , "/" )
331+ switch len (parts ) {
332+ case 0 :
333+ return nil
334+ case 1 :
335+ return & dist.Target {OS : parts [0 ]}
336+ case 2 :
337+ return & dist.Target {OS : parts [0 ], Arch : parts [1 ]}
338+ default :
339+ return & dist.Target {OS : parts [0 ], Arch : parts [1 ], ArchVariant : parts [2 ]}
340+ }
341+ }()
342+
343+ rawBuilderImage , err := c .imageFetcher .Fetch (
344+ ctx ,
345+ builderRef .Name (),
346+ image.FetchOptions {
347+ Daemon : true ,
348+ Target : requestedTarget ,
349+ PullPolicy : opts .PullPolicy },
350+ )
324351 if err != nil {
325352 return errors .Wrapf (err , "failed to fetch builder image '%s'" , builderRef .Name ())
326353 }
327354
328- builderOS , err := rawBuilderImage . OS ()
329- if err != nil {
330- return errors . Wrapf ( err , "getting builder OS" )
331- }
332-
333- builderArch , err := rawBuilderImage . Architecture ()
334- if err != nil {
335- return errors . Wrapf ( err , "getting builder architecture" )
355+ var targetToUse * dist. Target
356+ if requestedTarget != nil {
357+ targetToUse = requestedTarget
358+ } else {
359+ targetToUse , err = getTargetFromBuilder ( rawBuilderImage )
360+ if err != nil {
361+ return err
362+ }
336363 }
337364
338365 bldr , err := c .getBuilder (rawBuilderImage )
339366 if err != nil {
340367 return errors .Wrapf (err , "invalid builder %s" , style .Symbol (opts .Builder ))
341368 }
342369
343- target := & dist.Target {OS : builderOS , Arch : builderArch }
344-
345370 fetchOptions := image.FetchOptions {
346371 Daemon : ! opts .Publish ,
347372 PullPolicy : opts .PullPolicy ,
348- Target : target ,
373+ Target : targetToUse ,
349374 }
350375 runImageName := c .resolveRunImage (opts .RunImage , imgRegistry , builderRef .Context ().RegistryStr (), bldr .DefaultRunImage (), opts .AdditionalMirrors , opts .Publish , fetchOptions )
351376
@@ -374,12 +399,12 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
374399 return err
375400 }
376401
377- fetchedBPs , order , err := c .processBuildpacks (ctx , bldr .Image (), bldr . Buildpacks (), bldr .Order (), bldr .StackID , opts )
402+ fetchedBPs , order , err := c .processBuildpacks (ctx , bldr .Buildpacks (), bldr .Order (), bldr .StackID , opts , targetToUse )
378403 if err != nil {
379404 return err
380405 }
381406
382- fetchedExs , orderExtensions , err := c .processExtensions (ctx , bldr .Image (), bldr . Extensions (), bldr . OrderExtensions (), bldr . StackID , opts )
407+ fetchedExs , orderExtensions , err := c .processExtensions (ctx , bldr .Extensions (), opts , targetToUse )
383408 if err != nil {
384409 return err
385410 }
@@ -420,7 +445,7 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
420445 image.FetchOptions {
421446 Daemon : true ,
422447 PullPolicy : opts .PullPolicy ,
423- Target : target ,
448+ Target : targetToUse ,
424449 },
425450 )
426451 if err != nil {
@@ -492,7 +517,7 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
492517 defer c .docker .ImageRemove (context .Background (), ephemeralBuilder .Name (), types.RemoveOptions {Force : true })
493518
494519 if len (bldr .OrderExtensions ()) > 0 || len (ephemeralBuilder .OrderExtensions ()) > 0 {
495- if builderOS == "windows" {
520+ if targetToUse . OS == "windows" {
496521 return fmt .Errorf ("builder contains image extensions which are not supported for Windows builds" )
497522 }
498523 if ! (opts .PullPolicy == image .PullAlways ) {
@@ -504,7 +529,7 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
504529 opts .ContainerConfig .Volumes = appendLayoutVolumes (opts .ContainerConfig .Volumes , pathsConfig )
505530 }
506531
507- processedVolumes , warnings , err := processVolumes (builderOS , opts .ContainerConfig .Volumes )
532+ processedVolumes , warnings , err := processVolumes (targetToUse . OS , opts .ContainerConfig .Volumes )
508533 if err != nil {
509534 return err
510535 }
@@ -735,6 +760,26 @@ func (c *Client) Build(ctx context.Context, opts BuildOptions) error {
735760 return c .logImageNameAndSha (ctx , opts .Publish , imageRef )
736761}
737762
763+ func getTargetFromBuilder (builderImage imgutil.Image ) (* dist.Target , error ) {
764+ builderOS , err := builderImage .OS ()
765+ if err != nil {
766+ return nil , fmt .Errorf ("failed to get builder OS: %w" , err )
767+ }
768+ builderArch , err := builderImage .Architecture ()
769+ if err != nil {
770+ return nil , fmt .Errorf ("failed to get builder architecture: %w" , err )
771+ }
772+ builderArchVariant , err := builderImage .Variant ()
773+ if err != nil {
774+ return nil , fmt .Errorf ("failed to get builder architecture variant: %w" , err )
775+ }
776+ return & dist.Target {
777+ OS : builderOS ,
778+ Arch : builderArch ,
779+ ArchVariant : builderArchVariant ,
780+ }, nil
781+ }
782+
738783func extractSupportedLifecycleApis (labels map [string ]string ) ([]string , error ) {
739784 // sample contents of labels:
740785 // {io.buildpacks.builder.metadata:\"{\"lifecycle\":{\"version\":\"0.15.3\"},\"api\":{\"buildpack\":\"0.2\",\"platform\":\"0.3\"}}",
@@ -1087,7 +1132,7 @@ func (c *Client) processProxyConfig(config *ProxyConfig) ProxyConfig {
10871132// ----------
10881133// - group:
10891134// - A
1090- func (c * Client ) processBuildpacks (ctx context.Context , builderImage imgutil. Image , builderBPs []dist.ModuleInfo , builderOrder dist.Order , stackID string , opts BuildOptions ) (fetchedBPs []buildpack.BuildModule , order dist.Order , err error ) {
1135+ func (c * Client ) processBuildpacks (ctx context.Context , builderBPs []dist.ModuleInfo , builderOrder dist.Order , stackID string , opts BuildOptions , targetToUse * dist. Target ) (fetchedBPs []buildpack.BuildModule , order dist.Order , err error ) {
10911136 relativeBaseDir := opts .RelativeBaseDir
10921137 declaredBPs := opts .Buildpacks
10931138
@@ -1130,7 +1175,7 @@ func (c *Client) processBuildpacks(ctx context.Context, builderImage imgutil.Ima
11301175 order = newOrder
11311176 }
11321177 default :
1133- newFetchedBPs , moduleInfo , err := c .fetchBuildpack (ctx , bp , relativeBaseDir , builderImage , builderBPs , opts , buildpack .KindBuildpack )
1178+ newFetchedBPs , moduleInfo , err := c .fetchBuildpack (ctx , bp , relativeBaseDir , builderBPs , opts , buildpack .KindBuildpack , targetToUse )
11341179 if err != nil {
11351180 return fetchedBPs , order , err
11361181 }
@@ -1164,7 +1209,7 @@ func (c *Client) processBuildpacks(ctx context.Context, builderImage imgutil.Ima
11641209 if len (preBuildpacks ) > 0 || len (postBuildpacks ) > 0 {
11651210 order = builderOrder
11661211 for _ , bp := range preBuildpacks {
1167- newFetchedBPs , moduleInfo , err := c .fetchBuildpack (ctx , bp , relativeBaseDir , builderImage , builderBPs , opts , buildpack .KindBuildpack )
1212+ newFetchedBPs , moduleInfo , err := c .fetchBuildpack (ctx , bp , relativeBaseDir , builderBPs , opts , buildpack .KindBuildpack , targetToUse )
11681213 if err != nil {
11691214 return fetchedBPs , order , err
11701215 }
@@ -1173,7 +1218,7 @@ func (c *Client) processBuildpacks(ctx context.Context, builderImage imgutil.Ima
11731218 }
11741219
11751220 for _ , bp := range postBuildpacks {
1176- newFetchedBPs , moduleInfo , err := c .fetchBuildpack (ctx , bp , relativeBaseDir , builderImage , builderBPs , opts , buildpack .KindBuildpack )
1221+ newFetchedBPs , moduleInfo , err := c .fetchBuildpack (ctx , bp , relativeBaseDir , builderBPs , opts , buildpack .KindBuildpack , targetToUse )
11771222 if err != nil {
11781223 return fetchedBPs , order , err
11791224 }
@@ -1186,7 +1231,7 @@ func (c *Client) processBuildpacks(ctx context.Context, builderImage imgutil.Ima
11861231 return fetchedBPs , order , nil
11871232}
11881233
1189- func (c * Client ) fetchBuildpack (ctx context.Context , bp string , relativeBaseDir string , builderImage imgutil. Image , builderBPs []dist.ModuleInfo , opts BuildOptions , kind string ) ([]buildpack.BuildModule , * dist.ModuleInfo , error ) {
1234+ func (c * Client ) fetchBuildpack (ctx context.Context , bp string , relativeBaseDir string , builderBPs []dist.ModuleInfo , opts BuildOptions , kind string , targetToUse * dist. Target ) ([]buildpack.BuildModule , * dist.ModuleInfo , error ) {
11901235 pullPolicy := opts .PullPolicy
11911236 publish := opts .Publish
11921237 registry := opts .Registry
@@ -1206,19 +1251,9 @@ func (c *Client) fetchBuildpack(ctx context.Context, bp string, relativeBaseDir
12061251 Version : version ,
12071252 }
12081253 default :
1209- builderOS , err := builderImage .OS ()
1210- if err != nil {
1211- return nil , nil , errors .Wrapf (err , "getting builder OS" )
1212- }
1213-
1214- builderArch , err := builderImage .Architecture ()
1215- if err != nil {
1216- return nil , nil , errors .Wrapf (err , "getting builder architecture" )
1217- }
1218-
12191254 downloadOptions := buildpack.DownloadOptions {
12201255 RegistryName : registry ,
1221- Target : & dist. Target { OS : builderOS , Arch : builderArch } ,
1256+ Target : targetToUse ,
12221257 RelativeBaseDir : relativeBaseDir ,
12231258 Daemon : ! publish ,
12241259 PullPolicy : pullPolicy ,
@@ -1322,7 +1357,7 @@ func prependBuildpackToOrder(order dist.Order, bpInfo dist.ModuleInfo) (newOrder
13221357 return newOrder
13231358}
13241359
1325- func (c * Client ) processExtensions (ctx context.Context , builderImage imgutil. Image , builderExs []dist.ModuleInfo , builderOrder dist. Order , stackID string , opts BuildOptions ) (fetchedExs []buildpack.BuildModule , orderExtensions dist.Order , err error ) {
1360+ func (c * Client ) processExtensions (ctx context.Context , builderExs []dist.ModuleInfo , opts BuildOptions , targetToUse * dist. Target ) (fetchedExs []buildpack.BuildModule , orderExtensions dist.Order , err error ) {
13261361 relativeBaseDir := opts .RelativeBaseDir
13271362 declaredExs := opts .Extensions
13281363
@@ -1339,7 +1374,7 @@ func (c *Client) processExtensions(ctx context.Context, builderImage imgutil.Ima
13391374 case buildpack .FromBuilderLocator :
13401375 return nil , nil , errors .New ("from builder is not supported for extensions" )
13411376 default :
1342- newFetchedExs , moduleInfo , err := c .fetchBuildpack (ctx , ex , relativeBaseDir , builderImage , builderExs , opts , buildpack .KindExtension )
1377+ newFetchedExs , moduleInfo , err := c .fetchBuildpack (ctx , ex , relativeBaseDir , builderExs , opts , buildpack .KindExtension , targetToUse )
13431378 if err != nil {
13441379 return fetchedExs , orderExtensions , err
13451380 }
0 commit comments