Skip to content

Commit

Permalink
Merge pull request #329 from paketo-buildpacks/gh_308
Browse files Browse the repository at this point in the history
Also copy .so files when doing a native-image build
  • Loading branch information
dmikusa authored Jul 15, 2024
2 parents 78e7383 + 969834f commit 30ed23c
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 68 deletions.
10 changes: 4 additions & 6 deletions native/arguments.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package native

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
Expand Down Expand Up @@ -82,20 +81,20 @@ type UserFileArguments struct {
// Configure returns the inputArgs plus the additional arguments provided via argfile, setting via the '@argfile' format
func (u UserFileArguments) Configure(inputArgs []string) ([]string, string, error) {

rawArgs, err := ioutil.ReadFile(u.ArgumentsFile)
rawArgs, err := os.ReadFile(u.ArgumentsFile)
if err != nil {
return []string{}, "", fmt.Errorf("read arguments from %s\n%w", u.ArgumentsFile, err)
}

fileArgs := strings.Split(string(rawArgs), "\n")
if len(fileArgs) == 1{
if len(fileArgs) == 1 {
fileArgs = strings.Split(string(rawArgs), " ")
}

if containsArg("-jar", fileArgs) {
fileArgs = replaceJarArguments(fileArgs)
newArgList := strings.Join(fileArgs, " ")
if err = os.WriteFile(u.ArgumentsFile,[]byte(newArgList),0644); err != nil{
if err = os.WriteFile(u.ArgumentsFile, []byte(newArgList), 0644); err != nil {
return []string{}, "", fmt.Errorf("unable to write to arguments file %s\n%w", u.ArgumentsFile, err)
}
}
Expand All @@ -106,7 +105,6 @@ func (u UserFileArguments) Configure(inputArgs []string) ([]string, string, erro

}


// containsArg checks if needle is found in haystack
//
// needle and haystack entries are processed as key=val strings where only the key must match
Expand Down Expand Up @@ -230,4 +228,4 @@ func replaceJarArguments(fileArgs []string) []string {
modifiedArgs = append(modifiedArgs, inputArg)
}
return modifiedArgs
}
}
32 changes: 13 additions & 19 deletions native/arguments_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package native_test

import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
Expand All @@ -40,13 +39,8 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
)

it.Before(func() {
var err error

ctx.Application.Path, err = ioutil.TempDir("", "native-image-application")
Expect(err).NotTo(HaveOccurred())

ctx.Layers.Path, err = ioutil.TempDir("", "native-image-layers")
Expect(err).NotTo(HaveOccurred())
ctx.Application.Path = t.TempDir()
ctx.Layers.Path = t.TempDir()
})

it.After(func() {
Expand Down Expand Up @@ -126,10 +120,10 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
context("user arguments from file", func() {
it.Before(func() {
Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "target"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff.txt"), []byte("more stuff"), 0644)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff-quotes.txt"), []byte(`before -jar "more stuff.jar" after -other="my path"`), 0644)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt"), []byte(`stuff -jar stuff.jar after`), 0644)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "override.txt"), []byte(`one=output`), 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff.txt"), []byte("more stuff"), 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff-quotes.txt"), []byte(`before -jar "more stuff.jar" after -other="my path"`), 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt"), []byte(`stuff -jar stuff.jar after`), 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "override.txt"), []byte(`one=output`), 0644)).To(Succeed())
})

it("has none", func() {
Expand All @@ -146,7 +140,7 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
Expect(err).ToNot(HaveOccurred())
Expect(startClass).To(Equal(""))
Expect(args).To(HaveLen(4))
Expect(args).To(Equal([]string{"one", "two", "three", fmt.Sprintf("@%s",filepath.Join(ctx.Application.Path,"target/more-stuff.txt"))}))
Expect(args).To(Equal([]string{"one", "two", "three", fmt.Sprintf("@%s", filepath.Join(ctx.Application.Path, "target/more-stuff.txt"))}))
})

it("works with quotes in the file", func() {
Expand All @@ -158,7 +152,7 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
Expect(startClass).To(Equal(""))
Expect(args).To(HaveLen(4))
Expect(args).To(Equal([]string{"one", "two", "three", fmt.Sprintf("@%s", filepath.Join(ctx.Application.Path, "target/more-stuff-quotes.txt"))}))
bits, err := ioutil.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-quotes.txt"))
bits, err := os.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-quotes.txt"))
Expect(err).ToNot(HaveOccurred())
Expect(string(bits)).To(Equal("before after -other=\"my path\""))
})
Expand All @@ -170,9 +164,9 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
Expect(err).ToNot(HaveOccurred())
Expect(args).To(HaveLen(1))
Expect(args).To(Equal([]string{
fmt.Sprintf("@%s",filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt")),
fmt.Sprintf("@%s", filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt")),
}))
bits, err := ioutil.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-class.txt"))
bits, err := os.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-class.txt"))
Expect(err).ToNot(HaveOccurred())
Expect(string(bits)).To(Equal("after"))
})
Expand Down Expand Up @@ -254,9 +248,9 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
context("jar file", func() {
it.Before(func() {
Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "target"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "found.jar"), []byte{}, 0644)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "a.two"), []byte{}, 0644)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "b.two"), []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "found.jar"), []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "a.two"), []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "b.two"), []byte{}, 0644)).To(Succeed())
})

it("adds arguments", func() {
Expand Down
3 changes: 2 additions & 1 deletion native/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ package native
import (
"errors"
"fmt"
"github.com/paketo-buildpacks/libpak/sherpa"
"os"
"path/filepath"

"github.com/paketo-buildpacks/libpak/sherpa"

"github.com/paketo-buildpacks/libpak/effect"
"github.com/paketo-buildpacks/libpak/sbom"

Expand Down
64 changes: 44 additions & 20 deletions native/native_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,12 @@ import (
"bytes"
"crypto/sha256"
"fmt"
"github.com/paketo-buildpacks/native-image/v5/native/slices"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"

"github.com/paketo-buildpacks/native-image/v5/native/slices"

"github.com/buildpacks/libcnb"
"github.com/magiconair/properties"
"github.com/paketo-buildpacks/libpak"
Expand Down Expand Up @@ -143,7 +142,7 @@ func (n NativeImage) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
}

n.Logger.Header("Removing bytecode")
cs, err := ioutil.ReadDir(n.ApplicationPath)
cs, err := os.ReadDir(n.ApplicationPath)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to list children of %s\n%w", n.ApplicationPath, err)
}
Expand All @@ -154,22 +153,8 @@ func (n NativeImage) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
}
}

src := filepath.Join(layer.Path, startClass)
in, err := os.Open(src)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to open %s\n%w", filepath.Join(layer.Path, startClass), err)
}
defer in.Close()

dst := filepath.Join(n.ApplicationPath, startClass)
out, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0755)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to open %s\n%w", dst, err)
}
defer out.Close()

if _, err := io.Copy(out, in); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to copy\n%w", err)
if err := copyFilesFromLayer(layer.Path, startClass, n.ApplicationPath); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to copy files from layer\n%w", err)
}

return layer, nil
Expand Down Expand Up @@ -225,3 +210,42 @@ func (n NativeImage) ProcessArguments(layer libcnb.Layer) ([]string, string, err
func (NativeImage) Name() string {
return "native-image"
}

// copy the main file & any `*.so` files also in the layer to the application path
func copyFilesFromLayer(layerPath string, execName string, appPath string) error {
files, err := os.ReadDir(layerPath)
if err != nil {
return fmt.Errorf("unable to list files on layer %s\n%w", layerPath, err)
}

for _, file := range files {
if file.Type().IsRegular() && (file.Name() == execName) {
src := filepath.Join(layerPath, file.Name())
dst := filepath.Join(appPath, file.Name())

if err := copyFile(src, dst); err != nil {
return fmt.Errorf("unable to copy %s to %s\n%w", src, dst, err)
}
}
if file.Type().IsRegular() && (strings.HasSuffix(file.Name(), ".so")) {
src := filepath.Join(layerPath, file.Name())
dst := filepath.Join(appPath, file.Name())

if err := copyFile(src, dst); err != nil {
return fmt.Errorf("unable to copy %s to %s\n%w", src, dst, err)
}
}
}

return nil
}

func copyFile(src string, dst string) error {
in, err := os.Open(src)
if err != nil {
return fmt.Errorf("unable to open %s\n%w", src, err)
}
defer in.Close()

return sherpa.CopyFile(in, dst)
}
61 changes: 39 additions & 22 deletions native/native_image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package native_test
import (
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -50,27 +49,22 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
)

it.Before(func() {
var err error

ctx.Application.Path, err = ioutil.TempDir("", "native-image-application")
Expect(err).NotTo(HaveOccurred())

ctx.Layers.Path, err = ioutil.TempDir("", "native-image-layers")
Expect(err).NotTo(HaveOccurred())
ctx.Application.Path = t.TempDir()
ctx.Layers.Path = t.TempDir()

executor = &mocks.Executor{}

props = properties.NewProperties()

_, _, err = props.Set("Start-Class", "test-start-class")
_, _, err := props.Set("Start-Class", "test-start-class")
Expect(err).NotTo(HaveOccurred())
_, _, err = props.Set("Class-Path", "manifest-class-path")
Expect(err).NotTo(HaveOccurred())

Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "fixture-marker"), []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "fixture-marker"), []byte{}, 0644)).To(Succeed())
Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "BOOT-INF"), 0755)).To(Succeed())
Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "META-INF"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "META-INF", "MANIFEST.MF"), []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "META-INF", "MANIFEST.MF"), []byte{}, 0644)).To(Succeed())

nativeImage, err = native.NewNativeImage(ctx.Application.Path, "test-argument-1 test-argument-2", "", "none", "", props, ctx.StackID)
nativeImage.Logger = bard.NewLogger(io.Discard)
Expand All @@ -91,7 +85,9 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
})).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())
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0755)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt.so"), []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt_headless.so"), []byte{}, 0644)).To(Succeed())
}).Return(nil)

executor.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
Expand All @@ -100,7 +96,9 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
})).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())
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0755)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt.so"), []byte{}, 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt_headless.so"), []byte{}, 0644)).To(Succeed())
}).Return(nil)

layer, err = ctx.Layers.Layer("test-layer")
Expand Down Expand Up @@ -134,6 +132,25 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
"-cp", "some-classpath",
"test-start-class",
}))

Expect(filepath.Join(ctx.Application.Path, "BOOT-INF")).ToNot(BeADirectory())
Expect(filepath.Join(ctx.Application.Path, "META-INF")).ToNot(BeADirectory())

Expect(filepath.Join(layer.Path, "test-start-class")).To(BeARegularFile())
Expect(filepath.Join(layer.Path, "libawt.so")).To(BeARegularFile())
Expect(filepath.Join(layer.Path, "libawt_headless.so")).To(BeARegularFile())

info, err := os.Stat(filepath.Join(layer.Path, "test-start-class"))
Expect(err).NotTo(HaveOccurred())
fmt.Println("info.Mode().Perm(): ", info.Mode().Perm().String())
Expect(info.Mode().Perm()).To(Equal(os.FileMode(0755)))

Expect(filepath.Join(ctx.Application.Path, "test-start-class")).To(BeARegularFile())
Expect(filepath.Join(ctx.Application.Path, "libawt.so")).To(BeARegularFile())
Expect(filepath.Join(ctx.Application.Path, "libawt_headless.so")).To(BeARegularFile())
info, err = os.Stat(filepath.Join(ctx.Application.Path, "test-start-class"))
Expect(err).NotTo(HaveOccurred())
Expect(info.Mode().Perm()).To(Equal(os.FileMode(0755)))
})
})

Expand All @@ -160,7 +177,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
it("contributes native image with Class-Path from manifest and args from a file", func() {
argsFile := filepath.Join(ctx.Application.Path, "target", "args.txt")
Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "target"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(argsFile, []byte(`test-argument-1 test-argument-2`), 0644)).To(Succeed())
Expect(os.WriteFile(argsFile, []byte(`test-argument-1 test-argument-2`), 0644)).To(Succeed())

nativeImage, err := native.NewNativeImage(ctx.Application.Path, "", argsFile, "none", "", props, ctx.StackID)
nativeImage.Logger = bard.NewLogger(io.Discard)
Expand Down Expand Up @@ -209,7 +226,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
})).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())
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
}).Return(nil)

layer, err = ctx.Layers.Layer("test-layer")
Expand Down Expand Up @@ -254,7 +271,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
})).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())
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
}).Return(nil)

layer, err = ctx.Layers.Layer("test-layer")
Expand Down Expand Up @@ -316,7 +333,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 == "upx"
})).Run(func(args mock.Arguments) {
Expect(ioutil.WriteFile(filepath.Join(layer.Path, "test-start-class"), []byte("upx-compressed"), 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layer.Path, "test-start-class"), []byte("upx-compressed"), 0644)).To(Succeed())
}).Return(nil)

_, err := nativeImage.Contribute(layer)
Expand All @@ -331,7 +348,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
bin := filepath.Join(layer.Path, "test-start-class")
Expect(bin).To(BeARegularFile())

data, err := ioutil.ReadFile(bin)
data, err := os.ReadFile(bin)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(ContainSubstring("upx-compressed"))
})
Expand All @@ -344,8 +361,8 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
executor.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
return e.Command == "gzexe"
})).Run(func(args mock.Arguments) {
Expect(ioutil.WriteFile(filepath.Join(layer.Path, "test-start-class"), []byte("gzexe-compressed"), 0644)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(layer.Path, "test-start-class~"), []byte("original"), 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layer.Path, "test-start-class"), []byte("gzexe-compressed"), 0644)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layer.Path, "test-start-class~"), []byte("original"), 0644)).To(Succeed())
}).Return(nil)

_, err := nativeImage.Contribute(layer)
Expand All @@ -360,7 +377,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
bin := filepath.Join(layer.Path, "test-start-class")
Expect(bin).To(BeARegularFile())

data, err := ioutil.ReadFile(bin)
data, err := os.ReadFile(bin)
Expect(err).ToNot(HaveOccurred())
Expect(data).To(ContainSubstring("gzexe-compressed"))
Expect(filepath.Join(layer.Path, "test-start-class~")).ToNot(BeAnExistingFile())
Expand All @@ -373,7 +390,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
})

it("contributes a static native image executable with dynamic libc", func() {
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "BOOT-INF", "classpath.idx"), []byte(`
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "BOOT-INF", "classpath.idx"), []byte(`
- "test-jar.jar"
- "spring-graalvm-native-0.8.6-xxxxxx.jar"
`), 0644)).To(Succeed())
Expand Down

0 comments on commit 30ed23c

Please sign in to comment.