Skip to content

Commit

Permalink
Merge pull request #255 from paketo-buildpacks/fix-195-no-fallback
Browse files Browse the repository at this point in the history
Fix #195 --no-fallback by default
  • Loading branch information
dmikusa authored May 8, 2023
2 parents bafda9a + 5cd1610 commit 3c46566
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 7 deletions.
18 changes: 12 additions & 6 deletions native/native_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"bytes"
"crypto/sha256"
"fmt"
"github.com/paketo-buildpacks/native-image/v5/native/slices"
"io"
"io/ioutil"
"os"
Expand Down Expand Up @@ -69,9 +70,14 @@ func (n NativeImage) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to process arguments\n%w", err)
}

if !slices.Contains(arguments, "--auto-fallback") && !slices.Contains(arguments, "--force-fallback") {
arguments = append([]string{"--no-fallback"}, arguments...)
}

moduleVar := "USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM"
if _, set := os.LookupEnv(moduleVar); !set{
if err := os.Setenv(moduleVar, "false"); err != nil{
if _, set := os.LookupEnv(moduleVar); !set {
if err := os.Setenv(moduleVar, "false"); err != nil {
n.Logger.Bodyf("unable to set %s for GraalVM 22.2, if your build fails, you may need to set this manually at build time", moduleVar)
}
}
Expand All @@ -88,10 +94,10 @@ func (n NativeImage) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
nativeBinaryHash := fmt.Sprintf("%x", sha256.Sum256(buf.Bytes()))

contributor := libpak.NewLayerContributor("Native Image", map[string]interface{}{
"files": files,
"arguments": arguments,
"compression": n.Compressor,
"version-hash": nativeBinaryHash,
"files": files,
"arguments": arguments,
"compression": n.Compressor,
"version-hash": nativeBinaryHash,
}, libcnb.LayerTypes{
Cache: true,
})
Expand Down
101 changes: 100 additions & 1 deletion native/native_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {

executor.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
return e.Command == "native-image" &&
(e.Args[0] == "test-argument-1" || (e.Args[0] == "-H:+StaticExecutableWithDynamicLibC" && e.Args[1] == "test-argument-1"))
(e.Args[0] == "--no-fallback" || (e.Args[1] == "-H:+StaticExecutableWithDynamicLibC" && e.Args[0] == "--no-fallback"))
})).Run(func(args mock.Arguments) {
exec := args.Get(0).(effect.Execution)
lastArg := exec.Args[len(exec.Args)-1]
Expand Down Expand Up @@ -127,6 +127,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {

execution := executor.Calls[1].Arguments[0].(effect.Execution)
Expect(execution.Args).To(Equal([]string{
"--no-fallback",
"test-argument-1",
"test-argument-2",
fmt.Sprintf("-H:Name=%s", filepath.Join(layer.Path, "test-start-class")),
Expand All @@ -143,6 +144,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {

execution := executor.Calls[1].Arguments[0].(effect.Execution)
Expect(execution.Args).To(Equal([]string{
"--no-fallback",
"test-argument-1",
"test-argument-2",
fmt.Sprintf("-H:Name=%s", filepath.Join(layer.Path, "test-start-class")),
Expand Down Expand Up @@ -170,6 +172,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {

execution := executor.Calls[1].Arguments[0].(effect.Execution)
Expect(execution.Args).To(Equal([]string{
"--no-fallback",
fmt.Sprintf("@%s", argsFile),
fmt.Sprintf("-H:Name=%s", filepath.Join(layer.Path, "test-start-class")),
"-cp",
Expand All @@ -182,6 +185,100 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
})
})

context("user opts out of --no-fallback", func() {
var err error

it("contributes native image with --force-fallback", func() {
executorForceFallback := &mocks.Executor{}
nativeImage, err = native.NewNativeImage(ctx.Application.Path, "--force-fallback test-argument-1 test-argument-2", "", "none", "", props, ctx.StackID)
nativeImage.Logger = bard.NewLogger(io.Discard)
Expect(err).NotTo(HaveOccurred())
nativeImage.Executor = executorForceFallback

executorForceFallback.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
return e.Command == "native-image" && len(e.Args) == 1 && e.Args[0] == "--version"
})).Run(func(args mock.Arguments) {
exec := args.Get(0).(effect.Execution)
_, err := exec.Stdout.Write([]byte("1.2.3"))
Expect(err).To(Succeed())
}).Return(nil)

executorForceFallback.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
return e.Command == "native-image" &&
(e.Args[0] == "--force-fallback" || (e.Args[1] == "-H:+StaticExecutableWithDynamicLibC" && e.Args[0] == "--force-fallback"))
})).Run(func(args mock.Arguments) {
exec := args.Get(0).(effect.Execution)
lastArg := exec.Args[len(exec.Args)-1]
Expect(ioutil.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
}).Return(nil)

layer, err = ctx.Layers.Layer("test-layer")
Expect(err).NotTo(HaveOccurred())

_, err := nativeImage.Contribute(layer)
Expect(err).NotTo(HaveOccurred())

execution := executorForceFallback.Calls[1].Arguments[0].(effect.Execution)
Expect(execution.Args).To(Equal([]string{
"--force-fallback",
"test-argument-1",
"test-argument-2",
fmt.Sprintf("-H:Name=%s", filepath.Join(layer.Path, "test-start-class")),
"-cp",
strings.Join([]string{
ctx.Application.Path,
"manifest-class-path",
}, ":"),
"test-start-class",
}))
})

it("contributes native image with --auto-fallback", func() {
executorAutoFallback := &mocks.Executor{}
nativeImage, err = native.NewNativeImage(ctx.Application.Path, "--auto-fallback test-argument-1 test-argument-2", "", "none", "", props, ctx.StackID)
nativeImage.Logger = bard.NewLogger(io.Discard)
Expect(err).NotTo(HaveOccurred())
nativeImage.Executor = executorAutoFallback

executorAutoFallback.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
return e.Command == "native-image" && len(e.Args) == 1 && e.Args[0] == "--version"
})).Run(func(args mock.Arguments) {
exec := args.Get(0).(effect.Execution)
_, err := exec.Stdout.Write([]byte("1.2.3"))
Expect(err).To(Succeed())
}).Return(nil)

executorAutoFallback.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
return e.Command == "native-image" &&
(e.Args[0] == "--auto-fallback" || (e.Args[1] == "-H:+StaticExecutableWithDynamicLibC" && e.Args[0] == "--auto-fallback"))
})).Run(func(args mock.Arguments) {
exec := args.Get(0).(effect.Execution)
lastArg := exec.Args[len(exec.Args)-1]
Expect(ioutil.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
}).Return(nil)

layer, err = ctx.Layers.Layer("test-layer")
Expect(err).NotTo(HaveOccurred())

_, err := nativeImage.Contribute(layer)
Expect(err).NotTo(HaveOccurred())

execution := executorAutoFallback.Calls[1].Arguments[0].(effect.Execution)
Expect(execution.Args).To(Equal([]string{
"--auto-fallback",
"test-argument-1",
"test-argument-2",
fmt.Sprintf("-H:Name=%s", filepath.Join(layer.Path, "test-start-class")),
"-cp",
strings.Join([]string{
ctx.Application.Path,
"manifest-class-path",
}, ":"),
"test-start-class",
}))
})
})

context("Not a Spring Boot app", func() {
it.Before(func() {
// there won't be a Start-Class
Expand All @@ -198,6 +295,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {

execution := executor.Calls[1].Arguments[0].(effect.Execution)
Expect(execution.Args).To(Equal([]string{
"--no-fallback",
"test-argument-1",
"test-argument-2",
fmt.Sprintf("-H:Name=%s", filepath.Join(layer.Path, "test-main-class")),
Expand Down Expand Up @@ -291,6 +389,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
execution := executor.Calls[1].Arguments[0].(effect.Execution)
Expect(execution.Command).To(Equal("native-image"))
Expect(execution.Args).To(Equal([]string{
"--no-fallback",
"-H:+StaticExecutableWithDynamicLibC",
"test-argument-1",
"test-argument-2",
Expand Down
48 changes: 48 additions & 0 deletions native/slices/slices.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package slices

// Those 2 functions were copied from https://cs.opensource.google/go/x/exp/+/master:slices/slices.go ,
// an experimental collection of utility methods that could be promoted to stdlib in the future

//Copyright (c) 2009 The Go Authors. All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions are
//met:
//
// * Redistributions of source code must retain the above copyright
//notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
//copyright notice, this list of conditions and the following disclaimer
//in the documentation and/or other materials provided with the
//distribution.
// * Neither the name of Google Inc. nor the names of its
//contributors may be used to endorse or promote products derived from
//this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
//A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
//OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
//SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
//LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
//THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
//OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Index returns the index of the first occurrence of v in s,
// or -1 if not present.
func Index[E comparable](s []E, v E) int {
for i := range s {
if v == s[i] {
return i
}
}
return -1
}

// Contains reports whether v is present in s.
func Contains[E comparable](s []E, v E) bool {
return Index(s, v) >= 0
}

0 comments on commit 3c46566

Please sign in to comment.