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