@@ -3,7 +3,18 @@ package gps
3
3
import (
4
4
"bytes"
5
5
"crypto/sha256"
6
+ "io"
6
7
"sort"
8
+ "strings"
9
+ )
10
+
11
+ // string headers used to demarcate sections in hash input creation
12
+ const (
13
+ hhConstraints = "-CONSTRAINTS-"
14
+ hhImportsReqs = "-IMPORTS/REQS-"
15
+ hhIgnores = "-IGNORES-"
16
+ hhOverrides = "-OVERRIDES-"
17
+ hhAnalyzer = "-ANALYZER-"
7
18
)
8
19
9
20
// HashInputs computes a hash digest of all data in SolveParams and the
@@ -17,99 +28,91 @@ import (
17
28
//
18
29
// (Basically, this is for memoization.)
19
30
func (s * solver ) HashInputs () (digest []byte ) {
20
- buf := new (bytes. Buffer )
21
- s .writeHashingInputs (buf )
31
+ h := sha256 . New ( )
32
+ s .writeHashingInputs (h )
22
33
23
- hd := sha256 . Sum256 ( buf . Bytes () )
34
+ hd := h . Sum ( nil )
24
35
digest = hd [:]
25
36
return
26
37
}
27
38
28
- func (s * solver ) writeHashingInputs (buf * bytes.Buffer ) {
29
- // Apply overrides to the constraints from the root. Otherwise, the hash
30
- // would be computed on the basis of a constraint from root that doesn't
31
- // actually affect solving.
32
- p := s .ovr .overrideAll (s .rm .DependencyConstraints ().merge (s .rm .TestDependencyConstraints ()))
33
-
34
- for _ , pd := range p {
35
- buf .WriteString (string (pd .Ident .ProjectRoot ))
36
- buf .WriteString (pd .Ident .Source )
37
- // FIXME Constraint.String() is a surjective-only transformation - tags
38
- // and branches with the same name are written out as the same string.
39
- // This could, albeit rarely, result in input collisions when a real
40
- // change has occurred.
41
- buf .WriteString (pd .Constraint .String ())
42
- }
43
-
44
- // Write each of the packages, or the errors that were found for a
45
- // particular subpath, into the hash. We need to do this in a
46
- // deterministic order, so expand and sort the map.
47
- var pkgs []PackageOrErr
48
- for _ , perr := range s .rpt .Packages {
49
- pkgs = append (pkgs , perr )
50
- }
51
- sort .Sort (sortPackageOrErr (pkgs ))
52
- for _ , perr := range pkgs {
53
- if perr .Err != nil {
54
- buf .WriteString (perr .Err .Error ())
55
- } else {
56
- buf .WriteString (perr .P .Name )
57
- buf .WriteString (perr .P .CommentPath )
58
- buf .WriteString (perr .P .ImportPath )
59
- for _ , imp := range perr .P .Imports {
60
- if ! isStdLib (imp ) {
61
- buf .WriteString (imp )
62
- }
63
- }
64
- for _ , imp := range perr .P .TestImports {
65
- if ! isStdLib (imp ) {
66
- buf .WriteString (imp )
67
- }
68
- }
39
+ func (s * solver ) writeHashingInputs (w io.Writer ) {
40
+ writeString := func (s string ) {
41
+ // Skip zero-length string writes; it doesn't affect the real hash
42
+ // calculation, and keeps misleading newlines from showing up in the
43
+ // debug output.
44
+ if s != "" {
45
+ // All users of writeHashingInputs cannot error on Write(), so just
46
+ // ignore it
47
+ w .Write ([]byte (s ))
69
48
}
70
49
}
71
50
72
- // Write any require packages given in the root manifest.
73
- if len (s .req ) > 0 {
74
- // Dump and sort the reqnores
75
- req := make ([]string , 0 , len (s .req ))
76
- for pkg := range s .req {
77
- req = append (req , pkg )
78
- }
79
- sort .Strings (req )
51
+ // We write "section headers" into the hash purely to ease scanning when
52
+ // debugging this input-constructing algorithm; as long as the headers are
53
+ // constant, then they're effectively a no-op.
54
+ writeString (hhConstraints )
55
+
56
+ // getApplicableConstraints will apply overrides, incorporate requireds,
57
+ // apply local ignores, drop stdlib imports, and finally trim out
58
+ // ineffectual constraints.
59
+ for _ , pd := range s .rd .getApplicableConstraints () {
60
+ writeString (string (pd .Ident .ProjectRoot ))
61
+ writeString (pd .Ident .Source )
62
+ writeString (typedConstraintString (pd .Constraint ))
63
+ }
80
64
81
- for _ , reqp := range req {
82
- buf .WriteString (reqp )
83
- }
65
+ // Write out each discrete import, including those derived from requires.
66
+ writeString (hhImportsReqs )
67
+ imports := s .rd .externalImportList ()
68
+ sort .Strings (imports )
69
+ for _ , im := range imports {
70
+ writeString (im )
84
71
}
85
72
86
- // Add the ignored packages, if any.
87
- if len (s .ig ) > 0 {
88
- // Dump and sort the ignores
89
- ig := make ([]string , 0 , len (s .ig ))
90
- for pkg := range s .ig {
73
+ // Add ignores, skipping any that point under the current project root;
74
+ // those will have already been implicitly incorporated by the import
75
+ // lister.
76
+ writeString (hhIgnores )
77
+ ig := make ([]string , 0 , len (s .rd .ig ))
78
+ for pkg := range s .rd .ig {
79
+ if ! strings .HasPrefix (pkg , s .rd .rpt .ImportRoot ) || ! isPathPrefixOrEqual (s .rd .rpt .ImportRoot , pkg ) {
91
80
ig = append (ig , pkg )
92
81
}
93
- sort .Strings (ig )
82
+ }
83
+ sort .Strings (ig )
94
84
95
- for _ , igp := range ig {
96
- buf .WriteString (igp )
97
- }
85
+ for _ , igp := range ig {
86
+ writeString (igp )
98
87
}
99
88
100
- for _ , pc := range s .ovr .asSortedSlice () {
101
- buf .WriteString (string (pc .Ident .ProjectRoot ))
89
+ // Overrides *also* need their own special entry distinct from basic
90
+ // constraints, to represent the unique effects they can have on the entire
91
+ // solving process beyond root's immediate scope.
92
+ writeString (hhOverrides )
93
+ for _ , pc := range s .rd .ovr .asSortedSlice () {
94
+ writeString (string (pc .Ident .ProjectRoot ))
102
95
if pc .Ident .Source != "" {
103
- buf . WriteString (pc .Ident .Source )
96
+ writeString (pc .Ident .Source )
104
97
}
105
98
if pc .Constraint != nil {
106
- buf . WriteString ( pc .Constraint . String ( ))
99
+ writeString ( typedConstraintString ( pc .Constraint ))
107
100
}
108
101
}
109
102
103
+ writeString (hhAnalyzer )
110
104
an , av := s .b .AnalyzerInfo ()
111
- buf .WriteString (an )
112
- buf .WriteString (av .String ())
105
+ writeString (an )
106
+ writeString (av .String ())
107
+ }
108
+
109
+ // bytes.Buffer wrapper that injects newlines after each call to Write().
110
+ type nlbuf bytes.Buffer
111
+
112
+ func (buf * nlbuf ) Write (p []byte ) (n int , err error ) {
113
+ n , _ = (* bytes .Buffer )(buf ).Write (p )
114
+ (* bytes .Buffer )(buf ).WriteByte ('\n' )
115
+ return n + 1 , nil
113
116
}
114
117
115
118
// HashingInputsAsString returns the raw input data used by Solver.HashInputs()
@@ -118,10 +121,10 @@ func (s *solver) writeHashingInputs(buf *bytes.Buffer) {
118
121
// This is primarily intended for debugging purposes.
119
122
func HashingInputsAsString (s Solver ) string {
120
123
ts := s .(* solver )
121
- buf := new (bytes. Buffer )
124
+ buf := new (nlbuf )
122
125
ts .writeHashingInputs (buf )
123
126
124
- return buf .String ()
127
+ return ( * bytes . Buffer )( buf ) .String ()
125
128
}
126
129
127
130
type sortPackageOrErr []PackageOrErr
0 commit comments