1
2
3
4
5
6
7
8
9 package main
10
11 import (
12 "bytes"
13 "flag"
14 "fmt"
15 "go/format"
16 "os"
17 "os/exec"
18 "path"
19 "path/filepath"
20 "regexp"
21 "runtime"
22 "strings"
23 "sync"
24 "unicode"
25
26 "golang.org/x/text/collate"
27 "golang.org/x/text/internal/gen"
28 "golang.org/x/text/language"
29 )
30
31 var (
32 verbose = flag.Bool("v", false, "verbose output")
33 force = flag.Bool("force", false, "ignore failing dependencies")
34 doCore = flag.Bool("core", false, "force an update to core")
35 skipTest = flag.Bool("skiptest", false, "skip tests")
36 excludeList = flag.String("exclude", "",
37 "comma-separated list of packages to exclude")
38
39
40 args []string
41 )
42
43 func exclude(pkg string) bool {
44 if len(args) > 0 {
45 return !contains(args, pkg)
46 }
47 return contains(strings.Split(*excludeList, ","), pkg)
48 }
49
50
51
52
53
54
55
56 var vprintf = fmt.Printf
57
58 func main() {
59 gen.Init()
60 args = flag.Args()
61 if !*verbose {
62
63 vprintf = func(string, ...interface{}) (int, error) { return 0, nil }
64 }
65
66
67
68
69
70
71 updateCore := *doCore
72 if gen.UnicodeVersion() != unicode.Version {
73 fmt.Printf("Requested Unicode version %s; core unicode version is %s.\n",
74 gen.UnicodeVersion(),
75 unicode.Version)
76 c := collate.New(language.Und, collate.Numeric)
77 if c.CompareString(gen.UnicodeVersion(), unicode.Version) < 0 && !*force {
78 os.Exit(2)
79 }
80 updateCore = true
81 goroot := os.Getenv("GOROOT")
82 appendToFile(
83 filepath.Join(goroot, "api", "except.txt"),
84 fmt.Sprintf("pkg unicode, const Version = %q\n", unicode.Version),
85 )
86 const lines = `pkg unicode, const Version = %q
87 // TODO: add a new line of the following form for each new script and property.
88 pkg unicode, var <new script or property> *RangeTable
89 `
90 appendToFile(
91 filepath.Join(goroot, "api", "next.txt"),
92 fmt.Sprintf(lines, gen.UnicodeVersion()),
93 )
94 }
95
96 var unicode = &dependency{}
97 if updateCore {
98 fmt.Printf("Updating core to version %s...\n", gen.UnicodeVersion())
99 unicodeInternal := generate("./internal/export/unicode")
100 unicode = generate("unicode", unicodeInternal)
101
102
103
104 generate("regexp", unicode)
105 generate("strconv", unicode)
106 generate("strings", unicode)
107 generate("testing", unicode)
108 }
109
110 var (
111 cldr = generate("./unicode/cldr", unicode)
112 intlang = generate("./internal/language", cldr)
113 compact = generate("./internal/language/compact", intlang, cldr)
114 language = generate("./language", cldr, compact)
115 internal = generate("./internal", unicode, language)
116 norm = generate("./unicode/norm", unicode)
117 rangetable = generate("./unicode/rangetable", unicode)
118 cases = generate("./cases", unicode, norm, language, rangetable)
119 width = generate("./width", unicode)
120 bidi = generate("./unicode/bidi", unicode, norm, rangetable)
121 mib = generate("./encoding/internal/identifier", unicode)
122 number = generate("./internal/number", unicode, cldr, language, internal)
123 cldrtree = generate("./internal/cldrtree", language, internal)
124 _ = generate("./unicode/runenames", unicode)
125 _ = generate("./encoding/htmlindex", unicode, language, mib)
126 _ = generate("./encoding/ianaindex", unicode, language, mib)
127 _ = generate("./secure/precis", unicode, norm, rangetable, cases, width, bidi)
128 _ = generate("./currency", unicode, cldr, language, internal, number)
129 _ = generate("./feature/plural", unicode, cldr, language, internal, number)
130 _ = generate("./internal/export/idna", unicode, bidi, norm)
131 _ = generate("./language/display", unicode, cldr, language, internal, number)
132 _ = generate("./collate", unicode, norm, cldr, language, rangetable)
133 _ = generate("./search", unicode, norm, cldr, language, rangetable)
134 _ = generate("./date", cldr, language, cldrtree)
135 )
136 all.Wait()
137
138
139 copyExported("golang.org/x/net/idna")
140
141 if hasErrors {
142 fmt.Println("FAIL")
143 os.Exit(1)
144 }
145 vprintf("SUCCESS\n")
146 }
147
148 func appendToFile(file, text string) {
149 fmt.Println("Augmenting", file)
150 w, err := os.OpenFile(file, os.O_APPEND|os.O_WRONLY, 0600)
151 if err != nil {
152 fmt.Println("Failed to open file:", err)
153 os.Exit(1)
154 }
155 defer w.Close()
156 if _, err := w.WriteString(text); err != nil {
157 fmt.Println("Failed to write to file:", err)
158 os.Exit(1)
159 }
160 }
161
162 var (
163 all sync.WaitGroup
164 hasErrors bool
165 )
166
167 type dependency struct {
168 sync.WaitGroup
169 hasErrors bool
170 }
171
172 func generate(pkg string, deps ...*dependency) *dependency {
173 var wg dependency
174 if exclude(pkg) {
175 return &wg
176 }
177 wg.Add(1)
178 all.Add(1)
179 go func() {
180 defer wg.Done()
181 defer all.Done()
182
183 for _, d := range deps {
184 d.Wait()
185 if d.hasErrors && !*force {
186 fmt.Printf("--- ABORT: %s\n", pkg)
187 wg.hasErrors = true
188 return
189 }
190 }
191 vprintf("=== GENERATE %s\n", pkg)
192 args := []string{"generate"}
193 if *verbose {
194 args = append(args, "-v")
195 }
196 args = append(args, pkg)
197 cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
198 w := &bytes.Buffer{}
199 cmd.Stderr = w
200 cmd.Stdout = w
201 if err := cmd.Run(); err != nil {
202 fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(w), err)
203 hasErrors = true
204 wg.hasErrors = true
205 return
206 }
207
208 if *skipTest {
209 return
210 }
211
212 vprintf("=== TEST %s\n", pkg)
213 args[0] = "test"
214 cmd = exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
215 wt := &bytes.Buffer{}
216 cmd.Stderr = wt
217 cmd.Stdout = wt
218 if err := cmd.Run(); err != nil {
219 fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(wt), err)
220 hasErrors = true
221 wg.hasErrors = true
222 return
223 }
224 vprintf("--- SUCCESS: %s\n\t%v\n", pkg, indent(w))
225 fmt.Print(wt.String())
226 }()
227 return &wg
228 }
229
230
231
232 func copyExported(p string) {
233 copyPackage(
234 filepath.Join("internal", "export", path.Base(p)),
235 filepath.Join("..", filepath.FromSlash(p[len("golang.org/x"):])),
236 "golang.org/x/text/internal/export/"+path.Base(p),
237 p)
238 }
239
240
241 var goGenRE = regexp.MustCompile("//go:generate[^\n]*\n")
242
243
244
245
246
247 func copyPackage(dirSrc, dirDst, search, replace string) {
248 err := filepath.Walk(dirSrc, func(file string, info os.FileInfo, err error) error {
249 base := filepath.Base(file)
250 if err != nil || info.IsDir() ||
251 !strings.HasSuffix(base, ".go") ||
252 strings.HasSuffix(base, "_test.go") ||
253
254 filepath.Dir(file) != dirSrc {
255 return nil
256 }
257 b, err := os.ReadFile(file)
258 if err != nil || bytes.Contains(b, []byte("\n//go:build ignore")) {
259 return err
260 }
261
262 b = bytes.Replace(b, []byte(search), []byte(replace), -1)
263 b = bytes.Replace(b, []byte("internal/export"), []byte(""), -1)
264
265 b = goGenRE.ReplaceAllLiteral(b, nil)
266 comment := "// Code generated by running \"go generate\" in golang.org/x/text. DO NOT EDIT.\n\n"
267 if !bytes.HasPrefix(b, []byte(comment)) {
268 b = append([]byte(comment), b...)
269 }
270 if b, err = format.Source(b); err != nil {
271 fmt.Println("Failed to format file:", err)
272 os.Exit(1)
273 }
274 file = filepath.Join(dirDst, base)
275 vprintf("=== COPY %s\n", file)
276 return os.WriteFile(file, b, 0666)
277 })
278 if err != nil {
279 fmt.Println("Copying exported files failed:", err)
280 os.Exit(1)
281 }
282 }
283
284 func contains(a []string, s string) bool {
285 for _, e := range a {
286 if s == e {
287 return true
288 }
289 }
290 return false
291 }
292
293 func indent(b *bytes.Buffer) string {
294 return strings.Replace(strings.TrimSpace(b.String()), "\n", "\n\t", -1)
295 }
296
View as plain text