github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/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 "golang.org/x/text/internal/gen"
    20  
    21  import (
    22  	"bytes"
    23  	"flag"
    24  	"fmt"
    25  	"go/format"
    26  	"io"
    27  	"io/ioutil"
    28  	"log"
    29  	"net/http"
    30  	"os"
    31  	"path"
    32  	"path/filepath"
    33  	"unicode"
    34  
    35  	"golang.org/x/text/unicode/cldr"
    36  )
    37  
    38  var (
    39  	url = flag.String("url",
    40  		"http://www.unicode.org/Public",
    41  		"URL of Unicode database directory")
    42  	iana = flag.String("iana",
    43  		"http://www.iana.org",
    44  		"URL of the IANA repository")
    45  	unicodeVersion = flag.String("unicode",
    46  		getEnv("UNICODE_VERSION", unicode.Version),
    47  		"unicode version to use")
    48  	cldrVersion = flag.String("cldr",
    49  		getEnv("CLDR_VERSION", cldr.Version),
    50  		"cldr version to use")
    51  	// Allow an environment variable to specify the local directory.
    52  	// go generate doesn't allow specifying arguments; this is a useful
    53  	// alternative to specifying a local mirror.
    54  	localDir = flag.String("local",
    55  		os.Getenv("UNICODE_DIR"),
    56  		"directory containing local data files; for debugging only.")
    57  )
    58  
    59  func getEnv(name, def string) string {
    60  	if v := os.Getenv(name); v != "" {
    61  		return v
    62  	}
    63  	return def
    64  }
    65  
    66  // Init performs common initialization for a gen command. It parses the flags
    67  // and sets up the standard logging parameters.
    68  func Init() {
    69  	log.SetPrefix("")
    70  	log.SetFlags(log.Lshortfile)
    71  	flag.Parse()
    72  }
    73  
    74  const header = `// This file was generated by go generate; DO NOT EDIT
    75  
    76  package %s
    77  
    78  `
    79  
    80  // UnicodeVersion reports the requested Unicode version.
    81  func UnicodeVersion() string {
    82  	return *unicodeVersion
    83  }
    84  
    85  // UnicodeVersion reports the requested CLDR version.
    86  func CLDRVersion() string {
    87  	return *cldrVersion
    88  }
    89  
    90  // IsLocal reports whether the user specified a local directory.
    91  func IsLocal() bool {
    92  	return *localDir != ""
    93  }
    94  
    95  // OpenUCDFile opens the requested UCD file. The file is specified relative to
    96  // the public Unicode root directory. It will call log.Fatal if there are any
    97  // errors.
    98  func OpenUCDFile(file string) io.ReadCloser {
    99  	return openUnicode(path.Join(*unicodeVersion, "ucd", file))
   100  }
   101  
   102  // OpenCLDRCoreZip opens the CLDR core zip file. It will call log.Fatal if there
   103  // are any errors.
   104  func OpenCLDRCoreZip() io.ReadCloser {
   105  	return OpenUnicodeFile("cldr", *cldrVersion, "core.zip")
   106  }
   107  
   108  // OpenUnicodeFile opens the requested file of the requested category from the
   109  // root of the Unicode data archive. The file is specified relative to the
   110  // public Unicode root directory. If version is "", it will use the default
   111  // Unicode version. It will call log.Fatal if there are any errors.
   112  func OpenUnicodeFile(category, version, file string) io.ReadCloser {
   113  	if version == "" {
   114  		version = UnicodeVersion()
   115  	}
   116  	return openUnicode(path.Join(category, version, file))
   117  }
   118  
   119  // OpenIANAFile opens the requested IANA file. The file is specified relative
   120  // to the IANA root, which is typically either http://www.iana.org or the
   121  // iana directory in the local mirror. It will call log.Fatal if there are any
   122  // errors.
   123  func OpenIANAFile(path string) io.ReadCloser {
   124  	return Open(*iana, "iana", path)
   125  }
   126  
   127  // Open opens subdir/path if a local directory is specified and the file exists,
   128  // where subdir is a directory relative to the local root, or fetches it from
   129  // urlRoot/path otherwise. It will call log.Fatal if there are any errors.
   130  func Open(urlRoot, subdir, path string) io.ReadCloser {
   131  	if *localDir != "" {
   132  		path = filepath.FromSlash(path)
   133  		if f, err := os.Open(filepath.Join(*localDir, subdir, path)); err == nil {
   134  			return f
   135  		}
   136  	}
   137  	return get(urlRoot, path)
   138  }
   139  
   140  func openUnicode(path string) io.ReadCloser {
   141  	if *localDir != "" {
   142  		path = filepath.FromSlash(path)
   143  		f, err := os.Open(filepath.Join(*localDir, path))
   144  		if err != nil {
   145  			log.Fatal(err)
   146  		}
   147  		return f
   148  	}
   149  	return get(*url, path)
   150  }
   151  
   152  func get(root, path string) io.ReadCloser {
   153  	url := root + "/" + path
   154  	fmt.Printf("Fetching %s...", url)
   155  	defer fmt.Println(" done.")
   156  	resp, err := http.Get(url)
   157  	if err != nil {
   158  		log.Fatalf("HTTP GET: %v", err)
   159  	}
   160  	if resp.StatusCode != 200 {
   161  		log.Fatalf("Bad GET status for %q: %q", url, resp.Status)
   162  	}
   163  	return resp.Body
   164  }
   165  
   166  // TODO: use Write*Version in all applicable packages.
   167  
   168  // WriteUnicodeVersion writes a constant for the Unicode version from which the
   169  // tables are generated.
   170  func WriteUnicodeVersion(w io.Writer) {
   171  	fmt.Fprintf(w, "// UnicodeVersion is the Unicode version from which the tables in this package are derived.\n")
   172  	fmt.Fprintf(w, "const UnicodeVersion = %q\n\n", UnicodeVersion())
   173  }
   174  
   175  // WriteCLDRVersion writes a constant for the CLDR version from which the
   176  // tables are generated.
   177  func WriteCLDRVersion(w io.Writer) {
   178  	fmt.Fprintf(w, "// CLDRVersion is the CLDR version from which the tables in this package are derived.\n")
   179  	fmt.Fprintf(w, "const CLDRVersion = %q\n\n", CLDRVersion())
   180  }
   181  
   182  // WriteGoFile prepends a standard file comment and package statement to the
   183  // given bytes, applies gofmt, and writes them to a file with the given name.
   184  // It will call log.Fatal if there are any errors.
   185  func WriteGoFile(filename, pkg string, b []byte) {
   186  	w, err := os.Create(filename)
   187  	if err != nil {
   188  		log.Fatalf("Could not create file %s: %v", filename, err)
   189  	}
   190  	defer w.Close()
   191  	if _, err = WriteGo(w, pkg, b); err != nil {
   192  		log.Fatalf("Error writing file %s: %v", filename, err)
   193  	}
   194  }
   195  
   196  // WriteGo prepends a standard file comment and package statement to the given
   197  // bytes, applies gofmt, and writes them to w.
   198  func WriteGo(w io.Writer, pkg string, b []byte) (n int, err error) {
   199  	src := []byte(fmt.Sprintf(header, pkg))
   200  	src = append(src, b...)
   201  	formatted, err := format.Source(src)
   202  	if err != nil {
   203  		// Print the generated code even in case of an error so that the
   204  		// returned error can be meaningfully interpreted.
   205  		n, _ = w.Write(src)
   206  		return n, err
   207  	}
   208  	return w.Write(formatted)
   209  }
   210  
   211  // Repackage rewrites a Go file from belonging to package main to belonging to
   212  // the given package.
   213  func Repackage(inFile, outFile, pkg string) {
   214  	src, err := ioutil.ReadFile(inFile)
   215  	if err != nil {
   216  		log.Fatalf("reading %s: %v", inFile, err)
   217  	}
   218  	const toDelete = "package main\n\n"
   219  	i := bytes.Index(src, []byte(toDelete))
   220  	if i < 0 {
   221  		log.Fatalf("Could not find %q in %s.", toDelete, inFile)
   222  	}
   223  	w := &bytes.Buffer{}
   224  	w.Write(src[i+len(toDelete):])
   225  	WriteGoFile(outFile, pkg, w.Bytes())
   226  }