Skip to content

Commit 74fcf74

Browse files
committed
fix error reporitng for compress + add clean commands
1 parent 44f8f31 commit 74fcf74

4 files changed

Lines changed: 195 additions & 83 deletions

File tree

main-build.go

Lines changed: 143 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package main
22

33
import (
4+
"bytes"
45
"fmt"
6+
"io"
57
"io/fs"
68
"log"
79
"os"
@@ -10,7 +12,19 @@ import (
1012
"strings"
1113
)
1214

15+
func buildClean() {
16+
cwd, err := os.Getwd()
17+
exitIfErr(err)
18+
19+
productOutputFolder := filepath.Join(cwd, defaultGoBuildOutputFolder)
20+
21+
err = os.RemoveAll(productOutputFolder)
22+
exitIfErr(err)
23+
}
24+
1325
func build() {
26+
buildClean()
27+
1428
buildConfig, cwd := genBuildConfig()
1529

1630
productOutputFolder := filepath.Join(cwd, defaultGoBuildOutputFolder)
@@ -20,65 +34,22 @@ func build() {
2034
log.Printf("config : %v\n", buildConfig.Config)
2135
log.Printf("build : %s @ %s\n", buildConfig.Binary, buildConfig.Version)
2236

23-
err := os.RemoveAll(productOutputFolder)
37+
err := os.MkdirAll(productOutputFolder, os.FileMode(0755))
2438
exitIfErr(err)
2539

26-
err = os.MkdirAll(productOutputFolder, os.FileMode(0755))
27-
exitIfErr(err)
28-
29-
var debugFlags = ""
30-
if false {
31-
debugFlags = "-tags debug"
32-
}
33-
3440
var ldFlags = fmt.Sprintf("-s -w -X main.version=\"%s\"", buildConfig.Version)
3541

3642
// format: $binary_$version-$os.$arch-$details.$ext
3743
for _, platform := range buildConfig.Config.Platforms {
38-
// get OS + ARCH
39-
productOSAndArch := strings.Split(platform, "/")
40-
41-
productOS := ""
42-
productOSLabel := ""
43-
if len(productOSAndArch) > 0 {
44-
productOS = productOSAndArch[0]
45-
productOSLabel = productOS
46-
if _, hasKey := osToDisplay[productOS]; hasKey {
47-
productOSLabel = osToDisplay[productOS]
48-
}
49-
}
50-
51-
productArch := ""
52-
productArchLabel := ""
53-
if len(productOSAndArch) > 1 {
54-
productArch = productOSAndArch[1]
55-
productArchLabel = productArch
56-
if _, hasKey := archToDisplay[productArch]; hasKey {
57-
productArchLabel = archToDisplay[productArch]
58-
}
59-
}
60-
61-
productArmVersion := ""
62-
if len(productOSAndArch) > 2 {
63-
productArmVersion = productOSAndArch[2]
64-
}
65-
66-
productArchWithDetails := productArchLabel
67-
if productArmVersion != "" {
68-
productArchWithDetails = fmt.Sprintf("%s-v%s", productArchLabel, productArmVersion)
69-
}
70-
71-
productExt := ""
72-
if _, hasKey := osToExt[productOS]; hasKey {
73-
productExt = osToExt[productOS]
74-
}
44+
target, err := parsePlatform(platform)
45+
exitIfErr(err)
7546

7647
productBinaryName := buildConfig.Binary + "_v" + buildConfig.Version
7748
outputFile := filepath.Join(productOutputFolder, fmt.Sprintf("%s_%s-%s%s",
7849
productBinaryName,
79-
productOSLabel,
80-
productArchWithDetails,
81-
productExt,
50+
target.productOSLabel,
51+
target.productArchWithDetails,
52+
target.productExt,
8253
))
8354

8455
log.Printf(" -> %-55s \n", filepath.Base(outputFile))
@@ -89,10 +60,15 @@ func build() {
8960
cmdToRunEnv = append(cmdToRunEnv, fmt.Sprintf("%s=%s", productEnvK, productEnvV))
9061
}
9162

92-
cmdToRunEnv = append(cmdToRunEnv, fmt.Sprintf("GOOS=%s", productOS))
93-
cmdToRunEnv = append(cmdToRunEnv, fmt.Sprintf("GOARCH=%s", productArch))
94-
if productArmVersion != "" {
95-
cmdToRunEnv = append(cmdToRunEnv, fmt.Sprintf("GOARM=%s", productArmVersion))
63+
cmdToRunEnv = append(cmdToRunEnv, fmt.Sprintf("GOOS=%s", target.productOS))
64+
cmdToRunEnv = append(cmdToRunEnv, fmt.Sprintf("GOARCH=%s", target.productArch))
65+
if target.productArmVersion != "" {
66+
cmdToRunEnv = append(cmdToRunEnv, fmt.Sprintf("GOARM=%s", target.productArmVersion))
67+
}
68+
69+
var debugFlags = ""
70+
if false {
71+
debugFlags = "-tags debug"
9672
}
9773

9874
cmdToRun := exec.Command("go", "build")
@@ -106,38 +82,128 @@ func build() {
10682
cmdToRun.Dir = buildConfig.GoModFolder
10783
cmdToRun.Stderr = os.Stderr
10884
cmdToRun.Stdout = os.Stdout
109-
err = cmdToRun.Run()
110-
exitIfErr(err)
85+
if err := cmdToRun.Run(); err != nil {
86+
exitIfErr(fmt.Errorf("build %q: %w", platform, err))
87+
}
11188
}
11289

113-
// run compressor
114-
if buildConfig.Config.Compress {
115-
if _, err := exec.LookPath(buildConfig.Config.Compressor); err == nil {
116-
filepath.WalkDir(productOutputFolder, func(path string, d fs.DirEntry, err error) error {
117-
if !isFile(path) {
118-
return nil
119-
}
90+
exitIfErr(compressOutputs(productOutputFolder, buildConfig.Config))
91+
}
92+
93+
type buildTarget struct {
94+
productOS string
95+
productOSLabel string
96+
97+
productArch string
98+
productArchLabel string
99+
100+
productArmVersion string
120101

121-
preSize := getFileSize(path)
122-
preSizeKB := preSize / 1024
123-
preSizeMB := preSize / (1024 * 1024)
102+
productArchWithDetails string
124103

125-
cmdToRun := exec.Command(buildConfig.Config.Compressor)
126-
if len(buildConfig.Config.CompressorFlags) > 0 {
127-
cmdToRun.Args = append(cmdToRun.Args, buildConfig.Config.CompressorFlags...)
128-
}
129-
cmdToRun.Args = append(cmdToRun.Args, path)
104+
productExt string
105+
}
106+
107+
func parsePlatform(platform string) (buildTarget, error) {
108+
platform = strings.TrimSpace(platform)
109+
110+
target := buildTarget{}
111+
112+
parts := strings.Split(platform, "/")
113+
if len(parts) < 2 || len(parts) > 3 {
114+
return target, fmt.Errorf("invalid platform %q: expected GOOS/GOARCH or GOOS/GOARCH/GOARM", platform)
115+
}
116+
117+
// OS
118+
target.productOS = parts[0]
130119

131-
_ = cmdToRun.Run()
120+
target.productOSLabel = target.productOS
121+
if display, ok := osToDisplay[target.productOS]; ok {
122+
target.productOSLabel = display
123+
}
132124

133-
postSize := getFileSize(path)
134-
postSizeKB := postSize / 1024
135-
postSizeMB := postSize / (1024 * 1024)
125+
// ARCH
126+
target.productArch = parts[1]
127+
if target.productOS == "" || target.productArch == "" {
128+
return target, fmt.Errorf("invalid platform %q: GOOS and GOARCH are required", platform)
129+
}
136130

137-
log.Printf("compress : %s %d/%dK/%dMB -> %d/%dK/%dMB \n", d.Name(), preSize, preSizeKB, preSizeMB, postSize, postSizeKB, postSizeMB)
131+
target.productArchLabel = target.productArch
132+
if display, ok := archToDisplay[target.productArch]; ok {
133+
target.productArchLabel = display
134+
}
138135

139-
return nil
140-
})
136+
// ARM
137+
if len(parts) == 3 {
138+
if parts[2] == "" {
139+
return target, fmt.Errorf("invalid platform %q: GOARM is required when a third segment is provided", platform)
140+
}
141+
if target.productArch != "arm" {
142+
return target, fmt.Errorf("invalid platform %q: GOARM can only be set for arm builds", platform)
141143
}
144+
145+
target.productArmVersion = parts[2]
146+
}
147+
148+
// DET
149+
target.productArchWithDetails = target.productArchLabel
150+
if target.productArmVersion != "" {
151+
target.productArchWithDetails = fmt.Sprintf("%s-v%s", target.productArchLabel, target.productArmVersion)
152+
}
153+
154+
// EXT
155+
target.productExt = osToExt[target.productOS]
156+
157+
return target, nil
158+
}
159+
160+
func compressOutputs(productOutputFolder string, config Config) error {
161+
if !config.Compress {
162+
return nil
163+
}
164+
165+
compressorPath, err := exec.LookPath(config.Compressor)
166+
if err != nil {
167+
log.Printf("compress : skipping, %s not found in PATH\n", config.Compressor)
168+
return nil
142169
}
170+
171+
return filepath.WalkDir(productOutputFolder, func(path string, dirEntry fs.DirEntry, walkErr error) error {
172+
if walkErr != nil {
173+
return walkErr
174+
}
175+
if dirEntry.IsDir() {
176+
return nil
177+
}
178+
179+
preSize := getFileSize(path)
180+
preSizeKB := preSize / 1024
181+
preSizeMB := preSize / (1024 * 1024)
182+
183+
var stderrBuffer bytes.Buffer
184+
185+
cmdToRun := exec.Command(compressorPath)
186+
if len(config.CompressorFlags) > 0 {
187+
cmdToRun.Args = append(cmdToRun.Args, config.CompressorFlags...)
188+
}
189+
cmdToRun.Args = append(cmdToRun.Args, path)
190+
cmdToRun.Stderr = io.MultiWriter(&stderrBuffer) //os.Stderr
191+
// cmdToRun.Stdout = os.Stdout
192+
if err := cmdToRun.Run(); err != nil {
193+
subErr := stderrBuffer.String()
194+
subErrI := strings.Index(strings.ToLower(subErr), "exception") + len("exception")
195+
if subErrI < len(subErr) {
196+
subErr = subErr[subErrI:]
197+
}
198+
return fmt.Errorf("compress : SKIP %s -> %s -> %s", dirEntry.Name(), config.Compressor, subErr)
199+
}
200+
201+
postSize := getFileSize(path)
202+
postSizeKB := postSize / 1024
203+
postSizeMB := postSize / (1024 * 1024)
204+
205+
log.Printf("compress : %s %d/%dK/%dMB -> %d/%dK/%dMB \n", dirEntry.Name(), preSize, preSizeKB, preSizeMB, postSize, postSizeKB, postSizeMB)
206+
207+
return nil
208+
})
143209
}

main-gen.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,23 @@ func gen() {
5656
log.Printf("gen: %s\n", goBuildVersionFile)
5757
}
5858

59+
func genRM() {
60+
cwd, err := os.Getwd()
61+
exitIfErr(err)
62+
63+
// binary
64+
goBuildBinaryFile := filepath.Join(cwd, defaultGoBuildBinaryFile)
65+
os.Remove(goBuildBinaryFile)
66+
67+
// config
68+
goBuildConfigFile := filepath.Join(cwd, defaultGoBuildConfig)
69+
os.Remove(goBuildConfigFile)
70+
71+
// version
72+
goBuildVersionFile := filepath.Join(cwd, defaultGoBuildVersionFile)
73+
os.Remove(goBuildVersionFile)
74+
}
75+
5976
func genBuildConfig() (BuildConfig, string) {
6077
cwd, err := os.Getwd()
6178
exitIfErr(err)
@@ -98,6 +115,7 @@ func findGoModFolder(cwd string) (string, error) {
98115
foldersToCheck := []string{
99116
cwd,
100117
filepath.Join(cwd, "pkg"),
118+
filepath.Join(cwd, "src"),
101119
}
102120

103121
for _, folder := range foldersToCheck {

main.go

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

33
import (
4+
"fmt"
45
"log"
56
"os"
67
)
@@ -10,10 +11,25 @@ func main() {
1011
switch os.Args[1] {
1112
case "gen":
1213
gen()
14+
15+
case "gen-rm":
16+
genRM()
17+
18+
case "clean":
19+
buildClean()
20+
1321
case "version":
14-
log.Println("version: ", version)
22+
fmt.Println("version:", version)
23+
os.Exit(0)
24+
1525
default:
16-
log.Println("unknown command")
26+
fmt.Println("usage: ", os.Args[0], "[COMMAND], no COMMAND builds it")
27+
fmt.Println("COMMAND is one of")
28+
fmt.Println("\tgen")
29+
fmt.Println("\tgen-rm")
30+
fmt.Println("\tclean")
31+
fmt.Println("\tversion")
32+
os.Exit(1)
1733
}
1834

1935
return

readme.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
# what
2-
- demo: https://codemodify.github.io/gobuild
1+
# demo
2+
- https://codemodify.github.io/gobuild
33

44
# install
55
- `go install github.com/codemodify/gobuild@latest`
66

77
# build
88
- `cd MY-GO-PROJECT`
99
- `gobuild`
10-
- it understands folder layout where the source is in PKG folder layout type
11-
- or if you run it from PKG folder
10+
- it understands standard folder layout with `go.mod` being in (https://github.com/golang-standards/project-layout)
11+
- the current folder
12+
- the `PKG` folder
13+
- the `SRC` folder
1214

1315
# customize
1416
- `cd MY-GO-PROJECT`
@@ -17,3 +19,13 @@
1719
- generates `.gobuild-version` file with the version to set (`-X main.version=` for `-ldflags`)
1820
- generates `.gobuild-binary` file with the binary file name
1921
- these files can be added to the source control for custom builds
22+
23+
# purpose
24+
- this is a tool designed for use in projects that
25+
- don't require custom build setups
26+
- fast prototyping and distribution for different platforms
27+
28+
- why to look elsewhere
29+
- if you need `docker/buildx` for cross builds in complex scenarios
30+
- if you use CGO that will use custom APIs (ex: Xlib for linux, and other things for other OSes)
31+
- custom C/C++ cross-compiler toolchains

0 commit comments

Comments
 (0)