|
5 | 5 | package main |
6 | 6 |
|
7 | 7 | import ( |
| 8 | + "archive/tar" |
| 9 | + "archive/zip" |
8 | 10 | "bufio" |
9 | 11 | "bytes" |
| 12 | + "compress/gzip" |
10 | 13 | "encoding/json" |
11 | 14 | "fmt" |
12 | 15 | "io" |
13 | 16 | "os" |
14 | 17 | "os/exec" |
15 | 18 | "path/filepath" |
| 19 | + "slices" |
16 | 20 | "strings" |
17 | 21 | "sync" |
18 | 22 | ) |
@@ -117,30 +121,123 @@ func doMultibuild(args cliArgs) { |
117 | 121 | out := formattedOutput |
118 | 122 | out = strings.ReplaceAll(out, "${GOOS}", goos) |
119 | 123 | out = strings.ReplaceAll(out, "${GOARCH}", goarch) |
| 124 | + outBin := out |
120 | 125 |
|
121 | 126 | if goos == "windows" { |
122 | | - out += ".exe" |
| 127 | + outBin += ".exe" |
123 | 128 | } |
124 | 129 |
|
125 | | - buildArgs := []string{"-o", out} |
| 130 | + buildArgs := []string{"-o", outBin} |
126 | 131 | buildArgs = append(buildArgs, args.goBuildArgs...) |
127 | 132 |
|
128 | 133 | wg.Add(1) // acquire for global |
129 | | - go func(goos, goarch string, buildArgs []string) { |
| 134 | + go func(out, outBin, goos, goarch string, buildArgs []string) { |
130 | 135 | if args.verbose { |
131 | 136 | fmt.Fprintf(os.Stderr, "%s/%s: waiting\n", goos, goarch) |
132 | 137 | } |
133 | 138 | sem <- struct{}{} // acquire for job |
134 | 139 | if args.verbose { |
135 | | - fmt.Fprintf(os.Stderr, "%s/%s: building\n", goos, goarch) |
| 140 | + fmt.Fprintf(os.Stderr, "%s/%s: build\n", goos, goarch) |
136 | 141 | } |
137 | 142 | runBuild(buildArgs, goos, goarch) |
138 | 143 | if args.verbose { |
139 | | - fmt.Fprintf(os.Stderr, "%s/%s: done\n", goos, goarch) |
| 144 | + fmt.Fprintf(os.Stderr, "%s/%s: archive\n", goos, goarch) |
| 145 | + } |
| 146 | + |
| 147 | + for _, format := range opts.Format { |
| 148 | + switch format { |
| 149 | + case formatRaw: |
| 150 | + // already built (obvs).. |
| 151 | + case formatZip: |
| 152 | + arPath := out + ".zip" |
| 153 | + f, err := os.Create(arPath) |
| 154 | + defer f.Close() |
| 155 | + if err != nil { |
| 156 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to create archive %s: %s\n", goos, goarch, arPath, err) |
| 157 | + os.Exit(1) |
| 158 | + } |
| 159 | + |
| 160 | + zw := zip.NewWriter(f) |
| 161 | + defer zw.Close() |
| 162 | + |
| 163 | + w, err := zw.Create(outBin) |
| 164 | + if err != nil { |
| 165 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to create header %s: %s\n", goos, goarch, arPath, err) |
| 166 | + os.Exit(1) |
| 167 | + } |
| 168 | + |
| 169 | + st, err := os.Stat(outBin) |
| 170 | + if err != nil { |
| 171 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to stat raw %s: %s\n", goos, goarch, outBin, err) |
| 172 | + os.Exit(1) |
| 173 | + } |
| 174 | + bin, err := os.Open(outBin) |
| 175 | + if err != nil { |
| 176 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to open raw %s: %s\n", goos, goarch, outBin, err) |
| 177 | + os.Exit(1) |
| 178 | + } |
| 179 | + defer bin.Close() |
| 180 | + sz, err := io.Copy(w, bin) |
| 181 | + if err != nil { |
| 182 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to copy %s: %s\n", goos, goarch, outBin, err) |
| 183 | + os.Exit(1) |
| 184 | + } |
| 185 | + if sz != st.Size() { |
| 186 | + fmt.Fprintf(os.Stderr, "%s/%s: size mismatch in copy of %s: (%d vs %d)\n", goos, goarch, outBin, sz, st.Size()) |
| 187 | + os.Exit(1) |
| 188 | + } |
| 189 | + case formatTgz: |
| 190 | + arPath := out + ".tar.gz" |
| 191 | + f, err := os.Create(arPath) |
| 192 | + if err != nil { |
| 193 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to create archive %s: %s\n", goos, goarch, arPath, err) |
| 194 | + os.Exit(1) |
| 195 | + } |
| 196 | + defer f.Close() |
| 197 | + |
| 198 | + gz := gzip.NewWriter(f) |
| 199 | + defer gz.Close() |
| 200 | + |
| 201 | + tw := tar.NewWriter(gz) |
| 202 | + defer tw.Close() |
| 203 | + |
| 204 | + st, err := os.Stat(outBin) |
| 205 | + if err != nil { |
| 206 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to stat raw %s: %s\n", goos, goarch, outBin, err) |
| 207 | + os.Exit(1) |
| 208 | + } |
| 209 | + bin, err := os.Open(outBin) |
| 210 | + if err != nil { |
| 211 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to open raw %s: %s\n", goos, goarch, outBin, err) |
| 212 | + os.Exit(1) |
| 213 | + } |
| 214 | + defer bin.Close() |
| 215 | + |
| 216 | + hdr := &tar.Header{Name: outBin, Mode: 0755, Size: st.Size()} |
| 217 | + tw.WriteHeader(hdr) |
| 218 | + sz, err := io.Copy(tw, bin) |
| 219 | + if err != nil { |
| 220 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to copy %s: %s\n", goos, goarch, outBin, err) |
| 221 | + os.Exit(1) |
| 222 | + } |
| 223 | + if sz != st.Size() { |
| 224 | + fmt.Fprintf(os.Stderr, "%s/%s: size mismatch in copy of %s: (%d vs %d)\n", goos, goarch, outBin, sz, st.Size()) |
| 225 | + os.Exit(1) |
| 226 | + } |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + // If the format list specifically excluded raw, remove the binary. |
| 231 | + // I don't know why one would want to do this, but nevertheless... |
| 232 | + if !slices.Contains(opts.Format, formatRaw) { |
| 233 | + err := os.Remove(outBin) |
| 234 | + if err != nil { |
| 235 | + fmt.Fprintf(os.Stderr, "%s/%s: failed to remove unwanted raw output %s: %s\n", goos, goarch, outBin, err) |
| 236 | + } |
140 | 237 | } |
141 | 238 | <-sem // release for job |
142 | 239 | wg.Done() // release for global |
143 | | - }(goos, goarch, buildArgs) |
| 240 | + }(out, outBin, goos, goarch, buildArgs) |
144 | 241 | } |
145 | 242 |
|
146 | 243 | wg.Wait() |
|
0 commit comments