libgo: update to Go 1.11
[official-gcc.git] / libgo / go / cmd / go / internal / modfetch / pseudo.go
blob32c7bf883becd471344061028757e777f8d3c8a0
1 // Copyright 2018 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.
5 // Pseudo-versions
6 //
7 // Code authors are expected to tag the revisions they want users to use,
8 // including prereleases. However, not all authors tag versions at all,
9 // and not all commits a user might want to try will have tags.
10 // A pseudo-version is a version with a special form that allows us to
11 // address an untagged commit and order that version with respect to
12 // other versions we might encounter.
14 // A pseudo-version takes one of the general forms:
16 // (1) vX.0.0-yyyymmddhhmmss-abcdef123456
17 // (2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456
18 // (3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible
19 // (4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456
20 // (5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible
22 // If there is no recently tagged version with the right major version vX,
23 // then form (1) is used, creating a space of pseudo-versions at the bottom
24 // of the vX version range, less than any tagged version, including the unlikely v0.0.0.
26 // If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible,
27 // then the pseudo-version uses form (2) or (3), making it a prerelease for the next
28 // possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string
29 // ensures that the pseudo-version compares less than possible future explicit prereleases
30 // like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1.
32 // If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible,
33 // then the pseudo-version uses form (4) or (5), making it a slightly later prerelease.
35 package modfetch
37 import (
38 "cmd/go/internal/semver"
39 "fmt"
40 "regexp"
41 "strings"
42 "time"
45 // PseudoVersion returns a pseudo-version for the given major version ("v1")
46 // preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time,
47 // and revision identifier (usually a 12-byte commit hash prefix).
48 func PseudoVersion(major, older string, t time.Time, rev string) string {
49 if major == "" {
50 major = "v0"
52 major = strings.TrimSuffix(major, "-unstable") // make gopkg.in/macaroon-bakery.v2-unstable use "v2"
53 segment := fmt.Sprintf("%s-%s", t.UTC().Format("20060102150405"), rev)
54 build := semver.Build(older)
55 older = semver.Canonical(older)
56 if older == "" {
57 return major + ".0.0-" + segment // form (1)
59 if semver.Prerelease(older) != "" {
60 return older + ".0." + segment + build // form (4), (5)
63 // Form (2), (3).
64 // Extract patch from vMAJOR.MINOR.PATCH
65 v := older[:len(older)]
66 i := strings.LastIndex(v, ".") + 1
67 v, patch := v[:i], v[i:]
69 // Increment PATCH by adding 1 to decimal:
70 // scan right to left turning 9s to 0s until you find a digit to increment.
71 // (Number might exceed int64, but math/big is overkill.)
72 digits := []byte(patch)
73 for i = len(digits) - 1; i >= 0 && digits[i] == '9'; i-- {
74 digits[i] = '0'
76 if i >= 0 {
77 digits[i]++
78 } else {
79 // digits is all zeros
80 digits[0] = '1'
81 digits = append(digits, '0')
83 patch = string(digits)
85 // Reassemble.
86 return v + patch + "-0." + segment + build
89 var pseudoVersionRE = regexp.MustCompile(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+incompatible)?$`)
91 // IsPseudoVersion reports whether v is a pseudo-version.
92 func IsPseudoVersion(v string) bool {
93 return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v)
96 // PseudoVersionTime returns the time stamp of the pseudo-version v.
97 // It returns an error if v is not a pseudo-version or if the time stamp
98 // embedded in the pseudo-version is not a valid time.
99 func PseudoVersionTime(v string) (time.Time, error) {
100 timestamp, _, err := parsePseudoVersion(v)
101 t, err := time.Parse("20060102150405", timestamp)
102 if err != nil {
103 return time.Time{}, fmt.Errorf("pseudo-version with malformed time %s: %q", timestamp, v)
105 return t, nil
108 // PseudoVersionRev returns the revision identifier of the pseudo-version v.
109 // It returns an error if v is not a pseudo-version.
110 func PseudoVersionRev(v string) (rev string, err error) {
111 _, rev, err = parsePseudoVersion(v)
112 return
115 func parsePseudoVersion(v string) (timestamp, rev string, err error) {
116 if !IsPseudoVersion(v) {
117 return "", "", fmt.Errorf("malformed pseudo-version %q", v)
119 v = strings.TrimSuffix(v, "+incompatible")
120 j := strings.LastIndex(v, "-")
121 v, rev = v[:j], v[j+1:]
122 i := strings.LastIndex(v, "-")
123 if j := strings.LastIndex(v, "."); j > i {
124 timestamp = v[j+1:]
125 } else {
126 timestamp = v[i+1:]
128 return timestamp, rev, nil