diff --git a/frontend/dockerfile/dockerfile2llb/convert.go b/frontend/dockerfile/dockerfile2llb/convert.go index 88e5943ebdcc..e484e5a9a7c5 100644 --- a/frontend/dockerfile/dockerfile2llb/convert.go +++ b/frontend/dockerfile/dockerfile2llb/convert.go @@ -468,9 +468,11 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS if d.base == nil && !d.dispatched && !d.resolved { d.resolved = reachable // avoid re-resolving if called again after onbuild if d.stage.BaseName == emptyImageName { - d.state = llb.Scratch() - d.image = emptyImage(platformOpt.targetPlatform) - d.platform = &platformOpt.targetPlatform + if d.platform == nil { + d.platform = &platformOpt.targetPlatform + } + d.state = llb.Scratch().Platform(*d.platform) + d.image = emptyImage(*d.platform) if d.unregistered { d.dispatched = true } @@ -511,7 +513,7 @@ func toDispatchState(ctx context.Context, dt []byte, opt ConvertOpt) (*dispatchS if img != nil { d.image = *img } else { - d.image = emptyImage(platformOpt.targetPlatform) + d.image = emptyImage(*platform) } d.state = st.Platform(*platform) d.platform = platform diff --git a/frontend/dockerfile/dockerfile2llb/convert_test.go b/frontend/dockerfile/dockerfile2llb/convert_test.go index 80f4d895fb72..32260f26f2d9 100644 --- a/frontend/dockerfile/dockerfile2llb/convert_test.go +++ b/frontend/dockerfile/dockerfile2llb/convert_test.go @@ -9,6 +9,7 @@ import ( "github.com/moby/buildkit/frontend/dockerui" "github.com/moby/buildkit/util/appcontext" digest "github.com/opencontainers/go-digest" + ocispecs "github.com/opencontainers/image-spec/specs-go/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -224,9 +225,43 @@ RUN echo foo FROM foo AS bar RUN echo bar ` - _, _, baseImg, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{}) + state, _, baseImg, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{TargetPlatform: &ocispecs.Platform{OS: "targetos", Architecture: "targetarch"}}) require.NoError(t, err) + + platform, err := state.GetPlatform(context.TODO()) + require.NoError(t, err) + assert.Equal(t, "linux", platform.OS) + assert.Equal(t, "amd64", platform.Architecture) + t.Logf("baseImg=%+v", baseImg) + assert.Equal(t, "linux", baseImg.Platform.OS) + assert.Equal(t, "amd64", baseImg.Platform.Architecture) assert.Equal(t, []digest.Digest{"sha256:2e112031b4b923a873c8b3d685d48037e4d5ccd967b658743d93a6e56c3064b9"}, baseImg.RootFS.DiffIDs) assert.Equal(t, "2024-01-17 21:49:12 +0000 UTC", baseImg.Created.String()) } + +func TestBaseImageScratchPlatform(t *testing.T) { + // Defaults to targetplatform + df := `FROM scratch` + state, _, baseImg, _, err := Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{TargetPlatform: &ocispecs.Platform{OS: "targetos", Architecture: "targetarch"}}) + require.NoError(t, err) + + platform, err := state.GetPlatform(context.TODO()) + require.NoError(t, err) + assert.Equal(t, "targetos", platform.OS) + assert.Equal(t, "targetarch", platform.Architecture) + + assert.Nil(t, baseImg) + + // Cross-compilation scenario + df = `FROM --platform=linux/ppc64le scratch` + state, _, baseImg, _, err = Dockerfile2LLB(appcontext.Context(), []byte(df), ConvertOpt{TargetPlatform: &ocispecs.Platform{OS: "targetos", Architecture: "targetarch"}}) + require.NoError(t, err) + + platform, err = state.GetPlatform(context.TODO()) + require.NoError(t, err) + assert.Equal(t, "linux", platform.OS) + assert.Equal(t, "ppc64le", platform.Architecture) + + assert.Nil(t, baseImg) +} diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 33cafc9e1b1c..26fe2dad3423 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -109,6 +109,7 @@ var allTests = integration.TestFuncs( testHTTPDockerfile, testPlatformArgsImplicit, testPlatformArgsExplicit, + testPlatformArgsExplicitContext, testExportMultiPlatform, testQuotedMetaArgs, testGlobalArgErrors, @@ -6310,6 +6311,61 @@ COPY --from=build out . require.Equal(t, "freebsd", string(dt)) } +func testPlatformArgsExplicitContext(t *testing.T, sb integration.Sandbox) { + integration.SkipOnPlatform(t, "windows") + f := getFrontend(t, sb) + + dockerfile := []byte(` +FROM --platform=$BUILDPLATFORM scratch AS build +COPY --from=bb . / +ARG TARGETPLATFORM +ARG TARGETOS +RUN mkdir /out && echo -n $TARGETPLATFORM > /out/platform && echo -n $TARGETOS > /out/os +FROM scratch +COPY --from=build out . +`) + + dir := integration.Tmpdir( + t, + fstest.CreateFile("Dockerfile", dockerfile, 0600), + ) + + c, err := client.New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + destDir := t.TempDir() + + opt := client.SolveOpt{ + Exports: []client.ExportEntry{ + { + Type: client.ExporterLocal, + OutputDir: destDir, + }, + }, + FrontendAttrs: map[string]string{ + "platform": "darwin/ppc64le", + "build-arg:TARGETOS": "freebsd", + "context:bb": "docker-image://busybox", + }, + LocalMounts: map[string]fsutil.FS{ + dockerui.DefaultLocalNameDockerfile: dir, + dockerui.DefaultLocalNameContext: dir, + }, + } + + _, err = f.Solve(sb.Context(), c, opt, nil) + require.NoError(t, err) + + dt, err := os.ReadFile(filepath.Join(destDir, "platform")) + require.NoError(t, err) + require.Equal(t, "darwin/ppc64le", string(dt)) + + dt, err = os.ReadFile(filepath.Join(destDir, "os")) + require.NoError(t, err) + require.Equal(t, "freebsd", string(dt)) +} + func testBuiltinArgs(t *testing.T, sb integration.Sandbox) { f := getFrontend(t, sb)