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.
7 // gen runs go generate on Unicode- and CLDR-related package in the text
8 // repositories, taking into account dependencies and versions.
28 "golang.org/x/text/internal/gen"
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 excludeList = flag.String("exclude", "",
36 "comma-separated list of packages to exclude")
38 // The user can specify a selection of packages to build on the command line.
42 func exclude(pkg string) bool {
44 return !contains(args, pkg)
46 return contains(strings.Split(*excludeList, ","), pkg)
50 // - Better version handling.
51 // - Generate tables for the core unicode package?
52 // - Add generation for encodings. This requires some retooling here and there.
53 // - Running repo-wide "long" tests.
55 var vprintf = fmt.Printf
61 // Set vprintf to a no-op.
62 vprintf = func(string, ...interface{}) (int, error) { return 0, nil }
65 // TODO: create temporary cache directory to load files and create and set
66 // a "cache" option if the user did not specify the UNICODE_DIR environment
67 // variable. This will prevent duplicate downloads and also will enable long
68 // tests, which really need to be run after each generated package.
71 if gen.UnicodeVersion() != unicode.Version {
72 fmt.Printf("Requested Unicode version %s; core unicode version is %s.\n",
75 // TODO: use collate to compare. Simple comparison will work, though,
76 // until Unicode reaches version 10. To avoid circular dependencies, we
77 // could use the NumericWeighter without using package collate using a
78 // trivial Weighter implementation.
79 if gen.UnicodeVersion() < unicode.Version && !*force {
85 var unicode = &dependency{}
87 fmt.Printf("Updating core to version %s...\n", gen.UnicodeVersion())
88 unicode = generate("unicode")
90 // Test some users of the unicode packages, especially the ones that
91 // keep a mirrored table. These may need to be corrected by hand.
92 generate("regexp", unicode)
93 generate("strconv", unicode) // mimics Unicode table
94 generate("strings", unicode)
95 generate("testing", unicode) // mimics Unicode table
99 cldr = generate("./unicode/cldr", unicode)
100 language = generate("./language", cldr)
101 internal = generate("./internal", unicode, language)
102 norm = generate("./unicode/norm", unicode)
103 rangetable = generate("./unicode/rangetable", unicode)
104 cases = generate("./cases", unicode, norm, language, rangetable)
105 width = generate("./width", unicode)
106 bidi = generate("./unicode/bidi", unicode, norm, rangetable)
107 mib = generate("./encoding/internal/identifier", unicode)
108 _ = generate("./encoding/htmlindex", unicode, language, mib)
109 _ = generate("./encoding/ianaindex", unicode, language, mib)
110 _ = generate("./secure/precis", unicode, norm, rangetable, cases, width, bidi)
111 _ = generate("./currency", unicode, cldr, language, internal)
112 _ = generate("./internal/number", unicode, cldr, language, internal)
113 _ = generate("./feature/plural", unicode, cldr, language, internal)
114 _ = generate("./internal/export/idna", unicode, bidi, norm)
115 _ = generate("./language/display", unicode, cldr, language, internal)
116 _ = generate("./collate", unicode, norm, cldr, language, rangetable)
117 _ = generate("./search", unicode, norm, cldr, language, rangetable)
121 // Copy exported packages to the destination golang.org repo.
122 copyExported("golang.org/x/net/idna")
140 type dependency struct {
145 func generate(pkg string, deps ...*dependency) *dependency {
155 // Wait for dependencies to finish.
156 for _, d := range deps {
158 if d.hasErrors && !*force {
159 fmt.Printf("--- ABORT: %s\n", pkg)
164 vprintf("=== GENERATE %s\n", pkg)
165 args := []string{"generate"}
167 args = append(args, "-v")
169 args = append(args, pkg)
170 cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
174 if err := cmd.Run(); err != nil {
175 fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(w), err)
181 vprintf("=== TEST %s\n", pkg)
183 cmd = exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), args...)
184 wt := &bytes.Buffer{}
187 if err := cmd.Run(); err != nil {
188 fmt.Printf("--- FAIL: %s:\n\t%v\n\tError: %v\n", pkg, indent(wt), err)
193 vprintf("--- SUCCESS: %s\n\t%v\n", pkg, indent(w))
194 fmt.Print(wt.String())
199 // copyExported copies a package in x/text/internal/export to the
200 // destination repository.
201 func copyExported(p string) {
203 filepath.Join("internal", "export", path.Base(p)),
204 filepath.Join("..", filepath.FromSlash(p[len("golang.org/x"):])),
205 "golang.org/x/text/internal/export/"+path.Base(p),
209 // copyVendored copies packages used by Go core into the vendored directory.
210 func copyVendored() {
211 root := filepath.Join(build.Default.GOROOT, filepath.FromSlash("src/vendor/golang_org/x"))
213 err := filepath.Walk(root, func(dir string, info os.FileInfo, err error) error {
214 if err != nil || !info.IsDir() || root == dir {
217 src := dir[len(root)+1:]
218 const slash = string(filepath.Separator)
219 if c := strings.Split(src, slash); c[0] == "text" {
220 // Copy a text repo package from its normal location.
221 src = strings.Join(c[1:], slash)
223 // Copy the vendored package if it exists in the export directory.
224 src = filepath.Join("internal", "export", filepath.Base(src))
226 copyPackage(src, dir, "golang.org", "golang_org")
230 fmt.Printf("Seeding directory %s has failed %v:", root, err)
235 // goGenRE is used to remove go:generate lines.
236 var goGenRE = regexp.MustCompile("//go:generate[^\n]*\n")
238 // copyPackage copies relevant files from a directory in x/text to the
239 // destination package directory. The destination package is assumed to have
240 // the same name. For each copied file go:generate lines are removed and
241 // and package comments are rewritten to the new path.
242 func copyPackage(dirSrc, dirDst, search, replace string) {
243 err := filepath.Walk(dirSrc, func(file string, info os.FileInfo, err error) error {
244 base := filepath.Base(file)
245 if err != nil || info.IsDir() ||
246 !strings.HasSuffix(base, ".go") ||
247 strings.HasSuffix(base, "_test.go") && !strings.HasPrefix(base, "example") ||
248 // Don't process subdirectories.
249 filepath.Dir(file) != dirSrc {
252 b, err := ioutil.ReadFile(file)
253 if err != nil || bytes.Contains(b, []byte("\n// +build ignore")) {
257 b = bytes.Replace(b, []byte(search), []byte(replace), -1)
258 // Remove go:generate lines.
259 b = goGenRE.ReplaceAllLiteral(b, nil)
260 comment := "// Code generated by running \"go generate\" in golang.org/x/text. DO NOT EDIT.\n\n"
262 comment = "// Code generated by running \"go run gen.go -core\" in golang.org/x/text. DO NOT EDIT.\n\n"
264 if !bytes.HasPrefix(b, []byte(comment)) {
265 b = append([]byte(comment), b...)
267 if b, err = format.Source(b); err != nil {
268 fmt.Println("Failed to format file:", err)
271 file = filepath.Join(dirDst, base)
272 vprintf("=== COPY %s\n", file)
273 return ioutil.WriteFile(file, b, 0666)
276 fmt.Println("Copying exported files failed:", err)
281 func contains(a []string, s string) bool {
282 for _, e := range a {
290 func indent(b *bytes.Buffer) string {
291 return strings.Replace(strings.TrimSpace(b.String()), "\n", "\n\t", -1)