提交 6c480485 编写于 作者: T Thomas Stromberg

Merge from hell #2: refactor start.go because it's terrible

......@@ -668,12 +668,20 @@
revision = "95c6576299259db960f6c5b9b69ea52422860fce"
[[projects]]
digest = "1:5b12278b98e82aecd7d0b84e0b5fba67f37aba8fde89fa86d51e30556d381a4c"
branch = "master"
digest = "1:11d290de3457882172ce8d9ffe930999cfb62929b58e486e1ff1b6adcf3c52bc"
name = "golang.org/x/text"
packages = [
"collate",
"collate/build",
"feature/plural",
"internal",
"internal/catmsg",
"internal/colltab",
"internal/format",
"internal/gen",
"internal/language",
"internal/language/compact",
"internal/number",
"internal/stringset",
"internal/tag",
......@@ -690,7 +698,7 @@
"unicode/rangetable",
]
pruneopts = "NUT"
revision = "b19bf474d317b857955b12035d2c5acb57ce8b01"
revision = "e6919f6577db79269a6443b9dc46d18f2238fb5d"
[[projects]]
digest = "1:d37b0ef2944431fe9e8ef35c6fffc8990d9e2ca300588df94a6890f3649ae365"
......
......@@ -114,3 +114,7 @@
[[constraint]]
name = "github.com/google/go-cmp"
version = "0.2.0"
[[constraint]]
name = "golang.org/x/text"
branch = "master"
......@@ -149,8 +149,6 @@ var addonsConfigureCmd = &cobra.Command{
if err != nil {
console.Warning("ERROR creating `registry-creds-dpr` secret")
}
break
default:
console.Failure("%s has no available configuration options", addon)
return
......
......@@ -19,6 +19,8 @@ package cmd
import (
"os"
"github.com/docker/machine/libmachine/mcnerror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdUtil "k8s.io/minikube/cmd/util"
......@@ -40,8 +42,8 @@ associated files.`,
console.ErrStyle("usage", "usage: minikube delete")
os.Exit(1)
}
console.OutStyle("stopping", "Deleting local Kubernetes cluster ...")
profile := viper.GetString(pkg_config.MachineProfile)
console.OutStyle("deleting-vm", "Deleting %q Kubernetes cluster ...", profile)
api, err := machine.NewAPIClient()
if err != nil {
console.Fatal("Error getting client: %v", err)
......@@ -50,19 +52,30 @@ associated files.`,
defer api.Close()
if err = cluster.DeleteHost(api); err != nil {
console.Fatal("Errors occurred deleting machine: %v", err)
os.Exit(1)
switch err := errors.Cause(err).(type) {
case mcnerror.ErrHostDoesNotExist:
console.OutStyle("meh", "%q VM does not exist", profile)
default:
console.Fatal("Failed to delete VM: %v", err)
os.Exit(1)
}
} else {
console.OutStyle("crushed", "VM deleted.")
}
console.Success("Machine deleted.")
if err := cmdUtil.KillMountProcess(); err != nil {
console.Fatal("Errors occurred deleting mount process: %v", err)
console.Fatal("Failed to kill mount process: %v", err)
}
if err := os.Remove(constants.GetProfileFile(viper.GetString(pkg_config.MachineProfile))); err != nil {
console.Fatal("Error deleting machine profile config: %v", err)
if os.IsNotExist(err) {
console.OutStyle("meh", "%q profile does not exist", profile)
os.Exit(0)
}
console.Fatal("Failed to remove profile: %v", err)
os.Exit(1)
}
console.Success("Removed %q profile!", profile)
},
}
......
此差异已折叠。
......@@ -20,9 +20,13 @@ import (
"os"
"time"
"github.com/docker/machine/libmachine/mcnerror"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
cmdUtil "k8s.io/minikube/cmd/util"
"k8s.io/minikube/pkg/minikube/cluster"
pkg_config "k8s.io/minikube/pkg/minikube/config"
"k8s.io/minikube/pkg/minikube/console"
"k8s.io/minikube/pkg/minikube/machine"
pkgutil "k8s.io/minikube/pkg/util"
......@@ -35,7 +39,9 @@ var stopCmd = &cobra.Command{
Long: `Stops a local kubernetes cluster running in Virtualbox. This command stops the VM
itself, leaving all files intact. The cluster can be started again with the "start" command.`,
Run: func(cmd *cobra.Command, args []string) {
console.OutStyle("stopping", "Stopping local Kubernetes cluster...")
profile := viper.GetString(pkg_config.MachineProfile)
console.OutStyle("stopping", "Stopping %q Kubernetes cluster...", profile)
api, err := machine.NewAPIClient()
if err != nil {
console.Fatal("Error getting client: %v", err)
......@@ -43,17 +49,29 @@ itself, leaving all files intact. The cluster can be started again with the "sta
}
defer api.Close()
nonexistent := false
stop := func() (err error) {
return cluster.StopHost(api)
err = cluster.StopHost(api)
switch err := errors.Cause(err).(type) {
case mcnerror.ErrHostDoesNotExist:
console.OutStyle("meh", "%q VM does not exist, nothing to stop", profile)
nonexistent = true
return nil
default:
return err
}
}
if err := pkgutil.RetryAfter(5, stop, 1*time.Second); err != nil {
console.Fatal("Error stopping machine: %v", err)
console.Fatal("Unable to stop VM: %v", err)
cmdUtil.MaybeReportErrorAndExit(err)
}
console.OutStyle("stopped", "Machine stopped.")
if !nonexistent {
console.OutStyle("stopped", "%q stopped.", profile)
}
if err := cmdUtil.KillMountProcess(); err != nil {
console.Fatal("Errors occurred deleting mount process: %v", err)
console.Fatal("Unable to kill mount process: %v", err)
}
},
}
......
......@@ -40,7 +40,7 @@ func main() {
}
console.SetOutFile(os.Stdout)
console.SetErrFile(os.Stderr)
err := console.SetLanguage(os.Getenv("LANG"))
err := console.SetPreferredLanguage(os.Getenv("LANG"))
if err != nil {
glog.Warningf("unable to detect language: %v", err)
}
......
......@@ -157,7 +157,6 @@ To disable this prompt, run: 'minikube config set WantReportErrorPrompt false'
if err != nil {
glog.Infof("report error failed: %v", err)
}
console.ErrStyle("embarassed", "minikube failed, exiting with error code %d", returnCode)
os.Exit(returnCode)
}
......
......@@ -187,6 +187,8 @@ func (k *KubeadmBootstrapper) StartCluster(k8s config.KubernetesConfig) error {
}
}
// NOTE: We have not yet asserted that we can access the apiserver. Now would be a great time to do so.
console.OutStyle("permissions", "Setting up cluster admin privileges ...")
if err := util.RetryAfter(100, elevateKubeSystemPrivileges, time.Millisecond*500); err != nil {
return errors.Wrap(err, "timed out waiting to elevate kube-system RBAC privileges")
}
......@@ -241,6 +243,9 @@ func (k *KubeadmBootstrapper) RestartCluster(k8s config.KubernetesConfig) error
return errors.Wrapf(err, "running cmd: %s", cmd)
}
}
// NOTE: Perhaps now would be a good time to check apiserver health?
console.OutStyle("waiting", "Restarting kube-proxy ...")
if err := restartKubeProxy(k8s); err != nil {
return errors.Wrap(err, "restarting kube-proxy")
}
......@@ -354,10 +359,10 @@ func (k *KubeadmBootstrapper) UpdateCluster(cfg config.KubernetesConfig) error {
}
f, err := assets.NewFileAsset(path, "/usr/bin", bin, "0641")
if err != nil {
return errors.Wrap(err, "making new file asset")
return errors.Wrap(err, "new file asset")
}
if err := k.c.Copy(f); err != nil {
return errors.Wrapf(err, "transferring kubeadm file: %+v", f)
return errors.Wrapf(err, "copy")
}
return nil
})
......@@ -367,15 +372,14 @@ func (k *KubeadmBootstrapper) UpdateCluster(cfg config.KubernetesConfig) error {
}
if err := addAddons(&files); err != nil {
return errors.Wrap(err, "adding addons to copyable files")
return errors.Wrap(err, "adding addons")
}
for _, f := range files {
if err := k.c.Copy(f); err != nil {
return errors.Wrapf(err, "transferring kubeadm file: %+v", f)
return errors.Wrapf(err, "copy")
}
}
err = k.c.Run(`
sudo systemctl daemon-reload &&
sudo systemctl enable kubelet &&
......@@ -483,7 +487,7 @@ func maybeDownloadAndCache(binary, version string) (string, error) {
options.Checksum = constants.GetKubernetesReleaseURLSha1(binary, version)
options.ChecksumHash = crypto.SHA1
console.OutStyle("download", "Downloading %s %s", binary, version)
console.OutStyle("file-download", "Downloading %s %s", binary, version)
if err := download.ToFile(url, targetFilepath, options); err != nil {
return "", errors.Wrapf(err, "Error downloading %s %s", binary, version)
}
......
......@@ -64,7 +64,7 @@ func init() {
func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error) {
exists, err := api.Exists(cfg.GetMachineName())
if err != nil {
return nil, errors.Wrapf(err, "Error checking if host exists: %s", cfg.GetMachineName())
return nil, errors.Wrapf(err, "machine name: %s", cfg.GetMachineName())
}
if !exists {
glog.Infoln("Machine does not exist... provisioning new machine")
......@@ -80,7 +80,10 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error)
}
if h.Driver.DriverName() != config.VMDriver {
console.Warning("Ignoring configuration which specifies %s driver, as the existing host is using the %s driver.", config.VMDriver, h.Driver.DriverName())
console.Warning("Ignoring --vm-driver=%s, as the %q host was created using the %s driver.",
config.VMDriver, cfg.GetMachineName(), h.Driver.DriverName())
console.Warning("To change drivers, create a new host using `minikube start -p <name>` or run `minikube delete -p %s`",
h.Driver.DriverName(), cfg.GetMachineName())
}
s, err := h.Driver.GetState()
......@@ -91,10 +94,10 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error)
if s != state.Running {
if err := h.Driver.Start(); err != nil {
return nil, errors.Wrap(err, "Error starting stopped host")
return nil, errors.Wrap(err, "start")
}
if err := api.Save(h); err != nil {
return nil, errors.Wrap(err, "Error saving started host")
return nil, errors.Wrap(err, "save")
}
}
......@@ -122,7 +125,7 @@ func StartHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error)
func StopHost(api libmachine.API) error {
host, err := api.Load(cfg.GetMachineName())
if err != nil {
return errors.Wrapf(err, "Load: %s", cfg.GetMachineName())
return errors.Wrapf(err, "load")
}
if err := host.Stop(); err != nil {
alreadyInStateError, ok := err.(mcnerror.ErrHostAlreadyInState)
......@@ -138,19 +141,22 @@ func StopHost(api libmachine.API) error {
func DeleteHost(api libmachine.API) error {
host, err := api.Load(cfg.GetMachineName())
if err != nil {
return errors.Wrapf(err, "Load: %s", cfg.GetMachineName())
return errors.Wrap(err, "load")
}
m := util.MultiError{}
m.Collect(host.Driver.Remove())
m.Collect(api.Remove(cfg.GetMachineName()))
return m.ToError()
if err := host.Driver.Remove(); err != nil {
return errors.Wrap(err, "host remove")
}
if err := api.Remove(cfg.GetMachineName()); err != nil {
return errors.Wrap(err, "api remove")
}
return nil
}
// GetHostStatus gets the status of the host VM.
func GetHostStatus(api libmachine.API) (string, error) {
exists, err := api.Exists(cfg.GetMachineName())
if err != nil {
return "", errors.Wrapf(err, "Error checking that api exists for: %s", cfg.GetMachineName())
return "", errors.Wrapf(err, "%s exists", cfg.GetMachineName())
}
if !exists {
return state.None.String(), nil
......@@ -158,12 +164,12 @@ func GetHostStatus(api libmachine.API) (string, error) {
host, err := api.Load(cfg.GetMachineName())
if err != nil {
return "", errors.Wrapf(err, "Error loading api for: %s", cfg.GetMachineName())
return "", errors.Wrapf(err, "load")
}
s, err := host.Driver.GetState()
if err != nil {
return "", errors.Wrap(err, "Error getting host state")
return "", errors.Wrap(err, "state")
}
return s.String(), nil
}
......@@ -177,11 +183,11 @@ func GetHostDriverIP(api libmachine.API, machineName string) (net.IP, error) {
ipStr, err := host.Driver.GetIP()
if err != nil {
return nil, errors.Wrap(err, "Error getting IP")
return nil, errors.Wrap(err, "getting IP")
}
ip := net.ParseIP(ipStr)
if ip == nil {
return nil, fmt.Errorf("error parsing IP: %s", ipStr)
return nil, fmt.Errorf("parsing IP: %s", ipStr)
}
return ip, nil
}
......@@ -249,12 +255,12 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error
data, err := json.Marshal(driver)
if err != nil {
return nil, errors.Wrap(err, "Error marshalling json")
return nil, errors.Wrap(err, "marshal")
}
h, err := api.NewHost(config.VMDriver, data)
if err != nil {
return nil, errors.Wrap(err, "Error creating new host")
return nil, errors.Wrap(err, "new host")
}
h.HostOptions.AuthOptions.CertDir = constants.GetMinipath()
......@@ -264,11 +270,11 @@ func createHost(api libmachine.API, config cfg.MachineConfig) (*host.Host, error
if err := api.Create(h); err != nil {
// Wait for all the logs to reach the client
time.Sleep(2 * time.Second)
return nil, errors.Wrap(err, "Error creating host")
return nil, errors.Wrap(err, "create")
}
if err := api.Save(h); err != nil {
return nil, errors.Wrap(err, "Error attempting to save")
return nil, errors.Wrap(err, "save")
}
return h, nil
}
......@@ -310,11 +316,11 @@ func MountHost(api libmachine.API, ip net.IP, path, port, mountVersion string, u
host.RunSSHCommand(GetMountCleanupCommand(path))
mountCmd, err := GetMountCommand(ip, path, port, mountVersion, uid, gid, msize)
if err != nil {
return errors.Wrap(err, "Error getting mount command")
return errors.Wrap(err, "mount command")
}
_, err = host.RunSSHCommand(mountCmd)
if err != nil {
return errors.Wrap(err, "running mount host command")
return errors.Wrap(err, "running mount")
}
return nil
}
......@@ -332,13 +338,13 @@ func GetVMHostIP(host *host.Host) (net.IP, error) {
hypervVirtualSwitch := re.FindStringSubmatch(string(host.RawDriver))[1]
ip, err := getIPForInterface(fmt.Sprintf("vEthernet (%s)", hypervVirtualSwitch))
if err != nil {
return []byte{}, errors.Wrap(err, "Error getting VM/Host IP address")
return []byte{}, errors.Wrap(err, fmt.Sprintf("ip for interface (%s)", hypervVirtualSwitch))
}
return ip, nil
case "virtualbox":
out, err := exec.Command(detectVBoxManageCmd(), "showvminfo", host.Name, "--machinereadable").Output()
if err != nil {
return []byte{}, errors.Wrap(err, "Error running vboxmanage command")
return []byte{}, errors.Wrap(err, "vboxmanage")
}
re := regexp.MustCompile(`hostonlyadapter2="(.*?)"`)
iface := re.FindStringSubmatch(string(out))[1]
......@@ -388,21 +394,21 @@ func CreateSSHShell(api libmachine.API, args []string) error {
machineName := cfg.GetMachineName()
host, err := CheckIfHostExistsAndLoad(api, machineName)
if err != nil {
return errors.Wrap(err, "Error checking if api exist and loading it")
return errors.Wrap(err, "host exists and load")
}
currentState, err := host.Driver.GetState()
if err != nil {
return errors.Wrap(err, "Error getting state of host")
return errors.Wrap(err, "state")
}
if currentState != state.Running {
return errors.Errorf("Error: Cannot run ssh command: Host %q is not running", machineName)
return errors.Errorf("%q is not running", machineName)
}
client, err := host.CreateSSHClient()
if err != nil {
return errors.Wrap(err, "Error creating ssh client")
return errors.Wrap(err, "Creating ssh client")
}
return client.Shell(args...)
}
......
......@@ -18,7 +18,6 @@ package cluster
import (
"os"
"strings"
"testing"
"github.com/docker/machine/libmachine/drivers"
......@@ -256,29 +255,6 @@ func TestDeleteHostErrorDeletingFiles(t *testing.T) {
}
}
func TestDeleteHostMultipleErrors(t *testing.T) {
api := tests.NewMockAPI()
api.RemoveError = true
h, _ := createHost(api, defaultMachineConfig)
d := &tests.MockDriver{RemoveError: true}
h.Driver = d
err := DeleteHost(api)
if err == nil {
t.Fatal("Expected error deleting host, didn't get one.")
}
expectedErrors := []string{"error removing " + config.GetMachineName(), "error deleting machine"}
for _, expectedError := range expectedErrors {
if !strings.Contains(err.Error(), expectedError) {
t.Fatalf("Error %v expected to contain: %s.", err, expectedError)
}
}
}
func TestGetHostStatus(t *testing.T) {
api := tests.NewMockAPI()
......
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package console provides a mechanism for sending localized, stylized output to the console.
package console
......@@ -23,6 +39,8 @@ import (
// console.SetErrFile(os.Stderr)
// console.Fatal("Oh no, everything failed.")
// NOTE: If you do not want colorized output, set MINIKUBE_IN_COLOR=0 in your environment.
var (
// outFile is where Out* functions send output to. Set using SetOutFile()
outFile fdWriter
......@@ -32,10 +50,10 @@ var (
preferredLanguage = language.AmericanEnglish
// our default language
defaultLanguage = language.AmericanEnglish
// ignoreTTYCheck ignores the result of the TTY check (for testing!)
ignoreTTYCheck = false
// useColor is whether or not color output should be used, updated by Set*Writer.
useColor = false
// OverrideEnv is the environment variable used to override color/emoji usage
OverrideEnv = "MINIKUBE_IN_COLOR"
)
// fdWriter is the subset of file.File that implements io.Writer and Fd()
......@@ -48,9 +66,9 @@ type fdWriter interface {
func OutStyle(style, format string, a ...interface{}) error {
OutStyle, err := applyStyle(style, useColor, fmt.Sprintf(format, a...))
if err != nil {
// Try anyways
if err := Out(OutStyle); err != nil {
glog.Errorf("Out failed: %v", err)
glog.Errorf("applyStyle(%s): %v", style, err)
if oerr := OutLn(format, a...); oerr != nil {
glog.Errorf("Out failed: %v", oerr)
}
return err
}
......@@ -61,7 +79,10 @@ func OutStyle(style, format string, a ...interface{}) error {
func Out(format string, a ...interface{}) error {
p := message.NewPrinter(preferredLanguage)
if outFile == nil {
return fmt.Errorf("No output file has been set")
if _, err := p.Fprintf(os.Stdout, "(stdout unset)"+format, a...); err != nil {
return err
}
return fmt.Errorf("no output file has been set")
}
_, err := p.Fprintf(outFile, format, a...)
return err
......@@ -76,9 +97,9 @@ func OutLn(format string, a ...interface{}) error {
func ErrStyle(style, format string, a ...interface{}) error {
format, err := applyStyle(style, useColor, fmt.Sprintf(format, a...))
if err != nil {
// Try anyways.
if err := Err(format); err != nil {
glog.Errorf("Err failed: %v", err)
glog.Errorf("applyStyle(%s): %v", style, err)
if oerr := ErrLn(format, a...); oerr != nil {
glog.Errorf("Err(%s) failed: %v", format, oerr)
}
return err
}
......@@ -89,7 +110,10 @@ func ErrStyle(style, format string, a ...interface{}) error {
func Err(format string, a ...interface{}) error {
p := message.NewPrinter(preferredLanguage)
if errFile == nil {
return fmt.Errorf("No error output file has been set")
if _, err := p.Fprintf(os.Stderr, "(stderr unset)"+format, a...); err != nil {
return err
}
return fmt.Errorf("no error file has been set")
}
_, err := p.Fprintf(errFile, format, a...)
return err
......@@ -120,16 +144,16 @@ func Failure(format string, a ...interface{}) error {
return ErrStyle("failure", format, a...)
}
// SetLanguageTag configures which language future messages should use.
func SetLanguageTag(l language.Tag) {
// SetPreferredLanguageTag configures which language future messages should use.
func SetPreferredLanguageTag(l language.Tag) {
glog.Infof("Setting Language to %s ...", l)
preferredLanguage = l
}
// SetLanguage configures which language future messages should use, based on a LANG string.
func SetLanguage(s string) error {
// SetPreferredLanguage configures which language future messages should use, based on a LANG string.
func SetPreferredLanguage(s string) error {
if s == "" || s == "C" {
SetLanguageTag(defaultLanguage)
SetPreferredLanguageTag(defaultLanguage)
return nil
}
// Ignore encoding preferences: we always output utf8. Handles "de_DE.utf8"
......@@ -138,40 +162,42 @@ func SetLanguage(s string) error {
if err != nil {
return err
}
SetLanguageTag(l)
SetPreferredLanguageTag(l)
return nil
}
// SetOutFile configures which writer standard output goes to.
func SetOutFile(w fdWriter) {
glog.Infof("Setting OutFile to %v (fd=%d) ...", w, w.Fd())
glog.Infof("Setting OutFile to fd %d ...", w.Fd())
outFile = w
useColor = wantsColor(w.Fd())
}
// SetErrFile configures which writer error output goes to.
func SetErrFile(w fdWriter) {
glog.Infof("Setting ErrFile to %v (fd=%d)...", w, w.Fd())
glog.Infof("Setting ErrFile to fd %d...", w.Fd())
errFile = w
useColor = wantsColor(w.Fd())
}
// wantsColor determines if the user might want colorized output.
func wantsColor(fd uintptr) bool {
// As in: term-256color
if !strings.Contains(os.Getenv("TERM"), "color") {
glog.Infof("TERM does not appear to support color")
glog.Infof("%s=%q\n", OverrideEnv, os.Getenv(OverrideEnv))
switch os.Getenv(OverrideEnv) {
case "0":
return false
case "1":
return true
}
// Allow boring people to continue to be boring people.
if os.Getenv("MINIKUBE_IS_BORING") == "1" {
glog.Infof("minikube is boring.")
term := os.Getenv("TERM")
// As in: term-256color
if !strings.Contains(term, "color") {
glog.Infof("TERM=%s, which probably does not support color", term)
return false
}
if ignoreTTYCheck {
return true
}
isT := isatty.IsTerminal(fd)
glog.Infof("IsTerminal(%d) = %v", fd, isT)
glog.Infof("isatty.IsTerminal(%d) = %v\n", fd, isT)
return isT
}
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package console
import (
"bytes"
"os"
"testing"
"golang.org/x/text/language"
......@@ -14,8 +31,6 @@ type fakeFile struct {
}
func newFakeFile() *fakeFile {
// So that we don't have to fully emulate being a terminal
ignoreTTYCheck = true
return &fakeFile{}
}
......@@ -31,6 +46,7 @@ func (f *fakeFile) String() string {
}
func TestOutStyle(t *testing.T) {
os.Setenv(OverrideEnv, "1")
f := newFakeFile()
SetOutFile(f)
if err := OutStyle("happy", "This is a happy message."); err != nil {
......@@ -45,23 +61,29 @@ func TestOutStyle(t *testing.T) {
}
func TestOut(t *testing.T) {
os.Setenv(OverrideEnv, "")
// An example translation just to assert that this code path is executed.
message.SetString(language.Arabic, "Installing Kubernetes version %s ...", "... %s تثبيت Kubernetes الإصدار")
SetLanguageTag(language.Arabic)
err := message.SetString(language.Arabic, "Installing Kubernetes version %s ...", "... %s تثبيت Kubernetes الإصدار")
if err != nil {
t.Fatalf("setstring: %v", err)
}
var tests = []struct {
format string
lang language.Tag
arg string
want string
}{
{format: "xyz123", want: "xyz123"},
{format: "Installing Kubernetes version %s ...", arg: "v1.13", want: "... v1.13 تثبيت Kubernetes الإصدار"},
{format: "Installing Kubernetes version %s ...", lang: language.Arabic, arg: "v1.13", want: "... v1.13 تثبيت Kubernetes الإصدار"},
{format: "Installing Kubernetes version %s ...", lang: language.AmericanEnglish, arg: "v1.13", want: "Installing Kubernetes version v1.13 ..."},
}
for _, tc := range tests {
t.Run(tc.format, func(t *testing.T) {
SetPreferredLanguageTag(tc.lang)
f := newFakeFile()
SetOutFile(f)
Err("unrelated message")
ErrLn("unrelated message")
if err := Out(tc.format, tc.arg); err != nil {
t.Errorf("unexpected error: %q", err)
}
......@@ -74,13 +96,14 @@ func TestOut(t *testing.T) {
}
func TestErr(t *testing.T) {
os.Setenv(OverrideEnv, "0")
f := newFakeFile()
SetErrFile(f)
if err := Err("xyz123\n"); err != nil {
t.Errorf("unexpected error: %q", err)
}
Out("unrelated message")
OutLn("unrelated message")
got := f.String()
want := "xyz123\n"
......@@ -90,6 +113,7 @@ func TestErr(t *testing.T) {
}
func TestErrStyle(t *testing.T) {
os.Setenv(OverrideEnv, "1")
f := newFakeFile()
SetErrFile(f)
if err := ErrStyle("fatal", "It broke"); err != nil {
......@@ -102,8 +126,8 @@ func TestErrStyle(t *testing.T) {
}
}
func TestSetLanguage(t *testing.T) {
func TestSetPreferredLanguage(t *testing.T) {
os.Setenv(OverrideEnv, "0")
var tests = []struct {
input string
want language.Tag
......@@ -116,8 +140,8 @@ func TestSetLanguage(t *testing.T) {
for _, tc := range tests {
t.Run(tc.input, func(t *testing.T) {
// Set something so that we can assert change.
SetLanguageTag(language.Icelandic)
if err := SetLanguage(tc.input); err != nil {
SetPreferredLanguageTag(language.Icelandic)
if err := SetPreferredLanguage(tc.input); err != nil {
t.Errorf("unexpected error: %q", err)
}
......@@ -125,7 +149,7 @@ func TestSetLanguage(t *testing.T) {
want, _ := tc.want.Base()
got, _ := preferredLanguage.Base()
if got != want {
t.Errorf("SetLanguage(%s) = %q, want %q", tc.input, got, want)
t.Errorf("SetPreferredLanguage(%s) = %q, want %q", tc.input, got, want)
}
})
}
......
/*
Copyright 2019 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package console
import (
......@@ -15,39 +31,50 @@ type style struct {
}
// styles is a map of style name to style struct
// For consistency, ensure that emojis added render with the same width across platforms.
var styles = map[string]style{
// General purpose
"happy": style{Prefix: "😄"},
"success": style{Prefix: "✅ "},
"failure": style{Prefix: "❌"},
"conflict": style{Prefix: "💥"},
"fatal": style{Prefix: "💣"},
"notice": style{Prefix: "📌"},
"ready": style{Prefix: "🏄"},
"restarting": style{Prefix: "🔁"},
"stopping": style{Prefix: "🚦"},
"stopped": style{Prefix: "🛑"},
"warning": style{Prefix: "⚠️"},
"waiting": style{Prefix: "⌛"},
"usage": style{Prefix: "💡"},
"launch": style{Prefix: "🚀"},
"happy": {Prefix: "😄"},
"success": {Prefix: "✅"},
"failure": {Prefix: "❌"},
"conflict": {Prefix: "💥"},
"fatal": {Prefix: "💣"},
"notice": {Prefix: "📌"},
"ready": {Prefix: "🏄"},
"restarting": {Prefix: "🔄"},
"stopping": {Prefix: "✋"},
"stopped": {Prefix: "🛑"},
"warning": {Prefix: "⚠️"},
"waiting": {Prefix: "⌛"},
"usage": {Prefix: "💡"},
"launch": {Prefix: "🚀"},
"thumbs-up": {Prefix: "👍"},
"option": {Prefix: " ▪ "},
"crushed": {Prefix: "💔"},
// Specialized purpose
"iso-download": style{Prefix: "💿"},
"file-download": style{Prefix: "💾"},
"caching": style{Prefix: "🤹"},
"starting-vm": style{Prefix: "🔥"},
"copying": style{Prefix: "✨"},
"connectivity": style{Prefix: "📡"},
"mounting": style{Prefix: "📁"},
"celebrate": style{Prefix: "🎉"},
"container-runtime": style{Prefix: "🎁"},
"enabling": style{Prefix: "🔌"},
"pulling": style{Prefix: "🚜"},
"verifying": style{Prefix: "🤔"},
"kubectl": style{Prefix: "❤️"},
"meh": style{Prefix: "🙄"},
"embarassed": style{Prefix: "🤦"},
// Specialized purpose styles
"iso-download": {Prefix: "💿"},
"file-download": {Prefix: "💾"},
"caching": {Prefix: "🤹"},
"starting-vm": {Prefix: "🔥"},
"starting-none": {Prefix: "🤹"},
"deleting-vm": {Prefix: "🔥"},
"copying": {Prefix: "✨"},
"connectivity": {Prefix: "📶"},
"mounting": {Prefix: "📁"},
"celebrate": {Prefix: "🎉"},
"container-runtime": {Prefix: "🎁"},
"Docker": {Prefix: "🐳"},
"CRIO": {Prefix: "🎁"}, // This should be a snow-flake, but the emoji has a strange width on macOS
"containerd": {Prefix: "📦"},
"permissions": {Prefix: "🔑"},
"enabling": {Prefix: "🔌"},
"pulling": {Prefix: "🚜"},
"verifying": {Prefix: "🤔"},
"verifying-noline": {Prefix: "🤔", OmitNewline: true},
"kubectl": {Prefix: "💗"},
"meh": {Prefix: "🙄"},
"embarassed": {Prefix: "🤦"},
"tip": {Prefix: "💡"},
}
// Add a prefix to a string
......@@ -56,15 +83,17 @@ func applyPrefix(prefix, format string) string {
return format
}
// TODO(tstromberg): Ensure compatibility with RTL languages.
return prefix + " " + format
return prefix + " " + format
}
// Apply styling to a format string
func applyStyle(style string, useColor bool, format string, a ...interface{}) (string, error) {
p := message.NewPrinter(preferredLanguage)
out := p.Sprintf(format, a...)
s, ok := styles[style]
if !s.OmitNewline {
format = format + "\n"
out += "\n"
}
// Similar to CSS styles, if no style matches, output an unformatted string.
......@@ -73,9 +102,9 @@ func applyStyle(style string, useColor bool, format string, a ...interface{}) (s
}
prefix := s.Prefix
if useColor && prefix != "" {
if !useColor && prefix != "" {
prefix = "-"
}
format = applyPrefix(prefix, format)
return p.Sprintf(format, a...), nil
out = applyPrefix(prefix, out)
return out, nil
}
......@@ -19,7 +19,6 @@ package machine
import (
"crypto/tls"
"encoding/json"
"fmt"
"net"
"os"
"path/filepath"
......@@ -120,7 +119,7 @@ func (api *LocalClient) NewHost(driverName string, rawDriver []byte) (*host.Host
func (api *LocalClient) Load(name string) (*host.Host, error) {
h, err := api.Filestore.Load(name)
if err != nil {
return nil, errors.Wrap(err, "Error loading host from store")
return nil, errors.Wrap(err, "filestore")
}
var def registry.DriverDef
......@@ -165,25 +164,25 @@ func (api *LocalClient) Create(h *host.Host) error {
f func() error
}{
{
"Bootstrapping certs.",
"bootstrapping certificates",
func() error { return cert.BootstrapCertificates(h.AuthOptions()) },
},
{
"Running precreate checks.",
"precreate",
h.Driver.PreCreateCheck,
},
{
"Saving driver.",
"saving",
func() error {
return api.Save(h)
},
},
{
"Creating VM.",
"creating",
h.Driver.Create,
},
{
"Waiting for VM to start.",
"waiting",
func() error {
if h.Driver.DriverName() == "none" {
return nil
......@@ -192,7 +191,7 @@ func (api *LocalClient) Create(h *host.Host) error {
},
},
{
"Provisioning VM.",
"provisioning",
func() error {
if h.Driver.DriverName() == "none" {
return nil
......@@ -205,7 +204,7 @@ func (api *LocalClient) Create(h *host.Host) error {
for _, step := range steps {
if err := step.f(); err != nil {
return errors.Wrap(err, fmt.Sprintf("Error executing step: %s\n", step.name))
return errors.Wrap(err, step.name)
}
}
......
......@@ -158,7 +158,7 @@ func TestRunDriver(t *testing.T) {
}
os.Stdout = old
fmt.Prinln(string(addr))
fmt.Println(string(addr))
// Now that we got the port, make sure we can connect.
if _, err := net.Dial("tcp", string(addr)); err != nil {
......
......@@ -74,8 +74,7 @@ type URLHandlerCorrect struct {
func (h *URLHandlerCorrect) ServeHTTP(w http.ResponseWriter, r *http.Request) {
b, err := json.Marshal(h.releases)
if err != nil {
// TODO(tstrombxerg): Do something else with this?
fmt.ErrLn(err)
fmt.Println(err)
return
}
w.Header().Set("Content-Type", "application/javascript")
......
......@@ -114,7 +114,7 @@ func RetryAfter(attempts int, callback func() error, d time.Duration) (err error
glog.Infof("non-retriable error: %v", err)
return m.ToError()
}
glog.V(2).Infof("sleeping %s", d)
glog.V(2).Infof("error: %v - sleeping %s", err, d)
time.Sleep(d)
}
return m.ToError()
......
......@@ -48,25 +48,43 @@ func NewCodeWriter() *CodeWriter {
}
// WriteGoFile appends the buffer with the total size of all created structures
// and writes it as a Go file to the the given file with the given package name.
// and writes it as a Go file to the given file with the given package name.
func (w *CodeWriter) WriteGoFile(filename, pkg string) {
f, err := os.Create(filename)
if err != nil {
log.Fatalf("Could not create file %s: %v", filename, err)
}
defer f.Close()
if _, err = w.WriteGo(f, pkg); err != nil {
if _, err = w.WriteGo(f, pkg, ""); err != nil {
log.Fatalf("Error writing file %s: %v", filename, err)
}
}
// WriteVersionedGoFile appends the buffer with the total size of all created
// structures and writes it as a Go file to the given file with the given
// package name and build tags for the current Unicode version,
func (w *CodeWriter) WriteVersionedGoFile(filename, pkg string) {
tags := buildTags()
if tags != "" {
filename = insertVersion(filename, UnicodeVersion())
}
f, err := os.Create(filename)
if err != nil {
log.Fatalf("Could not create file %s: %v", filename, err)
}
defer f.Close()
if _, err = w.WriteGo(f, pkg, tags); err != nil {
log.Fatalf("Error writing file %s: %v", filename, err)
}
}
// WriteGo appends the buffer with the total size of all created structures and
// writes it as a Go file to the the given writer with the given package name.
func (w *CodeWriter) WriteGo(out io.Writer, pkg string) (n int, err error) {
// writes it as a Go file to the given writer with the given package name.
func (w *CodeWriter) WriteGo(out io.Writer, pkg, tags string) (n int, err error) {
sz := w.Size
w.WriteComment("Total table size %d bytes (%dKiB); checksum: %X\n", sz, sz/1024, w.Hash.Sum32())
defer w.buf.Reset()
return WriteGo(out, pkg, w.buf.Bytes())
return WriteGo(out, pkg, tags, w.buf.Bytes())
}
func (w *CodeWriter) printf(f string, x ...interface{}) {
......@@ -181,7 +199,6 @@ func (w *CodeWriter) writeValue(v reflect.Value) {
// WriteString writes a string literal.
func (w *CodeWriter) WriteString(s string) {
s = strings.Replace(s, `\`, `\\`, -1)
io.WriteString(w.Hash, s) // content hash
w.Size += len(s)
......@@ -232,6 +249,9 @@ func (w *CodeWriter) WriteString(s string) {
out = fmt.Sprintf("\\U%08x", r)
}
chars = len(out)
} else if r == '\\' {
out = "\\" + string(r)
chars = 2
}
if n -= chars; n < 0 {
nLines++
......
......@@ -7,7 +7,7 @@
//
// This package defines command line flags that are common to most generation
// tools. The flags allow for specifying specific Unicode and CLDR versions
// in the public Unicode data repository (http://www.unicode.org/Public).
// in the public Unicode data repository (https://www.unicode.org/Public).
//
// A local Unicode data mirror can be set through the flag -local or the
// environment variable UNICODE_DIR. The former takes precedence. The local
......@@ -31,6 +31,7 @@ import (
"os"
"path"
"path/filepath"
"strings"
"sync"
"unicode"
......@@ -39,7 +40,7 @@ import (
var (
url = flag.String("url",
"http://www.unicode.org/Public",
"https://www.unicode.org/Public",
"URL of Unicode database directory")
iana = flag.String("iana",
"http://www.iana.org",
......@@ -69,8 +70,6 @@ func Init() {
const header = `// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
package %s
`
// UnicodeVersion reports the requested Unicode version.
......@@ -78,11 +77,33 @@ func UnicodeVersion() string {
return *unicodeVersion
}
// UnicodeVersion reports the requested CLDR version.
// CLDRVersion reports the requested CLDR version.
func CLDRVersion() string {
return *cldrVersion
}
var tags = []struct{ version, buildTags string }{
{"10.0.0", "go1.10"},
{"", "!go1.10"},
}
// buildTags reports the build tags used for the current Unicode version.
func buildTags() string {
v := UnicodeVersion()
for _, x := range tags {
// We should do a numeric comparison, but including the collate package
// would create an import cycle. We approximate it by assuming that
// longer version strings are later.
if len(x.version) <= len(v) {
return x.buildTags
}
if len(x.version) == len(v) && x.version <= v {
return x.buildTags
}
}
return tags[0].buildTags
}
// IsLocal reports whether data files are available locally.
func IsLocal() bool {
dir, err := localReadmeFile()
......@@ -243,15 +264,46 @@ func WriteGoFile(filename, pkg string, b []byte) {
log.Fatalf("Could not create file %s: %v", filename, err)
}
defer w.Close()
if _, err = WriteGo(w, pkg, b); err != nil {
if _, err = WriteGo(w, pkg, "", b); err != nil {
log.Fatalf("Error writing file %s: %v", filename, err)
}
}
func insertVersion(filename, version string) string {
suffix := ".go"
if strings.HasSuffix(filename, "_test.go") {
suffix = "_test.go"
}
return fmt.Sprint(filename[:len(filename)-len(suffix)], version, suffix)
}
// WriteVersionedGoFile prepends a standard file comment, adds build tags to
// version the file for the current Unicode version, and package statement to
// the given bytes, applies gofmt, and writes them to a file with the given
// name. It will call log.Fatal if there are any errors.
func WriteVersionedGoFile(filename, pkg string, b []byte) {
tags := buildTags()
if tags != "" {
filename = insertVersion(filename, UnicodeVersion())
}
w, err := os.Create(filename)
if err != nil {
log.Fatalf("Could not create file %s: %v", filename, err)
}
defer w.Close()
if _, err = WriteGo(w, pkg, tags, b); err != nil {
log.Fatalf("Error writing file %s: %v", filename, err)
}
}
// WriteGo prepends a standard file comment and package statement to the given
// bytes, applies gofmt, and writes them to w.
func WriteGo(w io.Writer, pkg string, b []byte) (n int, err error) {
src := []byte(fmt.Sprintf(header, pkg))
func WriteGo(w io.Writer, pkg, tags string, b []byte) (n int, err error) {
src := []byte(header)
if tags != "" {
src = append(src, fmt.Sprintf("// +build %s\n\n", tags)...)
}
src = append(src, fmt.Sprintf("package %s\n\n", pkg)...)
src = append(src, b...)
formatted, err := format.Source(src)
if err != nil {
......
......@@ -53,7 +53,7 @@
// Indexes of starter blocks in case of multiple trie roots.
//
// It is recommended that users test the generated trie by checking the returned
// value for every rune. Such exhaustive tests are possible as the the number of
// value for every rune. Such exhaustive tests are possible as the number of
// runes in Unicode is limited.
package triegen // import "golang.org/x/text/internal/triegen"
......
......@@ -3,16 +3,16 @@
// license that can be found in the LICENSE file.
// Package ucd provides a parser for Unicode Character Database files, the
// format of which is defined in http://www.unicode.org/reports/tr44/. See
// http://www.unicode.org/Public/UCD/latest/ucd/ for example files.
// format of which is defined in https://www.unicode.org/reports/tr44/. See
// https://www.unicode.org/Public/UCD/latest/ucd/ for example files.
//
// It currently does not support substitutions of missing fields.
package ucd // import "golang.org/x/text/internal/ucd"
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"log"
"regexp"
......@@ -92,10 +92,11 @@ type Parser struct {
keepRanges bool // Don't expand rune ranges in field 0.
err error
comment []byte
field [][]byte
comment string
field []string
// parsedRange is needed in case Range(0) is called more than once for one
// field. In some cases this requires scanning ahead.
line int
parsedRange bool
rangeStart, rangeEnd rune
......@@ -103,15 +104,19 @@ type Parser struct {
commentHandler func(s string)
}
func (p *Parser) setError(err error) {
if p.err == nil {
p.err = err
func (p *Parser) setError(err error, msg string) {
if p.err == nil && err != nil {
if msg == "" {
p.err = fmt.Errorf("ucd:line:%d: %v", p.line, err)
} else {
p.err = fmt.Errorf("ucd:line:%d:%s: %v", p.line, msg, err)
}
}
}
func (p *Parser) getField(i int) []byte {
func (p *Parser) getField(i int) string {
if i >= len(p.field) {
return nil
return ""
}
return p.field[i]
}
......@@ -139,65 +144,66 @@ func (p *Parser) Next() bool {
p.rangeStart++
return true
}
p.comment = nil
p.comment = ""
p.field = p.field[:0]
p.parsedRange = false
for p.scanner.Scan() {
b := p.scanner.Bytes()
if len(b) == 0 {
for p.scanner.Scan() && p.err == nil {
p.line++
s := p.scanner.Text()
if s == "" {
continue
}
if b[0] == '#' {
if s[0] == '#' {
if p.commentHandler != nil {
p.commentHandler(strings.TrimSpace(string(b[1:])))
p.commentHandler(strings.TrimSpace(s[1:]))
}
continue
}
// Parse line
if i := bytes.IndexByte(b, '#'); i != -1 {
p.comment = bytes.TrimSpace(b[i+1:])
b = b[:i]
if i := strings.IndexByte(s, '#'); i != -1 {
p.comment = strings.TrimSpace(s[i+1:])
s = s[:i]
}
if b[0] == '@' {
if s[0] == '@' {
if p.partHandler != nil {
p.field = append(p.field, bytes.TrimSpace(b[1:]))
p.field = append(p.field, strings.TrimSpace(s[1:]))
p.partHandler(p)
p.field = p.field[:0]
}
p.comment = nil
p.comment = ""
continue
}
for {
i := bytes.IndexByte(b, ';')
i := strings.IndexByte(s, ';')
if i == -1 {
p.field = append(p.field, bytes.TrimSpace(b))
p.field = append(p.field, strings.TrimSpace(s))
break
}
p.field = append(p.field, bytes.TrimSpace(b[:i]))
b = b[i+1:]
p.field = append(p.field, strings.TrimSpace(s[:i]))
s = s[i+1:]
}
if !p.keepRanges {
p.rangeStart, p.rangeEnd = p.getRange(0)
}
return true
}
p.setError(p.scanner.Err())
p.setError(p.scanner.Err(), "scanner failed")
return false
}
func parseRune(b []byte) (rune, error) {
func parseRune(b string) (rune, error) {
if len(b) > 2 && b[0] == 'U' && b[1] == '+' {
b = b[2:]
}
x, err := strconv.ParseUint(string(b), 16, 32)
x, err := strconv.ParseUint(b, 16, 32)
return rune(x), err
}
func (p *Parser) parseRune(b []byte) rune {
x, err := parseRune(b)
p.setError(err)
func (p *Parser) parseRune(s string) rune {
x, err := parseRune(s)
p.setError(err, "failed to parse rune")
return x
}
......@@ -211,13 +217,13 @@ func (p *Parser) Rune(i int) rune {
// Runes interprets and returns field i as a sequence of runes.
func (p *Parser) Runes(i int) (runes []rune) {
add := func(b []byte) {
if b = bytes.TrimSpace(b); len(b) > 0 {
runes = append(runes, p.parseRune(b))
add := func(s string) {
if s = strings.TrimSpace(s); len(s) > 0 {
runes = append(runes, p.parseRune(s))
}
}
for b := p.getField(i); ; {
i := bytes.IndexByte(b, ' ')
i := strings.IndexByte(b, ' ')
if i == -1 {
add(b)
break
......@@ -247,7 +253,7 @@ func (p *Parser) Range(i int) (first, last rune) {
func (p *Parser) getRange(i int) (first, last rune) {
b := p.getField(i)
if k := bytes.Index(b, []byte("..")); k != -1 {
if k := strings.Index(b, ".."); k != -1 {
return p.parseRune(b[:k]), p.parseRune(b[k+2:])
}
// The first field may not be a rune, in which case we may ignore any error
......@@ -260,23 +266,24 @@ func (p *Parser) getRange(i int) (first, last rune) {
p.keepRanges = true
}
// Special case for UnicodeData that was retained for backwards compatibility.
if i == 0 && len(p.field) > 1 && bytes.HasSuffix(p.field[1], []byte("First>")) {
if i == 0 && len(p.field) > 1 && strings.HasSuffix(p.field[1], "First>") {
if p.parsedRange {
return p.rangeStart, p.rangeEnd
}
mf := reRange.FindStringSubmatch(p.scanner.Text())
p.line++
if mf == nil || !p.scanner.Scan() {
p.setError(errIncorrectLegacyRange)
p.setError(errIncorrectLegacyRange, "")
return x, x
}
// Using Bytes would be more efficient here, but Text is a lot easier
// and this is not a frequent case.
ml := reRange.FindStringSubmatch(p.scanner.Text())
if ml == nil || mf[2] != ml[2] || ml[3] != "Last" || mf[4] != ml[4] {
p.setError(errIncorrectLegacyRange)
p.setError(errIncorrectLegacyRange, "")
return x, x
}
p.rangeStart, p.rangeEnd = x, p.parseRune(p.scanner.Bytes()[:len(ml[1])])
p.rangeStart, p.rangeEnd = x, p.parseRune(p.scanner.Text()[:len(ml[1])])
p.parsedRange = true
return p.rangeStart, p.rangeEnd
}
......@@ -298,34 +305,34 @@ var bools = map[string]bool{
// Bool parses and returns field i as a boolean value.
func (p *Parser) Bool(i int) bool {
b := p.getField(i)
f := p.getField(i)
for s, v := range bools {
if bstrEq(b, s) {
if f == s {
return v
}
}
p.setError(strconv.ErrSyntax)
p.setError(strconv.ErrSyntax, "error parsing bool")
return false
}
// Int parses and returns field i as an integer value.
func (p *Parser) Int(i int) int {
x, err := strconv.ParseInt(string(p.getField(i)), 10, 64)
p.setError(err)
p.setError(err, "error parsing int")
return int(x)
}
// Uint parses and returns field i as an unsigned integer value.
func (p *Parser) Uint(i int) uint {
x, err := strconv.ParseUint(string(p.getField(i)), 10, 64)
p.setError(err)
p.setError(err, "error parsing uint")
return uint(x)
}
// Float parses and returns field i as a decimal value.
func (p *Parser) Float(i int) float64 {
x, err := strconv.ParseFloat(string(p.getField(i)), 64)
p.setError(err)
p.setError(err, "error parsing float")
return x
}
......@@ -353,24 +360,12 @@ var errUndefinedEnum = errors.New("ucd: undefined enum value")
// Enum interprets and returns field i as a value that must be one of the values
// in enum.
func (p *Parser) Enum(i int, enum ...string) string {
b := p.getField(i)
f := p.getField(i)
for _, s := range enum {
if bstrEq(b, s) {
if f == s {
return s
}
}
p.setError(errUndefinedEnum)
p.setError(errUndefinedEnum, "error parsing enum")
return ""
}
func bstrEq(b []byte, s string) bool {
if len(b) != len(s) {
return false
}
for i, c := range b {
if c != s[i] {
return false
}
}
return true
}
......@@ -155,6 +155,7 @@ func DirectionString(s string) bidi.Direction {
e, sz := bidi.LookupString(s[i:])
if sz == 0 {
i++
continue
}
c := e.Class()
if c == bidi.R || c == bidi.AL || c == bidi.AN {
......@@ -202,13 +203,6 @@ func (t *Transformer) isRTL() bool {
return t.seen&isRTL != 0
}
func (t *Transformer) isFinal() bool {
if !t.isRTL() {
return true
}
return t.state == ruleLTRFinal || t.state == ruleRTLFinal || t.state == ruleInitial
}
// Reset implements transform.Transformer.
func (t *Transformer) Reset() { *t = Transformer{} }
......
......@@ -78,8 +78,8 @@ type SpanningTransformer interface {
// considering the error err.
//
// A nil error means that all input bytes are known to be identical to the
// output produced by the Transformer. A nil error can be be returned
// regardless of whether atEOF is true. If err is nil, then then n must
// output produced by the Transformer. A nil error can be returned
// regardless of whether atEOF is true. If err is nil, then n must
// equal len(src); the converse is not necessarily true.
//
// ErrEndOfSpan means that the Transformer output may differ from the
......
......@@ -6,7 +6,7 @@
// Package bidi contains functionality for bidirectional text support.
//
// See http://www.unicode.org/reports/tr9.
// See https://www.unicode.org/reports/tr9.
//
// NOTE: UNDER CONSTRUCTION. This API may change in backwards incompatible ways
// and without notice.
......
......@@ -12,7 +12,7 @@ import (
// This file contains a port of the reference implementation of the
// Bidi Parentheses Algorithm:
// http://www.unicode.org/Public/PROGRAMS/BidiReferenceJava/BidiPBAReference.java
// https://www.unicode.org/Public/PROGRAMS/BidiReferenceJava/BidiPBAReference.java
//
// The implementation in this file covers definitions BD14-BD16 and rule N0
// of UAX#9.
......@@ -246,7 +246,7 @@ func (p *bracketPairer) getStrongTypeN0(index int) Class {
// assuming the given embedding direction.
//
// It returns ON if no strong type is found. If a single strong type is found,
// it returns this this type. Otherwise it returns the embedding direction.
// it returns this type. Otherwise it returns the embedding direction.
//
// TODO: use separate type for "strong" directionality.
func (p *bracketPairer) classifyPairContent(loc bracketPair, dirEmbed Class) Class {
......
......@@ -7,7 +7,7 @@ package bidi
import "log"
// This implementation is a port based on the reference implementation found at:
// http://www.unicode.org/Public/PROGRAMS/BidiReferenceJava/
// https://www.unicode.org/Public/PROGRAMS/BidiReferenceJava/
//
// described in Unicode Bidirectional Algorithm (UAX #9).
//
......
......@@ -26,7 +26,7 @@ func main() {
}
// bidiClass names and codes taken from class "bc" in
// http://www.unicode.org/Public/8.0.0/ucd/PropertyValueAliases.txt
// https://www.unicode.org/Public/8.0.0/ucd/PropertyValueAliases.txt
var bidiClass = map[string]Class{
"AL": AL, // ArabicLetter
"AN": AN, // ArabicNumber
......@@ -59,7 +59,7 @@ func genTables() {
log.Fatalf("Too many Class constants (%#x > 0x0F).", numClass)
}
w := gen.NewCodeWriter()
defer w.WriteGoFile(*outputFile, "bidi")
defer w.WriteVersionedGoFile(*outputFile, "bidi")
gen.WriteUnicodeVersion(w)
......
......@@ -15,7 +15,7 @@ import (
)
// These tables are hand-extracted from:
// http://www.unicode.org/Public/8.0.0/ucd/extracted/DerivedBidiClass.txt
// https://www.unicode.org/Public/8.0.0/ucd/extracted/DerivedBidiClass.txt
func visitDefaults(fn func(r rune, c Class)) {
// first write default values for ranges listed above.
visitRunes(fn, AL, []rune{
......
......@@ -62,6 +62,11 @@ func (e *Common) Default() string {
return ""
}
// Element returns the XML element name.
func (e *Common) Element() string {
return e.name
}
// GetCommon returns e. It is provided such that Common implements Elem.
func (e *Common) GetCommon() *Common {
return e
......
......@@ -5,14 +5,15 @@
//go:generate go run makexml.go -output xml.go
// Package cldr provides a parser for LDML and related XML formats.
// This package is intended to be used by the table generation tools
// for the various internationalization-related packages.
// As the XML types are generated from the CLDR DTD, and as the CLDR standard
// is periodically amended, this package may change considerably over time.
// This mostly means that data may appear and disappear between versions.
// That is, old code should keep compiling for newer versions, but data
// may have moved or changed.
// CLDR version 22 is the first version supported by this package.
//
// This package is intended to be used by the table generation tools for the
// various packages in x/text and is not internal for historical reasons.
//
// As the XML types are generated from the CLDR DTD, and as the CLDR standard is
// periodically amended, this package may change considerably over time. This
// mostly means that data may appear and disappear between versions. That is,
// old code should keep compiling for newer versions, but data may have moved or
// changed. CLDR version 22 is the first version supported by this package.
// Older versions may not work.
package cldr // import "golang.org/x/text/unicode/cldr"
......@@ -94,6 +95,12 @@ func (cldr *CLDR) RawLDML(loc string) *LDML {
// LDML returns the fully resolved LDML XML for loc, which must be one of
// the strings returned by Locales.
//
// Deprecated: use RawLDML and implement inheritance manually or using the
// internal cldrtree package.
// Inheritance has changed quite a bit since the onset of this package and in
// practice data often represented in a way where knowledge of how it was
// inherited is relevant.
func (cldr *CLDR) LDML(loc string) (*LDML, error) {
return cldr.resolve(loc)
}
......
......@@ -27,7 +27,7 @@ const (
// cldrIndex is a Unicode-reserved sentinel value used to mark the start
// of a grouping within an index.
// We ignore any rule that starts with this rune.
// See http://unicode.org/reports/tr35/#Collation_Elements for details.
// See https://unicode.org/reports/tr35/#Collation_Elements for details.
cldrIndex = "\uFDD0"
// specialAnchor is the format in which to represent logical reset positions,
......@@ -51,7 +51,7 @@ func (c Collation) Process(p RuleProcessor) (err error) {
}
// processRules parses rules in the Collation Rule Syntax defined in
// http://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Tailorings.
// https://www.unicode.org/reports/tr35/tr35-collation.html#Collation_Tailorings.
func processRules(p RuleProcessor, s string) (err error) {
chk := func(s string, e error) string {
if err == nil {
......
......@@ -47,7 +47,7 @@ type Loader interface {
Reader(i int) (io.ReadCloser, error)
}
var fileRe = regexp.MustCompile(".*/(.*)/(.*)\\.xml")
var fileRe = regexp.MustCompile(`.*[/\\](.*)[/\\](.*)\.xml`)
// Decode loads and decodes the files represented by l.
func (d *Decoder) Decode(l Loader) (cldr *CLDR, err error) {
......@@ -58,9 +58,10 @@ func (d *Decoder) Decode(l Loader) (cldr *CLDR, err error) {
if len(d.dirFilter) > 0 && !in(d.dirFilter, m[1]) {
continue
}
var r io.Reader
var r io.ReadCloser
if r, err = l.Reader(i); err == nil {
err = d.decode(m[1], m[2], r)
r.Close()
}
if err != nil {
return nil, err
......@@ -100,7 +101,7 @@ func (d *Decoder) decode(dir, id string, r io.Reader) error {
if l.Identity == nil {
return fmt.Errorf("%s/%s: missing identity element", dir, id)
}
// TODO: verify when CLDR bug http://unicode.org/cldr/trac/ticket/8970
// TODO: verify when CLDR bug https://unicode.org/cldr/trac/ticket/8970
// is resolved.
// path := strings.Split(id, "_")
// if lang := l.Identity.Language.Type; lang != path[0] {
......
......@@ -153,7 +153,7 @@ var comments = map[string]string{
// Dates contains information regarding the format and parsing of dates and times.
`,
"localeDisplayNames": `
// LocaleDisplayNames specifies localized display names for for scripts, languages,
// LocaleDisplayNames specifies localized display names for scripts, languages,
// countries, currencies, and variants.
`,
"numbers": `
......
......@@ -5,7 +5,7 @@
package cldr
// This file implements the various inheritance constructs defined by LDML.
// See http://www.unicode.org/reports/tr35/#Inheritance_and_Validity
// See https://www.unicode.org/reports/tr35/#Inheritance_and_Validity
// for more details.
import (
......@@ -309,7 +309,7 @@ func in(set []string, s string) bool {
}
// attrKey computes a key based on the distinguishable attributes of
// an element and it's values.
// an element and its values.
func attrKey(v reflect.Value, exclude ...string) string {
parts := []string{}
ename := v.Interface().(Elem).GetCommon().name
......
......@@ -636,6 +636,13 @@ type SupplementalData struct {
Path string `xml:"path,attr"`
} `xml:"rgPath"`
} `xml:"rgScope"`
LanguageGroups *struct {
Common
LanguageGroup []*struct {
Common
Parent string `xml:"parent,attr"`
} `xml:"languageGroup"`
} `xml:"languageGroups"`
}
// LDML is the top-level type for locale-specific data.
......@@ -1230,7 +1237,7 @@ type TimeZoneNames struct {
} `xml:"metazone"`
}
// LocaleDisplayNames specifies localized display names for for scripts, languages,
// LocaleDisplayNames specifies localized display names for scripts, languages,
// countries, currencies, and variants.
type LocaleDisplayNames struct {
Common
......@@ -1484,4 +1491,4 @@ type Numbers struct {
}
// Version is the version of CLDR from which the XML definitions are generated.
const Version = "31"
const Version = "32"
......@@ -407,7 +407,7 @@ func decomposeHangul(buf []byte, r rune) int {
// decomposeHangul algorithmically decomposes a Hangul rune into
// its Jamo components.
// See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
// See https://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
func (rb *reorderBuffer) decomposeHangul(r rune) {
r -= hangulBase
x := r % jamoTCount
......@@ -420,7 +420,7 @@ func (rb *reorderBuffer) decomposeHangul(r rune) {
}
// combineHangul algorithmically combines Jamo character components into Hangul.
// See http://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
// See https://unicode.org/reports/tr15/#Hangul for details on combining Hangul.
func (rb *reorderBuffer) combineHangul(s, i, k int) {
b := rb.rune[:]
bn := rb.nrune
......@@ -461,6 +461,10 @@ func (rb *reorderBuffer) combineHangul(s, i, k int) {
// It should only be used to recompose a single segment, as it will not
// handle alternations between Hangul and non-Hangul characters correctly.
func (rb *reorderBuffer) compose() {
// Lazily load the map used by the combine func below, but do
// it outside of the loop.
recompMapOnce.Do(buildRecompMap)
// UAX #15, section X5 , including Corrigendum #5
// "In any character sequence beginning with starter S, a character C is
// blocked from S if and only if there is some character B between S
......
......@@ -4,6 +4,8 @@
package norm
import "encoding/binary"
// This file contains Form-specific logic and wrappers for data in tables.go.
// Rune info is stored in a separate trie per composing form. A composing form
......@@ -178,6 +180,17 @@ func (p Properties) TrailCCC() uint8 {
return ccc[p.tccc]
}
func buildRecompMap() {
recompMap = make(map[uint32]rune, len(recompMapPacked)/8)
var buf [8]byte
for i := 0; i < len(recompMapPacked); i += 8 {
copy(buf[:], recompMapPacked[i:i+8])
key := binary.BigEndian.Uint32(buf[:4])
val := binary.BigEndian.Uint32(buf[4:])
recompMap[key] = rune(val)
}
}
// Recomposition
// We use 32-bit keys instead of 64-bit for the two codepoint keys.
// This clips off the bits of three entries, but we know this will not
......@@ -186,8 +199,14 @@ func (p Properties) TrailCCC() uint8 {
// Note that the recomposition map for NFC and NFKC are identical.
// combine returns the combined rune or 0 if it doesn't exist.
//
// The caller is responsible for calling
// recompMapOnce.Do(buildRecompMap) sometime before this is called.
func combine(a, b rune) rune {
key := uint32(uint16(a))<<16 + uint32(uint16(b))
if recompMap == nil {
panic("caller error") // see func comment
}
return recompMap[key]
}
......
......@@ -128,8 +128,9 @@ func (i *Iter) Next() []byte {
func nextASCIIBytes(i *Iter) []byte {
p := i.p + 1
if p >= i.rb.nsrc {
p0 := i.p
i.setDone()
return i.rb.src.bytes[i.p:p]
return i.rb.src.bytes[p0:p]
}
if i.rb.src.bytes[p] < utf8.RuneSelf {
p0 := i.p
......
......@@ -12,6 +12,7 @@ package main
import (
"bytes"
"encoding/binary"
"flag"
"fmt"
"io"
......@@ -261,7 +262,7 @@ func compactCCC() {
// CompositionExclusions.txt has form:
// 0958 # ...
// See http://unicode.org/reports/tr44/ for full explanation
// See https://unicode.org/reports/tr44/ for full explanation
func loadCompositionExclusions() {
f := gen.OpenUCDFile("CompositionExclusions.txt")
defer f.Close()
......@@ -735,6 +736,8 @@ func makeTables() {
max = n
}
}
fmt.Fprintln(w, `import "sync"`)
fmt.Fprintln(w)
fmt.Fprintln(w, "const (")
fmt.Fprintln(w, "\t// Version is the Unicode edition from which the tables are derived.")
......@@ -782,20 +785,27 @@ func makeTables() {
sz := nrentries * 8
size += sz
fmt.Fprintf(w, "// recompMap: %d bytes (entries only)\n", sz)
fmt.Fprintln(w, "var recompMap = map[uint32]rune{")
fmt.Fprintln(w, "var recompMap map[uint32]rune")
fmt.Fprintln(w, "var recompMapOnce sync.Once\n")
fmt.Fprintln(w, `const recompMapPacked = "" +`)
var buf [8]byte
for i, c := range chars {
f := c.forms[FCanonical]
d := f.decomp
if !f.isOneWay && len(d) > 0 {
key := uint32(uint16(d[0]))<<16 + uint32(uint16(d[1]))
fmt.Fprintf(w, "0x%.8X: 0x%.4X,\n", key, i)
binary.BigEndian.PutUint32(buf[:4], key)
binary.BigEndian.PutUint32(buf[4:], uint32(i))
fmt.Fprintf(w, "\t\t%q + // 0x%.8X: 0x%.8X\n", string(buf[:]), key, uint32(i))
}
}
fmt.Fprintf(w, "}\n\n")
// hack so we don't have to special case the trailing plus sign
fmt.Fprintf(w, ` ""`)
fmt.Fprintln(w)
}
fmt.Fprintf(w, "// Total size of tables: %dKB (%d bytes)\n", (size+512)/1024, size)
gen.WriteGoFile("tables.go", "norm", w.Bytes())
gen.WriteVersionedGoFile("tables.go", "norm", w.Bytes())
}
func printChars() {
......@@ -857,7 +867,7 @@ func verifyComputed() {
// DerivedNormalizationProps.txt has form:
// 00C0..00C5 ; NFD_QC; N # ...
// 0374 ; NFD_QC; N # ...
// See http://unicode.org/reports/tr44/ for full explanation
// See https://unicode.org/reports/tr44/ for full explanation
func testDerived() {
f := gen.OpenUCDFile("DerivedNormalizationProps.txt")
defer f.Close()
......@@ -972,5 +982,5 @@ func printTestdata() {
}
}
fmt.Fprintln(w, "}")
gen.WriteGoFile("data_test.go", "norm", w.Bytes())
gen.WriteVersionedGoFile("data_test.go", "norm", w.Bytes())
}
......@@ -29,8 +29,8 @@ import (
// proceed independently on both sides:
// f(x) == append(f(x[0:n]), f(x[n:])...)
//
// References: http://unicode.org/reports/tr15/ and
// http://unicode.org/notes/tn5/.
// References: https://unicode.org/reports/tr15/ and
// https://unicode.org/notes/tn5/.
type Form int
const (
......
......@@ -60,8 +60,8 @@ func (w *normWriter) Close() error {
}
// Writer returns a new writer that implements Write(b)
// by writing f(b) to w. The returned writer may use an
// an internal buffer to maintain state across Write calls.
// by writing f(b) to w. The returned writer may use an
// internal buffer to maintain state across Write calls.
// Calling its Close method writes any buffered data to w.
func (f Form) Writer(w io.Writer) io.WriteCloser {
wr := &normWriter{rb: reorderBuffer{}, w: w}
......
......@@ -18,7 +18,6 @@ func (Form) Reset() {}
// Users should either catch ErrShortDst and allow dst to grow or have dst be at
// least of size MaxTransformChunkSize to be guaranteed of progress.
func (f Form) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
n := 0
// Cap the maximum number of src bytes to check.
b := src
eof := atEOF
......@@ -27,20 +26,21 @@ func (f Form) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
eof = false
b = b[:ns]
}
i, ok := formTable[f].quickSpan(inputBytes(b), n, len(b), eof)
n += copy(dst[n:], b[n:i])
i, ok := formTable[f].quickSpan(inputBytes(b), 0, len(b), eof)
n := copy(dst, b[:i])
if !ok {
nDst, nSrc, err = f.transform(dst[n:], src[n:], atEOF)
return nDst + n, nSrc + n, err
}
if n < len(src) && !atEOF {
if err == nil && n < len(src) && !atEOF {
err = transform.ErrShortSrc
}
return n, n, err
}
func flushTransform(rb *reorderBuffer) bool {
// Write out (must fully fit in dst, or else it is a ErrShortDst).
// Write out (must fully fit in dst, or else it is an ErrShortDst).
if len(rb.out) < rb.nrune*utf8.UTFMax {
return false
}
......@@ -79,7 +79,7 @@ func (f Form) transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error)
nSrc += n
nDst += n
if ok {
if n < rb.nsrc && !atEOF {
if err == nil && n < rb.nsrc && !atEOF {
err = transform.ErrShortSrc
}
return nDst, nSrc, err
......
......@@ -13,12 +13,13 @@ import (
"io"
"log"
"reflect"
"sort"
"strings"
"unicode"
"golang.org/x/text/collate"
"golang.org/x/text/internal/gen"
"golang.org/x/text/internal/ucd"
"golang.org/x/text/language"
"golang.org/x/text/unicode/rangetable"
)
......@@ -30,15 +31,16 @@ To bootstrap the code generation, run:
go run gen.go --versions=4.1.0,5.0.0,6.0.0,6.1.0,6.2.0,6.3.0,7.0.0
and ensure that the latest versions are included by checking:
http://www.unicode.org/Public/`
https://www.unicode.org/Public/`
func getVersions() []string {
if *versionList == "" {
log.Fatal(bootstrapMessage)
}
c := collate.New(language.Und, collate.Numeric)
versions := strings.Split(*versionList, ",")
sort.Strings(versions)
c.SortStrings(versions)
// Ensure that at least the current version is included.
for _, v := range versions {
......@@ -48,7 +50,7 @@ func getVersions() []string {
}
versions = append(versions, gen.UnicodeVersion())
sort.Strings(versions)
c.SortStrings(versions)
return versions
}
......@@ -74,7 +76,7 @@ func main() {
for _, v := range versions {
assigned := []rune{}
r := gen.Open("http://www.unicode.org/Public/", "", v+"/ucd/UnicodeData.txt")
r := gen.Open("https://www.unicode.org/Public/", "", v+"/ucd/UnicodeData.txt")
ucd.Parse(r, func(p *ucd.Parser) {
assigned = append(assigned, p.Rune(0))
})
......@@ -93,7 +95,7 @@ func main() {
fmt.Fprintf(w, "// Total size %d bytes (%d KiB)\n", size, size/1024)
gen.WriteGoFile("tables.go", "rangetable", w.Bytes())
gen.WriteVersionedGoFile("tables.go", "rangetable", w.Bytes())
}
func print(w io.Writer, rt *unicode.RangeTable) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册