forked from golang/dep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hash.go
133 lines (116 loc) · 3.88 KB
/
hash.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gps
import (
"bytes"
"crypto/sha256"
"io"
"sort"
"strconv"
"strings"
)
// string headers used to demarcate sections in hash input creation
const (
hhConstraints = "-CONSTRAINTS-"
hhImportsReqs = "-IMPORTS/REQS-"
hhIgnores = "-IGNORES-"
hhOverrides = "-OVERRIDES-"
hhAnalyzer = "-ANALYZER-"
)
// HashInputs computes a hash digest of all data in SolveParams and the
// RootManifest that act as function inputs to Solve().
//
// The digest returned from this function is the same as the digest that would
// be included with a Solve() Result. As such, it's appropriate for comparison
// against the digest stored in a lock file, generated by a previous Solve(): if
// the digests match, then manifest and lock are in sync, and a Solve() is
// unnecessary.
//
// (Basically, this is for memoization.)
func (s *solver) HashInputs() (digest []byte) {
h := sha256.New()
s.writeHashingInputs(h)
hd := h.Sum(nil)
digest = hd[:]
return
}
func (s *solver) writeHashingInputs(w io.Writer) {
writeString := func(s string) {
// Skip zero-length string writes; it doesn't affect the real hash
// calculation, and keeps misleading newlines from showing up in the
// debug output.
if s != "" {
// All users of writeHashingInputs cannot error on Write(), so just
// ignore it
w.Write([]byte(s))
}
}
// We write "section headers" into the hash purely to ease scanning when
// debugging this input-constructing algorithm; as long as the headers are
// constant, then they're effectively a no-op.
writeString(hhConstraints)
// getApplicableConstraints will apply overrides, incorporate requireds,
// apply local ignores, drop stdlib imports, and finally trim out
// ineffectual constraints.
for _, pd := range s.rd.getApplicableConstraints(s.stdLibFn) {
writeString(string(pd.Ident.ProjectRoot))
writeString(pd.Ident.Source)
writeString(pd.Constraint.typedString())
}
// Write out each discrete import, including those derived from requires.
writeString(hhImportsReqs)
imports := s.rd.externalImportList(s.stdLibFn)
sort.Strings(imports)
for _, im := range imports {
writeString(im)
}
// Add ignores, skipping any that point under the current project root;
// those will have already been implicitly incorporated by the import
// lister.
writeString(hhIgnores)
ig := s.rd.ir.ToSlice()
sort.Strings(ig)
for _, igp := range ig {
// Typical prefix comparison checks will erroneously fail if the wildcard
// is present. Trim it off, if present.
tigp := strings.TrimSuffix(igp, "*")
if !strings.HasPrefix(tigp, s.rd.rpt.ImportRoot) || !isPathPrefixOrEqual(s.rd.rpt.ImportRoot, tigp) {
writeString(igp)
}
}
// Overrides *also* need their own special entry distinct from basic
// constraints, to represent the unique effects they can have on the entire
// solving process beyond root's immediate scope.
writeString(hhOverrides)
for _, pc := range s.rd.ovr.asSortedSlice() {
writeString(string(pc.Ident.ProjectRoot))
if pc.Ident.Source != "" {
writeString(pc.Ident.Source)
}
if pc.Constraint != nil {
writeString(pc.Constraint.typedString())
}
}
writeString(hhAnalyzer)
ai := s.rd.an.Info()
writeString(ai.Name)
writeString(strconv.Itoa(ai.Version))
}
// bytes.Buffer wrapper that injects newlines after each call to Write().
type nlbuf bytes.Buffer
func (buf *nlbuf) Write(p []byte) (n int, err error) {
n, _ = (*bytes.Buffer)(buf).Write(p)
(*bytes.Buffer)(buf).WriteByte('\n')
return n + 1, nil
}
// HashingInputsAsString returns the raw input data used by Solver.HashInputs()
// as a string.
//
// This is primarily intended for debugging purposes.
func HashingInputsAsString(s Solver) string {
ts := s.(*solver)
buf := new(nlbuf)
ts.writeHashingInputs(buf)
return (*bytes.Buffer)(buf).String()
}