package build
import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
+ "strconv"
"strings"
"testing"
)
// that shows up in programs that use cgo.
"C": {},
+ // Race detector uses cgo.
+ "runtime/race": {"C"},
+
// Plan 9 alone needs io/ioutil and os.
"os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"},
test := func(mustImport bool) {
for _, pkg := range all {
- if pkg == "runtime/cgo" && !ctxt.CgoEnabled {
- continue
- }
- p, err := ctxt.Import(pkg, "", 0)
+ imports, err := findImports(pkg)
if err != nil {
- if _, ok := err.(*NoGoError); ok {
- continue
- }
- if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
- continue
- }
- if !ctxt.CgoEnabled && pkg == "runtime/cgo" {
- continue
- }
- // Some of the combinations we try might not
- // be reasonable (like arm,plan9,cgo), so ignore
- // errors for the auto-generated combinations.
- if !mustImport {
- continue
- }
- t.Errorf("%s/%s/cgo=%v %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, err)
+ t.Error(err)
continue
}
ok := allowed(pkg)
var bad []string
- for _, imp := range p.Imports {
+ for _, imp := range imports {
if !ok[imp] {
bad = append(bad, imp)
}
}
if bad != nil {
- t.Errorf("%s/%s/cgo=%v unexpected dependency: %s imports %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, pkg, bad)
+ t.Errorf("unexpected dependency: %s imports %v", pkg, bad)
}
}
}
test(true)
+}
- if testing.Short() {
- t.Logf("skipping other systems")
- return
- }
+var buildIgnore = []byte("\n// +build ignore")
- for _, ctxt.GOOS = range geese {
- for _, ctxt.GOARCH = range goarches {
- for _, ctxt.CgoEnabled = range bools {
- test(false)
+func findImports(pkg string) ([]string, error) {
+ dir := filepath.Join(Default.GOROOT, "src", pkg)
+ files, err := ioutil.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+ var imports []string
+ var haveImport = map[string]bool{}
+ for _, file := range files {
+ name := file.Name()
+ if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
+ continue
+ }
+ f, err := os.Open(filepath.Join(dir, name))
+ if err != nil {
+ return nil, err
+ }
+ var imp []string
+ data, err := readImports(f, false, &imp)
+ f.Close()
+ if err != nil {
+ return nil, fmt.Errorf("reading %v: %v", name, err)
+ }
+ if bytes.Contains(data, buildIgnore) {
+ continue
+ }
+ for _, quoted := range imp {
+ path, err := strconv.Unquote(quoted)
+ if err != nil {
+ continue
+ }
+ if !haveImport[path] {
+ haveImport[path] = true
+ imports = append(imports, path)
}
}
}
+ sort.Strings(imports)
+ return imports, nil
}
// readString reads a quoted string literal from the input.
// If an identifier is not present, readString records a syntax error.
-func (r *importReader) readString() {
+func (r *importReader) readString(save *[]string) {
switch r.nextByte(true) {
case '`':
+ start := len(r.buf) - 1
for r.err == nil {
if r.nextByte(false) == '`' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
break
}
if r.eof {
}
}
case '"':
+ start := len(r.buf) - 1
for r.err == nil {
c := r.nextByte(false)
if c == '"' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
break
}
if r.eof || c == '\n' {
// readImport reads an import clause - optional identifier followed by quoted string -
// from the input.
-func (r *importReader) readImport() {
+func (r *importReader) readImport(imports *[]string) {
c := r.peekByte(true)
if c == '.' {
r.peek = 0
} else if isIdent(c) {
r.readIdent()
}
- r.readString()
+ r.readString(imports)
}
// readComments is like ioutil.ReadAll, except that it only reads the leading
// readImports is like ioutil.ReadAll, except that it expects a Go file as input
// and stops reading the input once the imports have completed.
-func readImports(f io.Reader, reportSyntaxError bool) ([]byte, error) {
+func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r.readKeyword("package")
if r.peekByte(true) == '(' {
r.nextByte(false)
for r.peekByte(true) != ')' && r.err == nil {
- r.readImport()
+ r.readImport(imports)
}
r.nextByte(false)
} else {
- r.readImport()
+ r.readImport(imports)
}
}
}
func TestReadImports(t *testing.T) {
- testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) })
+ testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
}
func TestReadComments(t *testing.T) {
func TestReadFailures(t *testing.T) {
// Errors should be reported (true arg to readImports).
- testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) })
+ testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
}
func TestReadFailuresIgnored(t *testing.T) {
tt.err = ""
}
}
- testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false) })
+ testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false, nil) })
}