]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/go/internal/toolchain/reqs.go
e5ca8d0eb4c2d07165ecfd3edc66eee2878b22c3
[gostls13.git] / src / cmd / go / internal / toolchain / reqs.go
1 // Copyright 2023 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 toolchain
6
7 import (
8         "context"
9         "fmt"
10         "os"
11
12         "cmd/go/internal/base"
13         "cmd/go/internal/gover"
14 )
15
16 // A Switcher collects errors to be reported and then decides
17 // between reporting the errors or switching to a new toolchain
18 // to resolve them.
19 //
20 // The client calls [Switcher.Error] repeatedly with errors encountered
21 // and then calls [Switcher.Switch]. If the errors included any
22 // *gover.TooNewErrors (potentially wrapped) and switching is
23 // permitted by GOTOOLCHAIN, Switch switches to a new toolchain.
24 // Otherwise Switch prints all the errors using base.Error.
25 type Switcher struct {
26         TooNew *gover.TooNewError // max go requirement observed
27         Errors []error            // errors collected so far
28 }
29
30 // Error reports the error to the Switcher,
31 // which saves it for processing during Switch.
32 func (s *Switcher) Error(err error) {
33         s.Errors = append(s.Errors, err)
34         s.addTooNew(err)
35 }
36
37 // addTooNew adds any TooNew errors that can be found in err.
38 func (s *Switcher) addTooNew(err error) {
39         switch err := err.(type) {
40         case interface{ Unwrap() []error }:
41                 for _, e := range err.Unwrap() {
42                         s.addTooNew(e)
43                 }
44
45         case interface{ Unwrap() error }:
46                 s.addTooNew(err.Unwrap())
47
48         case *gover.TooNewError:
49                 if s.TooNew == nil ||
50                         gover.Compare(err.GoVersion, s.TooNew.GoVersion) > 0 ||
51                         gover.Compare(err.GoVersion, s.TooNew.GoVersion) == 0 && err.What < s.TooNew.What {
52                         s.TooNew = err
53                 }
54         }
55 }
56
57 // NeedSwitch reports whether Switch would attempt to switch toolchains.
58 func (s *Switcher) NeedSwitch() bool {
59         return s.TooNew != nil && (HasAuto() || HasPath())
60 }
61
62 // Switch decides whether to switch to a newer toolchain
63 // to resolve any of the saved errors.
64 // It switches if toolchain switches are permitted and there is at least one TooNewError.
65 //
66 // If Switch decides not to switch toolchains, it prints the errors using base.Error and returns.
67 //
68 // If Switch decides to switch toolchains but cannot identify a toolchain to use.
69 // it prints the errors along with one more about not being able to find the toolchain
70 // and returns.
71 //
72 // Otherwise, Switch prints an informational message giving a reason for the
73 // switch and the toolchain being invoked and then switches toolchains.
74 // This operation never returns.
75 func (s *Switcher) Switch(ctx context.Context) {
76         if !s.NeedSwitch() {
77                 for _, err := range s.Errors {
78                         base.Error(err)
79                 }
80                 return
81         }
82
83         // Switch to newer Go toolchain if necessary and possible.
84         tv, err := NewerToolchain(ctx, s.TooNew.GoVersion)
85         if err != nil {
86                 for _, err := range s.Errors {
87                         base.Error(err)
88                 }
89                 base.Error(fmt.Errorf("switching to go >= %v: %w", s.TooNew.GoVersion, err))
90                 return
91         }
92
93         fmt.Fprintf(os.Stderr, "go: %v requires go >= %v; switching to %v\n", s.TooNew.What, s.TooNew.GoVersion, tv)
94         SwitchTo(tv)
95         panic("unreachable")
96 }
97
98 // SwitchOrFatal attempts a toolchain switch based on the information in err
99 // and otherwise falls back to base.Fatal(err).
100 func SwitchOrFatal(ctx context.Context, err error) {
101         var s Switcher
102         s.Error(err)
103         s.Switch(ctx)
104         base.Exit()
105 }