зеркало из https://github.com/golang/dep.git
134 строки
3.9 KiB
Go
134 строки
3.9 KiB
Go
// 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()
|
|
}
|