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 ".jj/**",
117 "VERSION.cache",
118 "misc/cgo/*/_obj/**",
119 "**/.DS_Store",
120 "**/*.exe~",
121
122 "src/cmd/dist/dist",
123 "src/cmd/dist/dist.exe",
124 )
125
126
127
128 srcArch := base.Clone()
129 srcArch.Remove(
130 "bin/**",
131 "pkg/**",
132
133
134 "src/cmd/go/internal/cfg/zdefaultcc.go",
135 "src/go/build/zcgo.go",
136 "src/internal/runtime/sys/zversion.go",
137 "src/time/tzdata/zzipdata.go",
138
139
140 "src/cmd/cgo/zdefaultcc.go",
141 "src/cmd/internal/objabi/zbootstrap.go",
142 "src/internal/buildcfg/zbootstrap.go",
143
144
145 "src/cmd/go/internal/cfg/zosarch.go",
146 )
147 srcArch.AddPrefix("go")
148 testSrc(srcArch)
149
150
151 binArch := base.Clone()
152 binArch.Filter(func(name string) bool {
153
154 if strings.HasPrefix(name, "bin/") {
155 return false
156 }
157
158 if strings.HasPrefix(name, "pkg/") {
159
160 if strings.HasPrefix(name, "pkg/include/") {
161 return true
162 }
163
164 if !strings.HasPrefix(name, "pkg/tool/") {
165 return false
166 }
167
168 if !strings.HasPrefix(name, "pkg/tool/"+goosUnderGoarch+"/") {
169 return false
170 }
171
172 switch strings.TrimSuffix(path.Base(name), ".exe") {
173 default:
174 return false
175
176 case "asm", "cgo", "compile", "cover", "export", "fix", "link", "preprofile", "vet":
177 }
178 }
179 return true
180 })
181
182
183
184
185 binExes := []string{
186 "go",
187 "gofmt",
188 }
189 crossBin := "bin"
190 if goos != gohostos || goarch != gohostarch {
191 crossBin = "bin/" + goosUnderGoarch
192 }
193 for _, b := range binExes {
194 name := "bin/" + b + exe
195 src := filepath.Join(goroot, crossBin, b+exe)
196 info, err := os.Stat(src)
197 if err != nil {
198 log.Fatal(err)
199 }
200 binArch.Add(name, src, info)
201 }
202 binArch.Sort()
203 binArch.SetTime(versionTime)
204 binArch.SetMode(mode)
205
206 zipArch := binArch.Clone()
207 zipArch.AddPrefix("go")
208 testZip(zipArch)
209
210
211
212 modArch := binArch.Clone()
213 modArch.Remove(
214 "api/**",
215 "doc/**",
216 "misc/**",
217 "test/**",
218 )
219 modVers := modVersionPrefix + "-" + version + "." + goosDashGoarch
220 modArch.AddPrefix(modPath + "@" + modVers)
221 modArch.RenameGoMod()
222 modArch.Sort()
223 testMod(modArch)
224
225
226 distpack := func(name string) string {
227 return filepath.Join(goroot, "pkg/distpack", name)
228 }
229 if err := os.MkdirAll(filepath.Join(goroot, "pkg/distpack"), 0777); err != nil {
230 log.Fatal(err)
231 }
232
233 writeTgz(distpack(version+".src.tar.gz"), srcArch)
234
235 if goos == "windows" {
236 writeZip(distpack(version+"."+goos+"-"+goarch+".zip"), zipArch)
237 } else {
238 writeTgz(distpack(version+"."+goos+"-"+goarch+".tar.gz"), zipArch)
239 }
240
241 writeZip(distpack(modVers+".zip"), modArch)
242 writeFile(distpack(modVers+".mod"),
243 []byte(fmt.Sprintf("module %s\n", modPath)))
244 writeFile(distpack(modVers+".info"),
245 []byte(fmt.Sprintf("{%q:%q, %q:%q}\n",
246 "Version", modVers,
247 "Time", versionTime.Format(time.RFC3339))))
248 }
249
250
251 func mode(name string, _ fs.FileMode) fs.FileMode {
252 if strings.HasPrefix(name, "bin/") ||
253 strings.HasPrefix(name, "pkg/tool/") ||
254 strings.HasSuffix(name, ".bash") ||
255 strings.HasSuffix(name, ".sh") ||
256 strings.HasSuffix(name, ".pl") ||
257 strings.HasSuffix(name, ".rc") {
258 return 0o755
259 } else if ok, _ := amatch("**/go_?*_?*_exec", name); ok {
260 return 0o755
261 }
262 return 0o644
263 }
264
265
266
267
268
269 func readVERSION(goroot string) (version string, t time.Time) {
270 data, err := os.ReadFile(filepath.Join(goroot, "VERSION"))
271 if err != nil {
272 log.Fatal(err)
273 }
274 version, rest, _ := strings.Cut(string(data), "\n")
275 for line := range strings.SplitSeq(rest, "\n") {
276 f := strings.Fields(line)
277 if len(f) == 0 {
278 continue
279 }
280 switch f[0] {
281 default:
282 log.Fatalf("VERSION: unexpected line: %s", line)
283 case "time":
284 if len(f) != 2 {
285 log.Fatalf("VERSION: unexpected time line: %s", line)
286 }
287 t, err = time.ParseInLocation(time.RFC3339, f[1], time.UTC)
288 if err != nil {
289 log.Fatalf("VERSION: bad time: %s", err)
290 }
291 }
292 }
293 return version, t
294 }
295
296
297 func writeFile(name string, data []byte) {
298 if err := os.WriteFile(name, data, 0666); err != nil {
299 log.Fatal(err)
300 }
301 reportHash(name)
302 }
303
304
305
306
307 func check[T any](x T, err error) T {
308 check1(err)
309 return x
310 }
311
312
313
314
315 func check1(err error) {
316 if err != nil {
317 panic(err)
318 }
319 }
320
321
322 func writeTgz(name string, a *Archive) {
323 out, err := os.Create(name)
324 if err != nil {
325 log.Fatal(err)
326 }
327
328 var f File
329 defer func() {
330 if err := recover(); err != nil {
331 extra := ""
332 if f.Name != "" {
333 extra = " " + f.Name
334 }
335 log.Fatalf("writing %s%s: %v", name, extra, err)
336 }
337 }()
338
339 zw := check(gzip.NewWriterLevel(out, gzip.BestCompression))
340 tw := tar.NewWriter(zw)
341
342
343
344
345 var dirMode fs.FileMode
346 var mtime time.Time
347 for _, f := range a.Files {
348 dirMode = fs.ModeDir | f.Mode | (f.Mode&0444)>>2
349 mtime = f.Time
350 break
351 }
352
353
354
355
356 haveDir := map[string]bool{".": true}
357 var mkdirAll func(string)
358 mkdirAll = func(dir string) {
359 if dir == "/" {
360 panic("mkdirAll /")
361 }
362 if haveDir[dir] {
363 return
364 }
365 haveDir[dir] = true
366 mkdirAll(path.Dir(dir))
367 df := &File{
368 Name: dir + "/",
369 Time: mtime,
370 Mode: dirMode,
371 }
372 h := check(tar.FileInfoHeader(df.Info(), ""))
373 h.Name = dir + "/"
374 if err := tw.WriteHeader(h); err != nil {
375 panic(err)
376 }
377 }
378
379 for _, f = range a.Files {
380 h := check(tar.FileInfoHeader(f.Info(), ""))
381 mkdirAll(path.Dir(f.Name))
382 h.Name = f.Name
383 if err := tw.WriteHeader(h); err != nil {
384 panic(err)
385 }
386 r := check(os.Open(f.Src))
387 check(io.Copy(tw, r))
388 check1(r.Close())
389 }
390 f.Name = ""
391 check1(tw.Close())
392 check1(zw.Close())
393 check1(out.Close())
394 reportHash(name)
395 }
396
397
398 func writeZip(name string, a *Archive) {
399 out, err := os.Create(name)
400 if err != nil {
401 log.Fatal(err)
402 }
403
404 var f File
405 defer func() {
406 if err := recover(); err != nil {
407 extra := ""
408 if f.Name != "" {
409 extra = " " + f.Name
410 }
411 log.Fatalf("writing %s%s: %v", name, extra, err)
412 }
413 }()
414
415 zw := zip.NewWriter(out)
416 zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
417 return flate.NewWriter(out, flate.BestCompression)
418 })
419 for _, f = range a.Files {
420 h := check(zip.FileInfoHeader(f.Info()))
421 h.Name = f.Name
422 h.Method = zip.Deflate
423 w := check(zw.CreateHeader(h))
424 r := check(os.Open(f.Src))
425 check(io.Copy(w, r))
426 check1(r.Close())
427 }
428 f.Name = ""
429 check1(zw.Close())
430 check1(out.Close())
431 reportHash(name)
432 }
433
434 func reportHash(name string) {
435 f, err := os.Open(name)
436 if err != nil {
437 log.Fatal(err)
438 }
439 h := sha256.New()
440 io.Copy(h, f)
441 f.Close()
442 fmt.Printf("distpack: %x %s\n", h.Sum(nil)[:8], filepath.Base(name))
443 }
444
View as plain text