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