Skip to content

Commit 216f570

Browse files
authored
Merge pull request #53 from kluctl/feat-switch-dist
feat: Strip debug info from binaries to save space
2 parents f98a789 + 20ca861 commit 216f570

7 files changed

Lines changed: 129 additions & 35 deletions

File tree

.github/workflows/release.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ on:
1111
env:
1212
PYTHON_STANDALONE_VERSIONS: |
1313
[
14-
"20260510"
14+
"20260602"
1515
]
1616
PYTHON_VERSIONS: |
1717
[
@@ -40,6 +40,8 @@ jobs:
4040
fail-fast: false
4141
runs-on: ubuntu-24.04
4242
steps:
43+
- name: Setup QEMU
44+
uses: docker/setup-qemu-action@v4
4345
- name: clone
4446
run: |
4547
# can't use actions/checkout here as transferring the shallow clone fails when using upload-/download-artifact
@@ -59,7 +61,7 @@ jobs:
5961
- name: Set up Go
6062
uses: actions/setup-go@v5
6163
with:
62-
go-version: 1.26
64+
go-version-file: go.mod
6365
- name: build-tag
6466
run: |
6567
git config --global user.email "no@mail.exists"
@@ -109,7 +111,7 @@ jobs:
109111
- name: Set up Go
110112
uses: actions/setup-go@v5
111113
with:
112-
go-version: 1.26
114+
go-version-file: go.mod
113115
- name: run tests
114116
shell: bash
115117
run: |

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ require (
88
github.com/klauspost/compress v1.18.6
99
github.com/sirupsen/logrus v1.9.4
1010
github.com/stretchr/testify v1.11.1
11-
golang.org/x/sync v0.20.0
11+
golang.org/x/sync v0.21.0
1212
)
1313

1414
require (
1515
github.com/davecgh/go-spew v1.1.1 // indirect
1616
github.com/kr/text v0.2.0 // indirect
1717
github.com/pmezard/go-difflib v1.0.0 // indirect
1818
github.com/rogpeppe/go-internal v1.13.1 // indirect
19-
golang.org/x/sys v0.44.0 // indirect
19+
golang.org/x/sys v0.46.0 // indirect
2020
gopkg.in/yaml.v3 v3.0.1 // indirect
2121
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,15 @@ golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
3434
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
3535
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
3636
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
37+
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
38+
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
3739
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
3840
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
3941
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
4042
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
4143
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
44+
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
45+
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
4246
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
4347
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
4448
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

internal/strip.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package internal
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"path/filepath"
8+
9+
log "github.com/sirupsen/logrus"
10+
)
11+
12+
func StripBinaries(path string, arch string) error {
13+
var binFiles []string
14+
15+
err := filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
16+
relPath, err := filepath.Rel(path, p)
17+
if err != nil {
18+
return err
19+
}
20+
if info.Mode().IsRegular() && info.Mode()&0111 != 0 {
21+
b, err := os.ReadFile(p)
22+
if err != nil {
23+
return err
24+
}
25+
if len(b) >= 4 && b[0] == 0x7F && b[1] == 0x45 && b[2] == 0x4c && b[3] == 0x46 {
26+
binFiles = append(binFiles, relPath)
27+
}
28+
}
29+
return nil
30+
})
31+
if err != nil {
32+
return err
33+
}
34+
35+
log.Infof("stripping %d files in %s", len(binFiles), path)
36+
37+
script := `set -e
38+
export DEBIAN_FRONTEND=noninteractive
39+
apt update
40+
apt install -y llvm
41+
cd /host
42+
`
43+
for _, file := range binFiles {
44+
script += fmt.Sprintf("llvm-strip %s\n", file)
45+
}
46+
47+
cmd := exec.Command("docker", "run", "-i", "-v", fmt.Sprintf("%s:/host", path), "-w", "/host", "debian:trixie", "sh", "-c", script)
48+
cmd.Stdout = os.Stdout
49+
cmd.Stderr = os.Stderr
50+
err = cmd.Run()
51+
if err != nil {
52+
return err
53+
}
54+
return nil
55+
}

pip/internal/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
pip==26.1.1
1+
pip==26.1.2

python/generate/main.go

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"compress/gzip"
45
"flag"
56
"fmt"
67
"io"
@@ -78,44 +79,58 @@ func main() {
7879
os string
7980
arch string
8081
dist string
82+
ext string
8183
keepPatterns []glob.Glob
84+
strip bool
8285
}
8386

8487
jobs := []job{
85-
{"linux", "amd64", "unknown-linux-gnu-pgo+lto-full", keepNixPatterns},
86-
{"linux", "arm64", "unknown-linux-gnu-pgo+lto-full", keepNixPatterns},
87-
{"darwin", "amd64", "apple-darwin-pgo+lto-full", keepNixPatterns},
88-
{"darwin", "arm64", "apple-darwin-pgo+lto-full", keepNixPatterns},
89-
{"windows", "amd64", "pc-windows-msvc-pgo-full", keepWinPatterns},
88+
{"linux", "amd64", "unknown-linux-gnu-pgo+lto-full", "tar.zst", keepNixPatterns, true},
89+
{"linux", "arm64", "unknown-linux-gnu-pgo+lto-full", "tar.zst", keepNixPatterns, true},
90+
{"darwin", "amd64", "apple-darwin-pgo+lto-full", "tar.zst", keepNixPatterns, false},
91+
{"darwin", "arm64", "apple-darwin-pgo+lto-full", "tar.zst", keepNixPatterns, false},
92+
{"windows", "amd64", "pc-windows-msvc-pgo-full", "tar.zst", keepWinPatterns, false},
9093
}
9194
for _, j := range jobs {
9295
j := j
9396
wg.Add(1)
9497
go func() {
9598
if *runPrepare {
96-
downloadAndPrepare(j.os, j.arch, j.dist, j.keepPatterns)
99+
downloadAndPrepare(j.os, j.arch, j.dist, j.ext, j.keepPatterns, j.strip)
97100
}
98101
if *runPack {
99-
packPrepared(j.os, j.arch, j.dist, targetPath)
102+
packPrepared(j.os, j.arch, j.dist, j.ext, targetPath)
100103
}
101104
wg.Done()
102105
}()
103106
}
104107
wg.Wait()
105108
}
106109

107-
func downloadAndPrepare(osName string, arch string, dist string, keepPatterns []glob.Glob) {
108-
downloadPath := download(osName, arch, dist)
110+
func calcInstallPath(extractPath string, dist string) string {
111+
installPath := filepath.Join(extractPath, "python")
112+
if !strings.Contains(dist, "install_only") {
113+
installPath = filepath.Join(installPath, "install")
114+
}
115+
return installPath
116+
}
117+
118+
func downloadAndPrepare(osName string, arch string, dist string, ext string, keepPatterns []glob.Glob, strip bool) {
119+
downloadPath := download(osName, arch, dist, ext)
109120

110121
extractPath := downloadPath + ".extracted"
111122
err := os.RemoveAll(extractPath)
112123
if err != nil {
113124
log.Panic(err)
114125
}
115126

116-
extract(downloadPath, extractPath)
127+
_, err = extract(downloadPath, extractPath, ext)
128+
if err != nil {
129+
log.Errorf("extract failed: %v", err)
130+
os.Exit(1)
131+
}
117132

118-
installPath := filepath.Join(extractPath, "python", "install")
133+
installPath := calcInstallPath(extractPath, dist)
119134

120135
var libPath string
121136
if osName == "windows" {
@@ -132,11 +147,18 @@ func downloadAndPrepare(osName string, arch string, dist string, keepPatterns []
132147
if err != nil {
133148
panic(err)
134149
}
150+
151+
if strip {
152+
err = internal.StripBinaries(installPath, arch)
153+
if err != nil {
154+
panic(err)
155+
}
156+
}
135157
}
136158

137-
func packPrepared(osName string, arch string, dist string, targetPath string) {
138-
extractPath := generateDownloadPath(arch, dist) + ".extracted"
139-
installPath := filepath.Join(extractPath, "python", "install")
159+
func packPrepared(osName string, arch string, dist string, ext string, targetPath string) {
160+
extractPath := generateDownloadPath(arch, dist, ext) + ".extracted"
161+
installPath := calcInstallPath(extractPath, dist)
140162
err := embed_util.CopyForEmbed(filepath.Join(targetPath, fmt.Sprintf("%s-%s", osName, arch)), installPath)
141163
if err != nil {
142164
panic(err)
@@ -158,21 +180,21 @@ func packPrepared(osName string, arch string, dist string, targetPath string) {
158180
}
159181
}
160182

161-
func generateDownloadPath(arch string, dist string) string {
183+
func generateDownloadPath(arch string, dist string, ext string) string {
162184
pythonArch, ok := archMapping[arch]
163185
if !ok {
164186
log.Errorf("arch %s not supported", arch)
165187
os.Exit(1)
166188
}
167-
fname := fmt.Sprintf("cpython-%s+%s-%s-%s.tar.zst", *pythonVersion, *pythonStandaloneVersion, pythonArch, dist)
189+
fname := fmt.Sprintf("cpython-%s+%s-%s-%s.%s", *pythonVersion, *pythonStandaloneVersion, pythonArch, dist, ext)
168190
return filepath.Join(*preparePath, fname)
169191
}
170192

171-
func download(osName string, arch string, dist string) string {
193+
func download(osName string, arch string, dist string, ext string) string {
172194
downloadLock.Lock()
173195
defer downloadLock.Unlock()
174196

175-
downloadPath := generateDownloadPath(arch, dist)
197+
downloadPath := generateDownloadPath(arch, dist, ext)
176198
fname := filepath.Base(downloadPath)
177199
downloadUrl := fmt.Sprintf("https://github.com/astral-sh/python-build-standalone/releases/download/%s/%s", *pythonStandaloneVersion, fname)
178200

@@ -211,27 +233,38 @@ func download(osName string, arch string, dist string) string {
211233
return downloadPath
212234
}
213235

214-
func extract(archivePath string, targetPath string) string {
236+
func extract(archivePath string, targetPath string, ext string) (string, error) {
215237
f, err := os.Open(archivePath)
216238
if err != nil {
217-
log.Errorf("opening file failed: %v", err)
218-
os.Exit(1)
239+
return "", err
219240
}
220241
defer f.Close()
221242

222-
z, err := zstd.NewReader(f)
223-
if err != nil {
224-
log.Errorf("decompression failed: %v", err)
225-
os.Exit(1)
243+
var dc io.Reader
244+
if ext == "tar.gz" {
245+
gz, err := gzip.NewReader(f)
246+
if err != nil {
247+
return "", err
248+
}
249+
defer gz.Close()
250+
dc = gz
251+
} else if ext == "tar.zst" {
252+
z, err := zstd.NewReader(f)
253+
if err != nil {
254+
return "", err
255+
}
256+
defer z.Close()
257+
dc = z
258+
} else {
259+
return "", fmt.Errorf("unknown extension: %s", ext)
226260
}
227-
defer z.Close()
228261

229262
log.Infof("decompressing %s", archivePath)
230-
err = internal.ExtractTarStream(z, targetPath)
263+
err = internal.ExtractTarStream(dc, targetPath)
231264
if err != nil {
232265
log.Errorf("decompression failed: %v", err)
233266
os.Exit(1)
234267
}
235268

236-
return targetPath
269+
return targetPath, nil
237270
}

python/internal/data/dummy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package data
22

33
// PLEASE READ THIS!!!!
4-
// This file is really just a dummy. The release process will remove this file and generate some read embedded files
4+
// This file is really just a dummy. The release process will remove this file and generate some real embedded files
55
// and commit these into a temporary branch and then tag it. This is to avoid clogging up the main branch with too many
66
// binary files, which would be a very bad experience when pulling in go-embed-python as a dependency.

0 commit comments

Comments
 (0)