github.com/goreleaser/goreleaser@v1.25.1/internal/pipe/upx/upx.go (about) 1 package upx 2 3 import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "strings" 8 9 "github.com/caarlos0/log" 10 "github.com/docker/go-units" 11 "github.com/goreleaser/goreleaser/internal/artifact" 12 "github.com/goreleaser/goreleaser/internal/pipe" 13 "github.com/goreleaser/goreleaser/internal/semerrgroup" 14 "github.com/goreleaser/goreleaser/internal/tmpl" 15 "github.com/goreleaser/goreleaser/pkg/config" 16 "github.com/goreleaser/goreleaser/pkg/context" 17 ) 18 19 type Pipe struct{} 20 21 func (Pipe) String() string { return "upx" } 22 func (Pipe) Default(ctx *context.Context) error { 23 for i := range ctx.Config.UPXs { 24 upx := &ctx.Config.UPXs[i] 25 if upx.Binary == "" { 26 upx.Binary = "upx" 27 } 28 } 29 return nil 30 } 31 func (Pipe) Skip(ctx *context.Context) bool { return len(ctx.Config.UPXs) == 0 } 32 func (Pipe) Run(ctx *context.Context) error { 33 g := semerrgroup.NewSkipAware(semerrgroup.New(ctx.Parallelism)) 34 for _, upx := range ctx.Config.UPXs { 35 upx := upx 36 enabled, err := tmpl.New(ctx).Bool(upx.Enabled) 37 if err != nil { 38 return err 39 } 40 if !enabled { 41 return pipe.Skip("upx is not enabled") 42 } 43 if _, err := exec.LookPath(upx.Binary); err != nil { 44 return pipe.Skipf("%s not found in PATH", upx.Binary) 45 } 46 for _, bin := range findBinaries(ctx, upx) { 47 bin := bin 48 g.Go(func() error { 49 sizeBefore := sizeOf(bin.Path) 50 args := []string{ 51 "--quiet", 52 } 53 switch upx.Compress { 54 case "best": 55 args = append(args, "--best") 56 case "": 57 default: 58 args = append(args, "-"+upx.Compress) 59 } 60 if upx.LZMA { 61 args = append(args, "--lzma") 62 } 63 if upx.Brute { 64 args = append(args, "--brute") 65 } 66 args = append(args, bin.Path) 67 out, err := exec.CommandContext(ctx, "upx", args...).CombinedOutput() 68 if err != nil { 69 for _, ke := range knownExceptions { 70 if strings.Contains(string(out), ke) { 71 log.WithField("binary", bin.Path). 72 WithField("exception", ke). 73 Warn("could not pack") 74 return nil 75 } 76 } 77 return fmt.Errorf("could not pack %s: %w: %s", bin.Path, err, string(out)) 78 } 79 80 sizeAfter := sizeOf(bin.Path) 81 82 log. 83 WithField("before", units.HumanSize(float64(sizeBefore))). 84 WithField("after", units.HumanSize(float64(sizeAfter))). 85 WithField("ratio", fmt.Sprintf("%d%%", (sizeAfter*100)/sizeBefore)). 86 WithField("binary", bin.Path). 87 Info("packed") 88 89 return nil 90 }) 91 } 92 } 93 return g.Wait() 94 } 95 96 var knownExceptions = []string{ 97 "CantPackException", 98 "AlreadyPackedException", 99 "NotCompressibleException", 100 } 101 102 func findBinaries(ctx *context.Context, upx config.UPX) []*artifact.Artifact { 103 filters := []artifact.Filter{ 104 artifact.Or( 105 artifact.ByType(artifact.Binary), 106 artifact.ByType(artifact.UniversalBinary), 107 ), 108 } 109 if f := orBy(artifact.ByGoos, upx.Goos); f != nil { 110 filters = append(filters, f) 111 } 112 if f := orBy(artifact.ByGoarch, upx.Goarch); f != nil { 113 filters = append(filters, f) 114 } 115 if f := orBy(artifact.ByGoarm, upx.Goarm); f != nil { 116 filters = append(filters, f) 117 } 118 if f := orBy(artifact.ByGoamd64, upx.Goamd64); f != nil { 119 filters = append(filters, f) 120 } 121 if len(upx.IDs) > 0 { 122 filters = append(filters, artifact.ByIDs(upx.IDs...)) 123 } 124 return ctx.Artifacts.Filter(artifact.And(filters...)).List() 125 } 126 127 func orBy(fn func(string) artifact.Filter, items []string) artifact.Filter { 128 var result []artifact.Filter 129 for _, f := range items { 130 result = append(result, fn(f)) 131 } 132 if len(result) == 0 { 133 return nil 134 } 135 return artifact.Or(result...) 136 } 137 138 func sizeOf(name string) int64 { 139 st, err := os.Stat(name) 140 if err != nil { 141 return 0 142 } 143 return st.Size() 144 }