1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// This file contains tests applied to the archives before they are written.
6
7package main
8
9import (
10	"bytes"
11	"fmt"
12	"log"
13	"os"
14	"path"
15	"path/filepath"
16	"strings"
17)
18
19type testRule struct {
20	name    string
21	goos    string
22	exclude bool
23}
24
25var srcRules = []testRule{
26	{name: "go/VERSION"},
27	{name: "go/src/cmd/go/main.go"},
28	{name: "go/src/bytes/bytes.go"},
29	{name: "**/.DS_Store", exclude: true},
30	{name: "go/.git", exclude: true},
31	{name: "go/.gitattributes", exclude: true},
32	{name: "go/.github", exclude: true},
33	{name: "go/VERSION.cache", exclude: true},
34	{name: "go/bin/**", exclude: true},
35	{name: "go/pkg/**", exclude: true},
36	{name: "go/src/cmd/dist/dist", exclude: true},
37	{name: "go/src/cmd/dist/dist.exe", exclude: true},
38	{name: "go/src/runtime/internal/sys/zversion.go", exclude: true},
39	{name: "go/src/time/tzdata/zzipdata.go", exclude: true},
40}
41
42var zipRules = []testRule{
43	{name: "go/VERSION"},
44	{name: "go/src/cmd/go/main.go"},
45	{name: "go/src/bytes/bytes.go"},
46
47	{name: "**/.DS_Store", exclude: true},
48	{name: "go/.git", exclude: true},
49	{name: "go/.gitattributes", exclude: true},
50	{name: "go/.github", exclude: true},
51	{name: "go/VERSION.cache", exclude: true},
52	{name: "go/bin", exclude: true},
53	{name: "go/pkg", exclude: true},
54	{name: "go/src/cmd/dist/dist", exclude: true},
55	{name: "go/src/cmd/dist/dist.exe", exclude: true},
56
57	{name: "go/bin/go", goos: "linux"},
58	{name: "go/bin/go", goos: "darwin"},
59	{name: "go/bin/go", goos: "windows", exclude: true},
60	{name: "go/bin/go.exe", goos: "windows"},
61	{name: "go/bin/gofmt", goos: "linux"},
62	{name: "go/bin/gofmt", goos: "darwin"},
63	{name: "go/bin/gofmt", goos: "windows", exclude: true},
64	{name: "go/bin/gofmt.exe", goos: "windows"},
65	{name: "go/pkg/tool/*/compile", goos: "linux"},
66	{name: "go/pkg/tool/*/compile", goos: "darwin"},
67	{name: "go/pkg/tool/*/compile", goos: "windows", exclude: true},
68	{name: "go/pkg/tool/*/compile.exe", goos: "windows"},
69}
70
71var modRules = []testRule{
72	{name: "golang.org/toolchain@*/VERSION"},
73	{name: "golang.org/toolchain@*/src/cmd/go/main.go"},
74	{name: "golang.org/toolchain@*/src/bytes/bytes.go"},
75
76	{name: "**/.DS_Store", exclude: true},
77	{name: "golang.org/toolchain@*/.git", exclude: true},
78	{name: "golang.org/toolchain@*/.gitattributes", exclude: true},
79	{name: "golang.org/toolchain@*/.github", exclude: true},
80	{name: "golang.org/toolchain@*/VERSION.cache", exclude: true},
81	{name: "golang.org/toolchain@*/bin", exclude: true},
82	{name: "golang.org/toolchain@*/pkg", exclude: true},
83	{name: "golang.org/toolchain@*/src/cmd/dist/dist", exclude: true},
84	{name: "golang.org/toolchain@*/src/cmd/dist/dist.exe", exclude: true},
85
86	{name: "golang.org/toolchain@*/bin/go", goos: "linux"},
87	{name: "golang.org/toolchain@*/bin/go", goos: "darwin"},
88	{name: "golang.org/toolchain@*/bin/go", goos: "windows", exclude: true},
89	{name: "golang.org/toolchain@*/bin/go.exe", goos: "windows"},
90	{name: "golang.org/toolchain@*/bin/gofmt", goos: "linux"},
91	{name: "golang.org/toolchain@*/bin/gofmt", goos: "darwin"},
92	{name: "golang.org/toolchain@*/bin/gofmt", goos: "windows", exclude: true},
93	{name: "golang.org/toolchain@*/bin/gofmt.exe", goos: "windows"},
94	{name: "golang.org/toolchain@*/pkg/tool/*/compile", goos: "linux"},
95	{name: "golang.org/toolchain@*/pkg/tool/*/compile", goos: "darwin"},
96	{name: "golang.org/toolchain@*/pkg/tool/*/compile", goos: "windows", exclude: true},
97	{name: "golang.org/toolchain@*/pkg/tool/*/compile.exe", goos: "windows"},
98
99	// go.mod are renamed to _go.mod.
100	{name: "**/go.mod", exclude: true},
101	{name: "**/_go.mod"},
102}
103
104func testSrc(a *Archive) {
105	test("source", a, srcRules)
106
107	// Check that no generated files slip in, even if new ones are added.
108	for _, f := range a.Files {
109		if strings.HasPrefix(path.Base(f.Name), "z") {
110			data, err := os.ReadFile(filepath.Join(goroot, strings.TrimPrefix(f.Name, "go/")))
111			if err != nil {
112				log.Fatalf("checking source archive: %v", err)
113			}
114			if strings.Contains(string(data), "generated by go tool dist; DO NOT EDIT") {
115				log.Fatalf("unexpected source archive file: %s (generated by dist)", f.Name)
116			}
117		}
118	}
119}
120
121func testZip(a *Archive) { test("binary", a, zipRules) }
122func testMod(a *Archive) { test("module", a, modRules) }
123
124func test(kind string, a *Archive, rules []testRule) {
125	ok := true
126	have := make([]bool, len(rules))
127	for _, f := range a.Files {
128		for i, r := range rules {
129			if r.goos != "" && r.goos != goos {
130				continue
131			}
132			match, err := amatch(r.name, f.Name)
133			if err != nil {
134				log.Fatal(err)
135			}
136			if match {
137				if r.exclude {
138					ok = false
139					if !have[i] {
140						log.Printf("unexpected %s archive file: %s", kind, f.Name)
141						have[i] = true // silence future prints for excluded directory
142					}
143				} else {
144					have[i] = true
145				}
146			}
147		}
148	}
149	missing := false
150	for i, r := range rules {
151		if r.goos != "" && r.goos != goos {
152			continue
153		}
154		if !r.exclude && !have[i] {
155			missing = true
156			log.Printf("missing %s archive file: %s", kind, r.name)
157		}
158	}
159	if missing {
160		ok = false
161		var buf bytes.Buffer
162		for _, f := range a.Files {
163			fmt.Fprintf(&buf, "\n\t%s", f.Name)
164		}
165		log.Printf("archive contents: %d files%s", len(a.Files), buf.Bytes())
166	}
167	if !ok {
168		log.Fatalf("bad archive file")
169	}
170}
171