OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / text / internal / gen / gen.go
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 // Package gen contains common code for the various code generation tools in the
6 // text repository. Its usage ensures consistency between tools.
7 //
8 // This package defines command line flags that are common to most generation
9 // tools. The flags allow for specifying specific Unicode and CLDR versions
10 // in the public Unicode data repository (http://www.unicode.org/Public).
11 //
12 // A local Unicode data mirror can be set through the flag -local or the
13 // environment variable UNICODE_DIR. The former takes precedence. The local
14 // directory should follow the same structure as the public repository.
15 //
16 // IANA data can also optionally be mirrored by putting it in the iana directory
17 // rooted at the top of the local mirror. Beware, though, that IANA data is not
18 // versioned. So it is up to the developer to use the right version.
19 package gen // import "golang.org/x/text/internal/gen"
20
21 import (
22         "bytes"
23         "flag"
24         "fmt"
25         "go/build"
26         "go/format"
27         "io"
28         "io/ioutil"
29         "log"
30         "net/http"
31         "os"
32         "path"
33         "path/filepath"
34         "sync"
35         "unicode"
36
37         "golang.org/x/text/unicode/cldr"
38 )
39
40 var (
41         url = flag.String("url",
42                 "http://www.unicode.org/Public",
43                 "URL of Unicode database directory")
44         iana = flag.String("iana",
45                 "http://www.iana.org",
46                 "URL of the IANA repository")
47         unicodeVersion = flag.String("unicode",
48                 getEnv("UNICODE_VERSION", unicode.Version),
49                 "unicode version to use")
50         cldrVersion = flag.String("cldr",
51                 getEnv("CLDR_VERSION", cldr.Version),
52                 "cldr version to use")
53 )
54
55 func getEnv(name, def string) string {
56         if v := os.Getenv(name); v != "" {
57                 return v
58         }
59         return def
60 }
61
62 // Init performs common initialization for a gen command. It parses the flags
63 // and sets up the standard logging parameters.
64 func Init() {
65         log.SetPrefix("")
66         log.SetFlags(log.Lshortfile)
67         flag.Parse()
68 }
69
70 const header = `// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
71
72 package %s
73
74 `
75
76 // UnicodeVersion reports the requested Unicode version.
77 func UnicodeVersion() string {
78         return *unicodeVersion
79 }
80
81 // UnicodeVersion reports the requested CLDR version.
82 func CLDRVersion() string {
83         return *cldrVersion
84 }
85
86 // IsLocal reports whether data files are available locally.
87 func IsLocal() bool {
88         dir, err := localReadmeFile()
89         if err != nil {
90                 return false
91         }
92         if _, err = os.Stat(dir); err != nil {
93                 return false
94         }
95         return true
96 }
97
98 // OpenUCDFile opens the requested UCD file. The file is specified relative to
99 // the public Unicode root directory. It will call log.Fatal if there are any
100 // errors.
101 func OpenUCDFile(file string) io.ReadCloser {
102         return openUnicode(path.Join(*unicodeVersion, "ucd", file))
103 }
104
105 // OpenCLDRCoreZip opens the CLDR core zip file. It will call log.Fatal if there
106 // are any errors.
107 func OpenCLDRCoreZip() io.ReadCloser {
108         return OpenUnicodeFile("cldr", *cldrVersion, "core.zip")
109 }
110
111 // OpenUnicodeFile opens the requested file of the requested category from the
112 // root of the Unicode data archive. The file is specified relative to the
113 // public Unicode root directory. If version is "", it will use the default
114 // Unicode version. It will call log.Fatal if there are any errors.
115 func OpenUnicodeFile(category, version, file string) io.ReadCloser {
116         if version == "" {
117                 version = UnicodeVersion()
118         }
119         return openUnicode(path.Join(category, version, file))
120 }
121
122 // OpenIANAFile opens the requested IANA file. The file is specified relative
123 // to the IANA root, which is typically either http://www.iana.org or the
124 // iana directory in the local mirror. It will call log.Fatal if there are any
125 // errors.
126 func OpenIANAFile(path string) io.ReadCloser {
127         return Open(*iana, "iana", path)
128 }
129
130 var (
131         dirMutex sync.Mutex
132         localDir string
133 )
134
135 const permissions = 0755
136
137 func localReadmeFile() (string, error) {
138         p, err := build.Import("golang.org/x/text", "", build.FindOnly)
139         if err != nil {
140                 return "", fmt.Errorf("Could not locate package: %v", err)
141         }
142         return filepath.Join(p.Dir, "DATA", "README"), nil
143 }
144
145 func getLocalDir() string {
146         dirMutex.Lock()
147         defer dirMutex.Unlock()
148
149         readme, err := localReadmeFile()
150         if err != nil {
151                 log.Fatal(err)
152         }
153         dir := filepath.Dir(readme)
154         if _, err := os.Stat(readme); err != nil {
155                 if err := os.MkdirAll(dir, permissions); err != nil {
156                         log.Fatalf("Could not create directory: %v", err)
157                 }
158                 ioutil.WriteFile(readme, []byte(readmeTxt), permissions)
159         }
160         return dir
161 }
162
163 const readmeTxt = `Generated by golang.org/x/text/internal/gen. DO NOT EDIT.
164
165 This directory contains downloaded files used to generate the various tables
166 in the golang.org/x/text subrepo.
167
168 Note that the language subtag repo (iana/assignments/language-subtag-registry)
169 and all other times in the iana subdirectory are not versioned and will need
170 to be periodically manually updated. The easiest way to do this is to remove
171 the entire iana directory. This is mostly of concern when updating the language
172 package.
173 `
174
175 // Open opens subdir/path if a local directory is specified and the file exists,
176 // where subdir is a directory relative to the local root, or fetches it from
177 // urlRoot/path otherwise. It will call log.Fatal if there are any errors.
178 func Open(urlRoot, subdir, path string) io.ReadCloser {
179         file := filepath.Join(getLocalDir(), subdir, filepath.FromSlash(path))
180         return open(file, urlRoot, path)
181 }
182
183 func openUnicode(path string) io.ReadCloser {
184         file := filepath.Join(getLocalDir(), filepath.FromSlash(path))
185         return open(file, *url, path)
186 }
187
188 // TODO: automatically periodically update non-versioned files.
189
190 func open(file, urlRoot, path string) io.ReadCloser {
191         if f, err := os.Open(file); err == nil {
192                 return f
193         }
194         r := get(urlRoot, path)
195         defer r.Close()
196         b, err := ioutil.ReadAll(r)
197         if err != nil {
198                 log.Fatalf("Could not download file: %v", err)
199         }
200         os.MkdirAll(filepath.Dir(file), permissions)
201         if err := ioutil.WriteFile(file, b, permissions); err != nil {
202                 log.Fatalf("Could not create file: %v", err)
203         }
204         return ioutil.NopCloser(bytes.NewReader(b))
205 }
206
207 func get(root, path string) io.ReadCloser {
208         url := root + "/" + path
209         fmt.Printf("Fetching %s...", url)
210         defer fmt.Println(" done.")
211         resp, err := http.Get(url)
212         if err != nil {
213                 log.Fatalf("HTTP GET: %v", err)
214         }
215         if resp.StatusCode != 200 {
216                 log.Fatalf("Bad GET status for %q: %q", url, resp.Status)
217         }
218         return resp.Body
219 }
220
221 // TODO: use Write*Version in all applicable packages.
222
223 // WriteUnicodeVersion writes a constant for the Unicode version from which the
224 // tables are generated.
225 func WriteUnicodeVersion(w io.Writer) {
226         fmt.Fprintf(w, "// UnicodeVersion is the Unicode version from which the tables in this package are derived.\n")
227         fmt.Fprintf(w, "const UnicodeVersion = %q\n\n", UnicodeVersion())
228 }
229
230 // WriteCLDRVersion writes a constant for the CLDR version from which the
231 // tables are generated.
232 func WriteCLDRVersion(w io.Writer) {
233         fmt.Fprintf(w, "// CLDRVersion is the CLDR version from which the tables in this package are derived.\n")
234         fmt.Fprintf(w, "const CLDRVersion = %q\n\n", CLDRVersion())
235 }
236
237 // WriteGoFile prepends a standard file comment and package statement to the
238 // given bytes, applies gofmt, and writes them to a file with the given name.
239 // It will call log.Fatal if there are any errors.
240 func WriteGoFile(filename, pkg string, b []byte) {
241         w, err := os.Create(filename)
242         if err != nil {
243                 log.Fatalf("Could not create file %s: %v", filename, err)
244         }
245         defer w.Close()
246         if _, err = WriteGo(w, pkg, b); err != nil {
247                 log.Fatalf("Error writing file %s: %v", filename, err)
248         }
249 }
250
251 // WriteGo prepends a standard file comment and package statement to the given
252 // bytes, applies gofmt, and writes them to w.
253 func WriteGo(w io.Writer, pkg string, b []byte) (n int, err error) {
254         src := []byte(fmt.Sprintf(header, pkg))
255         src = append(src, b...)
256         formatted, err := format.Source(src)
257         if err != nil {
258                 // Print the generated code even in case of an error so that the
259                 // returned error can be meaningfully interpreted.
260                 n, _ = w.Write(src)
261                 return n, err
262         }
263         return w.Write(formatted)
264 }
265
266 // Repackage rewrites a Go file from belonging to package main to belonging to
267 // the given package.
268 func Repackage(inFile, outFile, pkg string) {
269         src, err := ioutil.ReadFile(inFile)
270         if err != nil {
271                 log.Fatalf("reading %s: %v", inFile, err)
272         }
273         const toDelete = "package main\n\n"
274         i := bytes.Index(src, []byte(toDelete))
275         if i < 0 {
276                 log.Fatalf("Could not find %q in %s.", toDelete, inFile)
277         }
278         w := &bytes.Buffer{}
279         w.Write(src[i+len(toDelete):])
280         WriteGoFile(outFile, pkg, w.Bytes())
281 }