1// Copyright 2015 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// +build ignore 6 7// gen runs go generate on Unicode- and CLDR-related package in the text 8// repositories, taking into account dependencies and versions. 9package main 10 11import ( 12 "bytes" 13 "flag" 14 "fmt" 15 "go/build" 16 "io/ioutil" 17 "os" 18 "os/exec" 19 "path/filepath" 20 "runtime" 21 "strings" 22 "sync" 23 "unicode" 24 25 "golang.org/x/text/internal/gen" 26) 27 28var ( 29 verbose = flag.Bool("v", false, "verbose output") 30 force = flag.Bool("force", false, "ignore failing dependencies") 31 doCore = flag.Bool("core", false, "force an update to core") 32 excludeList = flag.String("exclude", "", 33 "comma-separated list of packages to exclude") 34 35 // The user can specify a selection of packages to build on the command line. 36 args []string 37) 38 39func exclude(pkg string) bool { 40 if len(args) > 0 { 41 return !contains(args, pkg) 42 } 43 return contains(strings.Split(*excludeList, ","), pkg) 44} 45 46// TODO: 47// - Better version handling. 48// - Generate tables for the core unicode package? 49// - Add generation for encodings. This requires some retooling here and there. 50// - Running repo-wide "long" tests. 51 52var vprintf = fmt.Printf 53 54func main() { 55 gen.Init() 56 args = flag.Args() 57 if !*verbose { 58 // Set vprintf to a no-op. 59 vprintf = func(string, ...interface{}) (int, error) { return 0, nil } 60 } 61 62 // TODO: create temporary cache directory to load files and create and set 63 // a "cache" option if the user did not specify the UNICODE_DIR environment 64 // variable. This will prevent duplicate downloads and also will enable long 65 // tests, which really need to be run after each generated package. 66 67 updateCore := *doCore 68 if gen.UnicodeVersion() != unicode.Version { 69 fmt.Printf("Requested Unicode version %s; core unicode version is %s.\n", 70 gen.UnicodeVersion(), 71 unicode.Version) 72 // TODO: use collate to compare. Simple comparison will work, though, 73 // until Unicode reaches version 10. To avoid circular dependencies, we 74 // could use the NumericWeighter without using package collate using a 75 // trivial Weighter implementation. 76 if gen.UnicodeVersion() < unicode.Version && !*force { 77 os.Exit(2) 78 } 79 updateCore = true 80 } 81 82 var unicode = &dependency{} 83 if updateCore { 84 fmt.Printf("Updating core to version %s...\n", gen.UnicodeVersion()) 85 unicode = generate("unicode") 86 87 // Test some users of the unicode packages, especially the ones that 88 // keep a mirrored table. These may need to be corrected by hand. 89 generate("regexp", unicode) 90 generate("strconv", unicode) // mimics Unicode table 91 generate("strings", unicode) 92 generate("testing", unicode) // mimics Unicode table 93 } 94 95 var ( 96 cldr = generate("./unicode/cldr", unicode) 97 language = generate("./language", cldr) 98 internal = generate("./internal", unicode, language) 99 norm = generate("./unicode/norm", unicode) 100 rangetable = generate("./unicode/rangetable", unicode) 101 cases = generate("./cases", unicode, norm, language, rangetable) 102 width = generate("./width", unicode) 103 bidi = generate("./unicode/bidi", unicode, norm, rangetable) 104 _ = generate("./secure/precis", unicode, norm, rangetable, cases, width, bidi) 105 _ = generate("./encoding/htmlindex", unicode, language) 106 _ = generate("./currency", unicode, cldr, language, internal) 107 _ = generate("./internal/number", unicode, cldr, language, internal) 108 _ = generate("./language/display", unicode, cldr, language, internal) 109 _ = generate("./collate", unicode, norm, cldr, language, rangetable) 110 _ = generate("./search", unicode, norm, cldr, language, rangetable) 111 ) 112 113 if updateCore { 114 copyVendored() 115 generate("vendor/golang_org/x/net/idna", unicode, norm, width, cases) 116 } 117 all.Wait() 118 119 if hasErrors { 120 fmt.Println("FAIL") 121 os.Exit(1) 122 } 123 vprintf("SUCCESS\n") 124} 125 126var ( 127 all sync.WaitGroup 128 hasErrors bool 129) 130 131type dependency struct { 132 sync.WaitGroup 133 hasErrors bool 134} 135 136func generate(pkg string, deps ...*dependency) *dependency { 137 var wg dependency 138 if exclude(pkg) { 139 return &wg 140 } 141 wg.Add(1) 142 all.Add(1) 143 go func() { 144 defer wg.Done() 145 defer all.Done() 146 // Wait for dependencies to finish. 147 for _, d := range deps { 148 d.Wait() 149 if d.hasErrors && !*force { 150 fmt.Printf("--- ABORT: %s\n", pkg) 151 wg.hasErrors = true 152 return 153 } 154 } 155 vprintf("=== GENERATE %s\n", pkg) 156 args := []string{"generate"} 157 if *verbose { 158 args = append(args, "-v") 159 } 160 args = append(args, pkg) 161 cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...) 162 w := &bytes.Buffer{} 163 cmd.Stderr = w 164 cmd.Stdout = w 165 if err := cmd.Run(); err != nil { 166 fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(w), err) 167 hasErrors = true 168 wg.hasErrors = true 169 return 170 } 171 172 vprintf("=== TEST %s\n", pkg) 173 args[0] = "test" 174 cmd = exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...) 175 wt := &bytes.Buffer{} 176 cmd.Stderr = wt 177 cmd.Stdout = wt 178 if err := cmd.Run(); err != nil { 179 fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(wt), err) 180 hasErrors = true 181 wg.hasErrors = true 182 return 183 } 184 vprintf("--- SUCCESS: %s\n\t%v\n", pkg, indent(w)) 185 fmt.Print(wt.String()) 186 }() 187 return &wg 188} 189 190func copyVendored() { 191 // Copy the vendored files. Some more may need to be copied in by hand. 192 dir := filepath.Join(build.Default.GOROOT, "src/vendor/golang_org/x/text") 193 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 194 if info.IsDir() { 195 return nil 196 } 197 b, err := ioutil.ReadFile(path[len(dir)+1:]) 198 if err != nil { 199 return err 200 } 201 vprintf("=== COPY %s\n", path) 202 b = bytes.Replace(b, []byte("golang.org"), []byte("golang_org"), -1) 203 return ioutil.WriteFile(path, b, 0666) 204 }) 205 if err != nil { 206 fmt.Println("Copying vendored files failed:", err) 207 os.Exit(1) 208 } 209} 210 211func contains(a []string, s string) bool { 212 for _, e := range a { 213 if s == e { 214 return true 215 } 216 } 217 return false 218} 219 220func indent(b *bytes.Buffer) string { 221 return strings.Replace(strings.TrimSpace(b.String()), "\n", "\n\t", -1) 222} 223