Skip to content

Commit 30ed23c

Browse files
authored
Merge pull request #329 from paketo-buildpacks/gh_308
Also copy .so files when doing a native-image build
2 parents 78e7383 + 969834f commit 30ed23c

File tree

5 files changed

+102
-68
lines changed

5 files changed

+102
-68
lines changed

native/arguments.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package native
1818

1919
import (
2020
"fmt"
21-
"io/ioutil"
2221
"os"
2322
"path/filepath"
2423
"sort"
@@ -82,20 +81,20 @@ type UserFileArguments struct {
8281
// Configure returns the inputArgs plus the additional arguments provided via argfile, setting via the '@argfile' format
8382
func (u UserFileArguments) Configure(inputArgs []string) ([]string, string, error) {
8483

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

9089
fileArgs := strings.Split(string(rawArgs), "\n")
91-
if len(fileArgs) == 1{
90+
if len(fileArgs) == 1 {
9291
fileArgs = strings.Split(string(rawArgs), " ")
9392
}
9493

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

107106
}
108107

109-
110108
// containsArg checks if needle is found in haystack
111109
//
112110
// needle and haystack entries are processed as key=val strings where only the key must match
@@ -230,4 +228,4 @@ func replaceJarArguments(fileArgs []string) []string {
230228
modifiedArgs = append(modifiedArgs, inputArg)
231229
}
232230
return modifiedArgs
233-
}
231+
}

native/arguments_test.go

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package native_test
1818

1919
import (
2020
"fmt"
21-
"io/ioutil"
2221
"os"
2322
"path/filepath"
2423
"testing"
@@ -40,13 +39,8 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
4039
)
4140

4241
it.Before(func() {
43-
var err error
44-
45-
ctx.Application.Path, err = ioutil.TempDir("", "native-image-application")
46-
Expect(err).NotTo(HaveOccurred())
47-
48-
ctx.Layers.Path, err = ioutil.TempDir("", "native-image-layers")
49-
Expect(err).NotTo(HaveOccurred())
42+
ctx.Application.Path = t.TempDir()
43+
ctx.Layers.Path = t.TempDir()
5044
})
5145

5246
it.After(func() {
@@ -126,10 +120,10 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
126120
context("user arguments from file", func() {
127121
it.Before(func() {
128122
Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "target"), 0755)).To(Succeed())
129-
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff.txt"), []byte("more stuff"), 0644)).To(Succeed())
130-
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())
131-
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt"), []byte(`stuff -jar stuff.jar after`), 0644)).To(Succeed())
132-
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "override.txt"), []byte(`one=output`), 0644)).To(Succeed())
123+
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff.txt"), []byte("more stuff"), 0644)).To(Succeed())
124+
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())
125+
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt"), []byte(`stuff -jar stuff.jar after`), 0644)).To(Succeed())
126+
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "override.txt"), []byte(`one=output`), 0644)).To(Succeed())
133127
})
134128

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

152146
it("works with quotes in the file", func() {
@@ -158,7 +152,7 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
158152
Expect(startClass).To(Equal(""))
159153
Expect(args).To(HaveLen(4))
160154
Expect(args).To(Equal([]string{"one", "two", "three", fmt.Sprintf("@%s", filepath.Join(ctx.Application.Path, "target/more-stuff-quotes.txt"))}))
161-
bits, err := ioutil.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-quotes.txt"))
155+
bits, err := os.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-quotes.txt"))
162156
Expect(err).ToNot(HaveOccurred())
163157
Expect(string(bits)).To(Equal("before after -other=\"my path\""))
164158
})
@@ -170,9 +164,9 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
170164
Expect(err).ToNot(HaveOccurred())
171165
Expect(args).To(HaveLen(1))
172166
Expect(args).To(Equal([]string{
173-
fmt.Sprintf("@%s",filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt")),
167+
fmt.Sprintf("@%s", filepath.Join(ctx.Application.Path, "target", "more-stuff-class.txt")),
174168
}))
175-
bits, err := ioutil.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-class.txt"))
169+
bits, err := os.ReadFile(filepath.Join(ctx.Application.Path, "target/more-stuff-class.txt"))
176170
Expect(err).ToNot(HaveOccurred())
177171
Expect(string(bits)).To(Equal("after"))
178172
})
@@ -254,9 +248,9 @@ func testArguments(t *testing.T, context spec.G, it spec.S) {
254248
context("jar file", func() {
255249
it.Before(func() {
256250
Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "target"), 0755)).To(Succeed())
257-
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "found.jar"), []byte{}, 0644)).To(Succeed())
258-
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "a.two"), []byte{}, 0644)).To(Succeed())
259-
Expect(ioutil.WriteFile(filepath.Join(ctx.Application.Path, "target", "b.two"), []byte{}, 0644)).To(Succeed())
251+
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "found.jar"), []byte{}, 0644)).To(Succeed())
252+
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "a.two"), []byte{}, 0644)).To(Succeed())
253+
Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "target", "b.two"), []byte{}, 0644)).To(Succeed())
260254
})
261255

262256
it("adds arguments", func() {

native/build.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ package native
1919
import (
2020
"errors"
2121
"fmt"
22-
"github.com/paketo-buildpacks/libpak/sherpa"
2322
"os"
2423
"path/filepath"
2524

25+
"github.com/paketo-buildpacks/libpak/sherpa"
26+
2627
"github.com/paketo-buildpacks/libpak/effect"
2728
"github.com/paketo-buildpacks/libpak/sbom"
2829

native/native_image.go

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ import (
2020
"bytes"
2121
"crypto/sha256"
2222
"fmt"
23-
"github.com/paketo-buildpacks/native-image/v5/native/slices"
24-
"io"
25-
"io/ioutil"
2623
"os"
2724
"path/filepath"
2825
"strings"
2926

27+
"github.com/paketo-buildpacks/native-image/v5/native/slices"
28+
3029
"github.com/buildpacks/libcnb"
3130
"github.com/magiconair/properties"
3231
"github.com/paketo-buildpacks/libpak"
@@ -143,7 +142,7 @@ func (n NativeImage) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
143142
}
144143

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

157-
src := filepath.Join(layer.Path, startClass)
158-
in, err := os.Open(src)
159-
if err != nil {
160-
return libcnb.Layer{}, fmt.Errorf("unable to open %s\n%w", filepath.Join(layer.Path, startClass), err)
161-
}
162-
defer in.Close()
163-
164-
dst := filepath.Join(n.ApplicationPath, startClass)
165-
out, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0755)
166-
if err != nil {
167-
return libcnb.Layer{}, fmt.Errorf("unable to open %s\n%w", dst, err)
168-
}
169-
defer out.Close()
170-
171-
if _, err := io.Copy(out, in); err != nil {
172-
return libcnb.Layer{}, fmt.Errorf("unable to copy\n%w", err)
156+
if err := copyFilesFromLayer(layer.Path, startClass, n.ApplicationPath); err != nil {
157+
return libcnb.Layer{}, fmt.Errorf("unable to copy files from layer\n%w", err)
173158
}
174159

175160
return layer, nil
@@ -225,3 +210,42 @@ func (n NativeImage) ProcessArguments(layer libcnb.Layer) ([]string, string, err
225210
func (NativeImage) Name() string {
226211
return "native-image"
227212
}
213+
214+
// copy the main file & any `*.so` files also in the layer to the application path
215+
func copyFilesFromLayer(layerPath string, execName string, appPath string) error {
216+
files, err := os.ReadDir(layerPath)
217+
if err != nil {
218+
return fmt.Errorf("unable to list files on layer %s\n%w", layerPath, err)
219+
}
220+
221+
for _, file := range files {
222+
if file.Type().IsRegular() && (file.Name() == execName) {
223+
src := filepath.Join(layerPath, file.Name())
224+
dst := filepath.Join(appPath, file.Name())
225+
226+
if err := copyFile(src, dst); err != nil {
227+
return fmt.Errorf("unable to copy %s to %s\n%w", src, dst, err)
228+
}
229+
}
230+
if file.Type().IsRegular() && (strings.HasSuffix(file.Name(), ".so")) {
231+
src := filepath.Join(layerPath, file.Name())
232+
dst := filepath.Join(appPath, file.Name())
233+
234+
if err := copyFile(src, dst); err != nil {
235+
return fmt.Errorf("unable to copy %s to %s\n%w", src, dst, err)
236+
}
237+
}
238+
}
239+
240+
return nil
241+
}
242+
243+
func copyFile(src string, dst string) error {
244+
in, err := os.Open(src)
245+
if err != nil {
246+
return fmt.Errorf("unable to open %s\n%w", src, err)
247+
}
248+
defer in.Close()
249+
250+
return sherpa.CopyFile(in, dst)
251+
}

native/native_image_test.go

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package native_test
1919
import (
2020
"fmt"
2121
"io"
22-
"io/ioutil"
2322
"os"
2423
"path/filepath"
2524
"strings"
@@ -50,27 +49,22 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
5049
)
5150

5251
it.Before(func() {
53-
var err error
54-
55-
ctx.Application.Path, err = ioutil.TempDir("", "native-image-application")
56-
Expect(err).NotTo(HaveOccurred())
57-
58-
ctx.Layers.Path, err = ioutil.TempDir("", "native-image-layers")
59-
Expect(err).NotTo(HaveOccurred())
52+
ctx.Application.Path = t.TempDir()
53+
ctx.Layers.Path = t.TempDir()
6054

6155
executor = &mocks.Executor{}
6256

6357
props = properties.NewProperties()
6458

65-
_, _, err = props.Set("Start-Class", "test-start-class")
59+
_, _, err := props.Set("Start-Class", "test-start-class")
6660
Expect(err).NotTo(HaveOccurred())
6761
_, _, err = props.Set("Class-Path", "manifest-class-path")
6862
Expect(err).NotTo(HaveOccurred())
6963

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

7569
nativeImage, err = native.NewNativeImage(ctx.Application.Path, "test-argument-1 test-argument-2", "", "none", "", props, ctx.StackID)
7670
nativeImage.Logger = bard.NewLogger(io.Discard)
@@ -91,7 +85,9 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
9185
})).Run(func(args mock.Arguments) {
9286
exec := args.Get(0).(effect.Execution)
9387
lastArg := exec.Args[len(exec.Args)-1]
94-
Expect(ioutil.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
88+
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0755)).To(Succeed())
89+
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt.so"), []byte{}, 0644)).To(Succeed())
90+
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt_headless.so"), []byte{}, 0644)).To(Succeed())
9591
}).Return(nil)
9692

9793
executor.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
@@ -100,7 +96,9 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
10096
})).Run(func(args mock.Arguments) {
10197
exec := args.Get(0).(effect.Execution)
10298
lastArg := exec.Args[len(exec.Args)-1]
103-
Expect(ioutil.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
99+
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0755)).To(Succeed())
100+
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt.so"), []byte{}, 0644)).To(Succeed())
101+
Expect(os.WriteFile(filepath.Join(layer.Path, "libawt_headless.so"), []byte{}, 0644)).To(Succeed())
104102
}).Return(nil)
105103

106104
layer, err = ctx.Layers.Layer("test-layer")
@@ -134,6 +132,25 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
134132
"-cp", "some-classpath",
135133
"test-start-class",
136134
}))
135+
136+
Expect(filepath.Join(ctx.Application.Path, "BOOT-INF")).ToNot(BeADirectory())
137+
Expect(filepath.Join(ctx.Application.Path, "META-INF")).ToNot(BeADirectory())
138+
139+
Expect(filepath.Join(layer.Path, "test-start-class")).To(BeARegularFile())
140+
Expect(filepath.Join(layer.Path, "libawt.so")).To(BeARegularFile())
141+
Expect(filepath.Join(layer.Path, "libawt_headless.so")).To(BeARegularFile())
142+
143+
info, err := os.Stat(filepath.Join(layer.Path, "test-start-class"))
144+
Expect(err).NotTo(HaveOccurred())
145+
fmt.Println("info.Mode().Perm(): ", info.Mode().Perm().String())
146+
Expect(info.Mode().Perm()).To(Equal(os.FileMode(0755)))
147+
148+
Expect(filepath.Join(ctx.Application.Path, "test-start-class")).To(BeARegularFile())
149+
Expect(filepath.Join(ctx.Application.Path, "libawt.so")).To(BeARegularFile())
150+
Expect(filepath.Join(ctx.Application.Path, "libawt_headless.so")).To(BeARegularFile())
151+
info, err = os.Stat(filepath.Join(ctx.Application.Path, "test-start-class"))
152+
Expect(err).NotTo(HaveOccurred())
153+
Expect(info.Mode().Perm()).To(Equal(os.FileMode(0755)))
137154
})
138155
})
139156

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

165182
nativeImage, err := native.NewNativeImage(ctx.Application.Path, "", argsFile, "none", "", props, ctx.StackID)
166183
nativeImage.Logger = bard.NewLogger(io.Discard)
@@ -209,7 +226,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
209226
})).Run(func(args mock.Arguments) {
210227
exec := args.Get(0).(effect.Execution)
211228
lastArg := exec.Args[len(exec.Args)-1]
212-
Expect(ioutil.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
229+
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
213230
}).Return(nil)
214231

215232
layer, err = ctx.Layers.Layer("test-layer")
@@ -254,7 +271,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
254271
})).Run(func(args mock.Arguments) {
255272
exec := args.Get(0).(effect.Execution)
256273
lastArg := exec.Args[len(exec.Args)-1]
257-
Expect(ioutil.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
274+
Expect(os.WriteFile(filepath.Join(layer.Path, lastArg), []byte{}, 0644)).To(Succeed())
258275
}).Return(nil)
259276

260277
layer, err = ctx.Layers.Layer("test-layer")
@@ -316,7 +333,7 @@ func testNativeImage(t *testing.T, context spec.G, it spec.S) {
316333
executor.On("Execute", mock.MatchedBy(func(e effect.Execution) bool {
317334
return e.Command == "upx"
318335
})).Run(func(args mock.Arguments) {
319-
Expect(ioutil.WriteFile(filepath.Join(layer.Path, "test-start-class"), []byte("upx-compressed"), 0644)).To(Succeed())
336+
Expect(os.WriteFile(filepath.Join(layer.Path, "test-start-class"), []byte("upx-compressed"), 0644)).To(Succeed())
320337
}).Return(nil)
321338

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

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

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

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

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

0 commit comments

Comments
 (0)