github.com/go-xe2/third@v1.0.3/golang.org/x/text/internal/gen/gen.go (about) 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 "github.com/go-xe2/third/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 "github.com/go-xe2/third/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("github.com/go-xe2/third/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 github.com/go-xe2/third/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 }