1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package main
29
30 import (
31 "archive/tar"
32 "archive/zip"
33 "compress/flate"
34 "compress/gzip"
35 "crypto/sha256"
36 "flag"
37 "fmt"
38 "io"
39 "io/fs"
40 "log"
41 "os"
42 "path"
43 "path/filepath"
44 "runtime"
45 "strings"
46 "time"
47
48 "cmd/internal/telemetry/counter"
49 )
50
51 func usage() {
52 fmt.Fprintf(os.Stderr, "usage: distpack\n")
53 os.Exit(2)
54 }
55
56 const (
57 modPath = "golang.org/toolchain"
58 modVersionPrefix = "v0.0.1"
59 )
60
61 var (
62 goroot string
63 gohostos string
64 gohostarch string
65 goos string
66 goarch string
67 )
68
69 func main() {
70 log.SetPrefix("distpack: ")
71 log.SetFlags(0)
72 counter.Open()
73 flag.Usage = usage
74 flag.Parse()
75 counter.Inc("distpack/invocations")
76 counter.CountFlags("distpack/flag:", *flag.CommandLine)
77 if flag.NArg() != 0 {
78 usage()
79 }
80
81
82 goroot = runtime.GOROOT()
83 if goroot == "" {
84 log.Fatalf("missing $GOROOT")
85 }
86 gohostos = runtime.GOOS
87 gohostarch = runtime.GOARCH
88 goos = os.Getenv("GOOS")
89 if goos == "" {
90 goos = gohostos
91 }
92 goarch = os.Getenv("GOARCH")
93 if goarch == "" {
94 goarch = gohostarch
95 }
96 goosUnderGoarch := goos + "_" + goarch
97 goosDashGoarch := goos + "-" + goarch
98 exe := ""
99 if goos == "windows" {
100 exe = ".exe"
101 }
102 version, versionTime := readVERSION(goroot)
103
104
105 base, err := NewArchive(goroot)
106 if err != nil {
107 log.Fatal(err)
108 }
109 base.SetTime(versionTime)
110 base.SetMode(mode)
111 base.Remove(
112 ".git/**",
113 ".gitattributes",
114 ".github/**",
115 ".gitignore",
116 "VERSION.cache",
117 "misc/cgo/*/_obj/**",
118 "**/.DS_Store",
119 "**/*.exe~",
120
121 "src/cmd/dist/dist",
122 "src/cmd/dist/dist.exe",
123 )
124
125
126
127 srcArch := base.Clone()
128 srcArch.Remove(
129 "bin/**",
130 "pkg/**",
131
132
133 "src/cmd/go/internal/cfg/zdefaultcc.go",
134 "src/go/build/zcgo.go",
135 "src/internal/runtime/sys/zversion.go",
136 "src/time/tzdata/zzipdata.go",
137
138
139 "src/cmd/cgo/zdefaultcc.go",
140 "src/cmd/internal/objabi/zbootstrap.go",
141 "src/internal/buildcfg/zbootstrap.go",
142
143
144 "src/cmd/go/internal/cfg/zosarch.go",
145 )
146 srcArch.AddPrefix("go")
147 testSrc(srcArch)
148
149
150 binArch := base.Clone()
151 binArch.Filter(func(name string) bool {
152
153 if strings.HasPrefix(name, "bin/") {
154 return false
155 }
156
157 if strings.HasPrefix(name, "pkg/") {
158
159 if strings.HasPrefix(name, "pkg/include/") {
160 return true
161 }
162
163 if !strings.HasPrefix(name, "pkg/tool/") {
164 return false
165 }
166
167 if !strings.HasPrefix(name, "pkg/tool/"+goosUnderGoarch+"/") {
168 return false
169 }
170
171 switch strings.TrimSuffix(path.Base(name), ".exe") {
172 default:
173 return false
174
175 case "asm", "cgo", "compile", "cover", "link", "preprofile", "vet":
176 }
177 }
178 return true
179 })
180
181
182
183
184 binExes := []string{
185 "go",
186 "gofmt",
187 }
188 crossBin := "bin"
189 if goos != gohostos || goarch != gohostarch {
190 crossBin = "bin/" + goosUnderGoarch
191 }
192 for _, b := range binExes {
193 name := "bin/" + b + exe
194 src := filepath.Join(goroot, crossBin, b+exe)
195 info, err := os.Stat(src)
196 if err != nil {
197 log.Fatal(err)
198 }
199 binArch.Add(name, src, info)
200 }
201 binArch.Sort()
202 binArch.SetTime(versionTime)
203 binArch.SetMode(mode)
204
205 zipArch := binArch.Clone()
206 zipArch.AddPrefix("go")
207 testZip(zipArch)
208
209
210
211 modArch := binArch.Clone()
212 modArch.Remove(
213 "api/**",
214 "doc/**",
215 "misc/**",
216 "test/**",
217 )
218 modVers := modVersionPrefix + "-" + version + "." + goosDashGoarch
219 modArch.AddPrefix(modPath + "@" + modVers)
220 modArch.RenameGoMod()
221 modArch.Sort()
222 testMod(modArch)
223
224
225 distpack := func(name string) string {
226 return filepath.Join(goroot, "pkg/distpack", name)
227 }
228 if err := os.MkdirAll(filepath.Join(goroot, "pkg/distpack"), 0777); err != nil {
229 log.Fatal(err)
230 }
231
232 writeTgz(distpack(version+".src.tar.gz"), srcArch)
233
234 if goos == "windows" {
235 writeZip(distpack(version+"."+goos+"-"+goarch+".zip"), zipArch)
236 } else {
237 writeTgz(distpack(version+"."+goos+"-"+goarch+".tar.gz"), zipArch)
238 }
239
240 writeZip(distpack(modVers+".zip"), modArch)
241 writeFile(distpack(modVers+".mod"),
242 []byte(fmt.Sprintf("module %s\n", modPath)))
243 writeFile(distpack(modVers+".info"),
244 []byte(fmt.Sprintf("{%q:%q, %q:%q}\n",
245 "Version", modVers,
246 "Time", versionTime.Format(time.RFC3339))))
247 }
248
249
250 func mode(name string, _ fs.FileMode) fs.FileMode {
251 if strings.HasPrefix(name, "bin/") ||
252 strings.HasPrefix(name, "pkg/tool/") ||
253 strings.HasSuffix(name, ".bash") ||
254 strings.HasSuffix(name, ".sh") ||
255 strings.HasSuffix(name, ".pl") ||
256 strings.HasSuffix(name, ".rc") {
257 return 0o755
258 } else if ok, _ := amatch("**/go_?*_?*_exec", name); ok {
259 return 0o755
260 }
261 return 0o644
262 }
263
264
265
266
267
268 func readVERSION(goroot string) (version string, t time.Time) {
269 data, err := os.ReadFile(filepath.Join(goroot, "VERSION"))
270 if err != nil {
271 log.Fatal(err)
272 }
273 version, rest, _ := strings.Cut(string(data), "\n")
274 for _, line := range strings.Split(rest, "\n") {
275 f := strings.Fields(line)
276 if len(f) == 0 {
277 continue
278 }
279 switch f[0] {
280 default:
281 log.Fatalf("VERSION: unexpected line: %s", line)
282 case "time":
283 if len(f) != 2 {
284 log.Fatalf("VERSION: unexpected time line: %s", line)
285 }
286 t, err = time.ParseInLocation(time.RFC3339, f[1], time.UTC)
287 if err != nil {
288 log.Fatalf("VERSION: bad time: %s", err)
289 }
290 }
291 }
292 return version, t
293 }
294
295
296 func writeFile(name string, data []byte) {
297 if err := os.WriteFile(name, data, 0666); err != nil {
298 log.Fatal(err)
299 }
300 reportHash(name)
301 }
302
303
304
305
306 func check[T any](x T, err error) T {
307 check1(err)
308 return x
309 }
310
311
312
313
314 func check1(err error) {
315 if err != nil {
316 panic(err)
317 }
318 }
319
320
321 func writeTgz(name string, a *Archive) {
322 out, err := os.Create(name)
323 if err != nil {
324 log.Fatal(err)
325 }
326
327 var f File
328 defer func() {
329 if err := recover(); err != nil {
330 extra := ""
331 if f.Name != "" {
332 extra = " " + f.Name
333 }
334 log.Fatalf("writing %s%s: %v", name, extra, err)
335 }
336 }()
337
338 zw := check(gzip.NewWriterLevel(out, gzip.BestCompression))
339 tw := tar.NewWriter(zw)
340
341
342
343
344 var dirMode fs.FileMode
345 var mtime time.Time
346 for _, f := range a.Files {
347 dirMode = fs.ModeDir | f.Mode | (f.Mode&0444)>>2
348 mtime = f.Time
349 break
350 }
351
352
353
354
355 haveDir := map[string]bool{".": true}
356 var mkdirAll func(string)
357 mkdirAll = func(dir string) {
358 if dir == "/" {
359 panic("mkdirAll /")
360 }
361 if haveDir[dir] {
362 return
363 }
364 haveDir[dir] = true
365 mkdirAll(path.Dir(dir))
366 df := &File{
367 Name: dir + "/",
368 Time: mtime,
369 Mode: dirMode,
370 }
371 h := check(tar.FileInfoHeader(df.Info(), ""))
372 h.Name = dir + "/"
373 if err := tw.WriteHeader(h); err != nil {
374 panic(err)
375 }
376 }
377
378 for _, f = range a.Files {
379 h := check(tar.FileInfoHeader(f.Info(), ""))
380 mkdirAll(path.Dir(f.Name))
381 h.Name = f.Name
382 if err := tw.WriteHeader(h); err != nil {
383 panic(err)
384 }
385 r := check(os.Open(f.Src))
386 check(io.Copy(tw, r))
387 check1(r.Close())
388 }
389 f.Name = ""
390 check1(tw.Close())
391 check1(zw.Close())
392 check1(out.Close())
393 reportHash(name)
394 }
395
396
397 func writeZip(name string, a *Archive) {
398 out, err := os.Create(name)
399 if err != nil {
400 log.Fatal(err)
401 }
402
403 var f File
404 defer func() {
405 if err := recover(); err != nil {
406 extra := ""
407 if f.Name != "" {
408 extra = " " + f.Name
409 }
410 log.Fatalf("writing %s%s: %v", name, extra, err)
411 }
412 }()
413
414 zw := zip.NewWriter(out)
415 zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
416 return flate.NewWriter(out, flate.BestCompression)
417 })
418 for _, f = range a.Files {
419 h := check(zip.FileInfoHeader(f.Info()))
420 h.Name = f.Name
421 h.Method = zip.Deflate
422 w := check(zw.CreateHeader(h))
423 r := check(os.Open(f.Src))
424 check(io.Copy(w, r))
425 check1(r.Close())
426 }
427 f.Name = ""
428 check1(zw.Close())
429 check1(out.Close())
430 reportHash(name)
431 }
432
433 func reportHash(name string) {
434 f, err := os.Open(name)
435 if err != nil {
436 log.Fatal(err)
437 }
438 h := sha256.New()
439 io.Copy(h, f)
440 f.Close()
441 fmt.Printf("distpack: %x %s\n", h.Sum(nil)[:8], filepath.Base(name))
442 }
443
View as plain text