To make sure that Go code will work when moved to a
system with a case-insensitive file system, like OS X or Windows,
reject any package built from files with names differing
only in case, and also any package built from imported
dependencies with names differing only in case.
Fixes #4773.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/
7314104
}
return x
}
+
+// toFold returns a string with the property that
+// strings.EqualFold(s, t) iff toFold(s) == toFold(t)
+// This lets us test a large set of strings for fold-equivalent
+// duplicates without making a quadratic number of calls
+// to EqualFold. Note that strings.ToUpper and strings.ToLower
+// have the desired property in some corner cases.
+func toFold(s string) string {
+ // Fast path: all ASCII, no upper case.
+ // Most paths look like this already.
+ for i := 0; i < len(s); i++ {
+ c := s[i]
+ if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
+ goto Slow
+ }
+ }
+ return s
+
+Slow:
+ var buf bytes.Buffer
+ for _, r := range s {
+ // SimpleFold(x) cycles to the next equivalent rune > x
+ // or wraps around to smaller values. Iterate until it wraps,
+ // and we've found the minimum value.
+ for {
+ r0 := r
+ r = unicode.SimpleFold(r0)
+ if r <= r0 {
+ break
+ }
+ }
+ // Exception to allow fast path above: A-Z => a-z
+ if 'A' <= r && r <= 'Z' {
+ r += 'a' - 'A'
+ }
+ buf.WriteRune(r)
+ }
+ return buf.String()
+}
+
+// foldDup reports a pair of strings from the list that are
+// equal according to strings.EqualFold.
+// It returns "", "" if there are no such strings.
+func foldDup(list []string) (string, string) {
+ clash := map[string]string{}
+ for _, s := range list {
+ fold := toFold(s)
+ if t := clash[fold]; t != "" {
+ if s > t {
+ s, t = t, s
+ }
+ return s, t
+ }
+ clash[fold] = s
+ }
+ return "", ""
+}
// is the most important thing.
return p.Pos + ": " + p.Err
}
+ if len(p.ImportStack) == 0 {
+ return p.Err
+ }
return "package " + strings.Join(p.ImportStack, "\n\timports ") + ": " + p.Err
}
p.allgofiles = append(p.allgofiles, p.gofiles...)
sort.Strings(p.allgofiles)
+ // Check for case-insensitive collision of input files.
+ // To avoid problems on case-insensitive files, we reject any package
+ // where two different input files have equal names under a case-insensitive
+ // comparison.
+ f1, f2 := foldDup(stringList(
+ p.GoFiles,
+ p.CgoFiles,
+ p.IgnoredGoFiles,
+ p.CFiles,
+ p.HFiles,
+ p.SFiles,
+ p.SysoFiles,
+ p.SwigFiles,
+ p.SwigCXXFiles,
+ p.TestGoFiles,
+ p.XTestGoFiles,
+ ))
+ if f1 != "" {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("case-insensitive file name collision: %q and %q", f1, f2),
+ }
+ return p
+ }
+
// Build list of imported packages and full dependency list.
imports := make([]*Package, 0, len(p.Imports))
deps := make(map[string]bool)
if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
p.target = ""
}
-
p.Target = p.target
+
+ // In the absence of errors lower in the dependency tree,
+ // check for case-insensitive collisions of import paths.
+ if len(p.DepsErrors) == 0 {
+ dep1, dep2 := foldDup(p.Deps)
+ if dep1 != "" {
+ p.Error = &PackageError{
+ ImportStack: stk.copy(),
+ Err: fmt.Sprintf("case-insensitive import collision: %q and %q", dep1, dep2),
+ }
+ return p
+ }
+ }
+
return p
}
unset GOPATH
rm -rf $d
+# issue 4773. case-insensitive collisions
+d=$(mktemp -d -t testgo)
+export GOPATH=$d
+mkdir -p $d/src/example/a $d/src/example/b
+cat >$d/src/example/a/a.go <<EOF
+package p
+import (
+ _ "math/rand"
+ _ "math/Rand"
+)
+EOF
+if ./testgo list example/a 2>$d/out; then
+ echo go list example/a should have failed, did not.
+ ok=false
+elif ! grep "case-insensitive import collision" $d/out >/dev/null; then
+ echo go list example/a did not report import collision.
+ ok=false
+fi
+cat >$d/src/example/b/file.go <<EOF
+package b
+EOF
+cat >$d/src/example/b/FILE.go <<EOF
+package b
+EOF
+if [ $(ls $d/src/example/b | wc -l) = 2 ]; then
+ # case-sensitive file system, let directory read find both files
+ args="example/b"
+else
+ # case-insensitive file system, list files explicitly on command line.
+ args="$d/src/example/b/file.go $d/src/example/b/FILE.go"
+fi
+if ./testgo list $args 2>$d/out; then
+ echo go list example/b should have failed, did not.
+ ok=false
+elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then
+ echo go list example/b did not report file name collision.
+ ok=false
+fi
+unset GOPATH
+rm -rf $d
+
# Only succeeds if source order is preserved.
./testgo test testdata/example[12]_test.go