Skip to content

Commit 3492640

Browse files
committed
support a list of build platforms
1 parent f62c5af commit 3492640

File tree

3 files changed

+80
-26
lines changed

3 files changed

+80
-26
lines changed

pkg/build/buildopts.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ type BOpts struct {
6464
Dockerfile []byte
6565
Tag string
6666
ContextDir string
67-
BuildPlatform ocispecs.Platform
67+
BuildPlatforms []ocispecs.Platform
6868
Platforms []ocispecs.Platform
6969
NoCache bool
7070
Target string
@@ -133,9 +133,9 @@ func NewBuildOpts(ctx context.Context, basePath string, contextMap map[string][]
133133
ctxDir = c
134134
}
135135

136-
buildPlatform := platforms.DefaultSpec()
137-
if bps := utils.BuildPlatforms(); len(bps) > 0 {
138-
buildPlatform = bps[0]
136+
bps := utils.BuildPlatforms()
137+
if len(bps) == 0 {
138+
bps = append(bps, platforms.DefaultSpec())
139139
}
140140

141141
pls, err := func() ([]ocispecs.Platform, error) {
@@ -238,7 +238,7 @@ func NewBuildOpts(ctx context.Context, basePath string, contextMap map[string][]
238238
BuildID: buildID,
239239
Dockerfile: dockerfileBytes,
240240
Tag: tag,
241-
BuildPlatform: buildPlatform,
241+
BuildPlatforms: bps,
242242
Platforms: pls,
243243
ContextDir: ctxDir,
244244
ContentStore: contentProxy,

pkg/build/frontend.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func resolveStates(ctx context.Context, bopts *BOpts, platform ocispecs.Platform
135135
defer wg.Done()
136136

137137
shlex := shell.NewLex(dockerfile.EscapeToken)
138-
resolvedGlobalArgs := globalArgs(bopts.BuildPlatform, platform, bopts.BuildArgs, bopts.Target)
138+
resolvedGlobalArgs := globalArgs(bopts.BuildPlatforms[0], platform, bopts.BuildArgs, bopts.Target)
139139
resolvedBaseStageName, err := shlex.ProcessWordWithMatches(stage.BaseName, resolvedGlobalArgs)
140140
if err != nil {
141141
errCh <- fmt.Errorf("invalid arg for stage[%s]: %v", stage.BaseName, err)
@@ -334,7 +334,7 @@ func solvePlatform(ctx context.Context, bopts *BOpts, pl ocispecs.Platform, c ga
334334
Client: cl,
335335
}
336336

337-
convertOpt.BuildPlatforms = []ocispecs.Platform{bopts.BuildPlatform}
337+
convertOpt.BuildPlatforms = bopts.BuildPlatforms
338338
convertOpt.TargetPlatforms = bopts.Platforms
339339
convertOpt.BuildArgs = bopts.BuildArgs
340340
convertOpt.Labels = bopts.Labels

pkg/build/frontend_test.go

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func TestResolveStates(t *testing.T) {
351351
tests := []struct {
352352
name string
353353
dockerfile string
354-
buildPlatform ocispecs.Platform
354+
buildPlatforms []ocispecs.Platform
355355
targetPlatform ocispecs.Platform
356356
buildArgs map[string]string
357357
target string
@@ -365,7 +365,7 @@ func TestResolveStates(t *testing.T) {
365365
name: "invalid dockerfile syntax should error",
366366
dockerfile: `INVALID INSTRUCTION
367367
FROM alpine:latest`,
368-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
368+
buildPlatforms: []ocispecs.Platform{{OS: "linux", Architecture: "amd64"}},
369369
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
370370
buildArgs: map[string]string{},
371371
target: "",
@@ -378,7 +378,7 @@ FROM alpine:latest`,
378378
name: "FROM with invalid platform should error",
379379
dockerfile: `FROM --platform=not-a-valid-platform alpine:latest
380380
RUN echo "hello"`,
381-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
381+
buildPlatforms: []ocispecs.Platform{{OS: "linux", Architecture: "amd64"}},
382382
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
383383
buildArgs: map[string]string{},
384384
target: "",
@@ -391,7 +391,7 @@ RUN echo "hello"`,
391391
name: "FROM with invalid platform from build arg should error",
392392
dockerfile: `FROM --platform=$BAD_PLATFORM alpine:latest
393393
RUN echo "hello"`,
394-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
394+
buildPlatforms: []ocispecs.Platform{{OS: "linux", Architecture: "amd64"}},
395395
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
396396
buildArgs: map[string]string{"BAD_PLATFORM": "not-a-valid-platform"},
397397
target: "",
@@ -405,7 +405,7 @@ RUN echo "hello"`,
405405
name: "FROM scratch with platform should be skipped",
406406
dockerfile: `FROM --platform=linux/arm64 scratch
407407
COPY app /app`,
408-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
408+
buildPlatforms: []ocispecs.Platform{{OS: "linux", Architecture: "amd64"}},
409409
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
410410
buildArgs: map[string]string{},
411411
target: "",
@@ -418,7 +418,7 @@ COPY app /app`,
418418
name: "FROM context with platform variables",
419419
dockerfile: `FROM --platform=$BUILDPLATFORM context
420420
COPY app /app`,
421-
buildPlatform: ocispecs.Platform{OS: "darwin", Architecture: "arm64"},
421+
buildPlatforms: []ocispecs.Platform{{OS: "darwin", Architecture: "arm64"}},
422422
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
423423
buildArgs: map[string]string{},
424424
target: "",
@@ -431,7 +431,7 @@ COPY app /app`,
431431
name: "FROM with build args overriding platform vars (context)",
432432
dockerfile: `FROM --platform=$CUSTOM_PLATFORM context
433433
COPY app /app`,
434-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
434+
buildPlatforms: []ocispecs.Platform{{OS: "linux", Architecture: "amd64"}},
435435
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
436436
buildArgs: map[string]string{"CUSTOM_PLATFORM": "windows/amd64"},
437437
target: "",
@@ -447,7 +447,7 @@ COPY app /build
447447
448448
FROM --platform=$TARGETPLATFORM context
449449
COPY --from=build /build /app`,
450-
buildPlatform: ocispecs.Platform{OS: "darwin", Architecture: "arm64"},
450+
buildPlatforms: []ocispecs.Platform{{OS: "darwin", Architecture: "arm64"}},
451451
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
452452
buildArgs: map[string]string{},
453453
target: "",
@@ -461,7 +461,7 @@ COPY --from=build /build /app`,
461461
name: "FROM alpine with TARGETPLATFORM",
462462
dockerfile: `FROM --platform=$TARGETPLATFORM alpine:latest
463463
RUN echo "hello"`,
464-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
464+
buildPlatforms: []ocispecs.Platform{{OS: "linux", Architecture: "amd64"}},
465465
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "arm64"},
466466
buildArgs: map[string]string{},
467467
target: "",
@@ -478,7 +478,7 @@ RUN echo "hello"`,
478478
name: "FROM nginx with BUILDPLATFORM",
479479
dockerfile: `FROM --platform=$BUILDPLATFORM nginx:alpine
480480
RUN echo "hello"`,
481-
buildPlatform: ocispecs.Platform{OS: "darwin", Architecture: "arm64"},
481+
buildPlatforms: []ocispecs.Platform{{OS: "darwin", Architecture: "arm64"}},
482482
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
483483
buildArgs: map[string]string{},
484484
target: "",
@@ -495,7 +495,10 @@ RUN echo "hello"`,
495495
name: "FROM with build args overriding platform vars",
496496
dockerfile: `FROM --platform=$CUSTOM_PLATFORM redis:alpine
497497
RUN echo "hello"`,
498-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
498+
buildPlatforms: []ocispecs.Platform{
499+
{OS: "linux", Architecture: "amd64"},
500+
{OS: "linux", Architecture: "arm64"},
501+
},
499502
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
500503
buildArgs: map[string]string{"CUSTOM_PLATFORM": "windows/amd64"},
501504
target: "",
@@ -512,7 +515,10 @@ RUN echo "hello"`,
512515
name: "FROM with platform variant",
513516
dockerfile: `FROM --platform=linux/arm/v7 node:18-alpine
514517
RUN npm install`,
515-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
518+
buildPlatforms: []ocispecs.Platform{
519+
{OS: "linux", Architecture: "amd64"},
520+
{OS: "darwin", Architecture: "arm64"},
521+
},
516522
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
517523
buildArgs: map[string]string{},
518524
target: "",
@@ -530,7 +536,7 @@ RUN npm install`,
530536
dockerfile: `ARG BASE_IMAGE=postgres:13
531537
FROM --platform=$TARGETPLATFORM $BASE_IMAGE
532538
RUN echo "hello"`,
533-
buildPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
539+
buildPlatforms: []ocispecs.Platform{{OS: "linux", Architecture: "amd64"}},
534540
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "arm64"},
535541
buildArgs: map[string]string{"BASE_IMAGE": "mysql:8.0"},
536542
target: "",
@@ -550,7 +556,7 @@ RUN go build -o app main.go
550556
551557
FROM --platform=$TARGETPLATFORM alpine:latest
552558
COPY --from=builder /app /app`,
553-
buildPlatform: ocispecs.Platform{OS: "darwin", Architecture: "arm64"},
559+
buildPlatforms: []ocispecs.Platform{{OS: "darwin", Architecture: "arm64"}},
554560
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
555561
buildArgs: map[string]string{},
556562
target: "",
@@ -567,6 +573,54 @@ COPY --from=builder /app /app`,
567573
expectedStates: 2,
568574
wantErr: false,
569575
},
576+
{
577+
name: "Multiple build platforms - uses first",
578+
dockerfile: `FROM --platform=$BUILDPLATFORM alpine:latest
579+
RUN echo "hello"`,
580+
buildPlatforms: []ocispecs.Platform{
581+
{OS: "linux", Architecture: "amd64"},
582+
{OS: "linux", Architecture: "arm64"},
583+
},
584+
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "amd64"},
585+
buildArgs: map[string]string{},
586+
target: "",
587+
expectedResolverCalls: []testResolverCall{
588+
{
589+
ref: "alpine:latest",
590+
platform: &ocispecs.Platform{OS: "linux", Architecture: "amd64"}, // Should use first BUILDPLATFORM
591+
},
592+
},
593+
expectedStates: 1,
594+
wantErr: false,
595+
},
596+
{
597+
name: "Multiple build platforms with cross-compilation",
598+
dockerfile: `FROM --platform=$BUILDPLATFORM golang:1.19 AS builder
599+
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -o app main.go
600+
601+
FROM --platform=$TARGETPLATFORM alpine:latest
602+
COPY --from=builder /app /app`,
603+
buildPlatforms: []ocispecs.Platform{
604+
{OS: "linux", Architecture: "amd64"},
605+
{OS: "darwin", Architecture: "arm64"},
606+
{OS: "windows", Architecture: "amd64"},
607+
},
608+
targetPlatform: ocispecs.Platform{OS: "linux", Architecture: "arm64"},
609+
buildArgs: map[string]string{},
610+
target: "",
611+
expectedResolverCalls: []testResolverCall{
612+
{
613+
ref: "golang:1.19",
614+
platform: &ocispecs.Platform{OS: "linux", Architecture: "amd64"}, // Should use first BUILDPLATFORM
615+
},
616+
{
617+
ref: "alpine:latest",
618+
platform: &ocispecs.Platform{OS: "linux", Architecture: "arm64"}, // Should use TARGETPLATFORM
619+
},
620+
},
621+
expectedStates: 2,
622+
wantErr: false,
623+
},
570624
}
571625

572626
for _, tt := range tests {
@@ -583,11 +637,11 @@ COPY --from=builder /app /app`,
583637

584638
// Create BOpts with the intercepting resolver
585639
bopts := &BOpts{
586-
Dockerfile: []byte(tt.dockerfile),
587-
BuildPlatform: tt.buildPlatform,
588-
BuildArgs: tt.buildArgs,
589-
Target: tt.target,
590-
Resolver: interceptor.ResolverProxy,
640+
Dockerfile: []byte(tt.dockerfile),
641+
BuildPlatforms: tt.buildPlatforms,
642+
BuildArgs: tt.buildArgs,
643+
Target: tt.target,
644+
Resolver: interceptor.ResolverProxy,
591645
}
592646

593647
clog := func(format string, params ...any) {

0 commit comments

Comments
 (0)