func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string, stk *importStack) *Package { // Read the files in the directory to learn the structure // of the package. p := &Package{ ImportPath: importPath, Dir: dir, Standard: t.Goroot && !strings.Contains(importPath, "."), t: t, } packageCache[dir] = p packageCache[importPath] = p info, err := ctxt.ScanDir(dir) if err != nil { p.Error = &PackageError{ ImportStack: stk.copy(), Err: err.Error(), } p.Incomplete = true return p } p.info = info p.Name = info.Package p.Doc = firstSentence(info.PackageComment.Text()) p.Imports = info.Imports p.GoFiles = info.GoFiles p.TestGoFiles = info.TestGoFiles p.XTestGoFiles = info.XTestGoFiles p.CFiles = info.CFiles p.HFiles = info.HFiles p.SFiles = info.SFiles p.CgoFiles = info.CgoFiles p.CgoCFLAGS = info.CgoCFLAGS p.CgoLDFLAGS = info.CgoLDFLAGS if info.Package == "main" { _, elem := filepath.Split(importPath) p.target = filepath.Join(t.BinDir(), elem) if ctxt.GOOS == "windows" { p.target += ".exe" } } else { p.target = filepath.Join(t.PkgDir(), filepath.FromSlash(importPath)+".a") } var built time.Time if fi, err := os.Stat(p.target); err == nil { built = fi.ModTime() } // Build list of full paths to all Go files in the package, // for use by commands like go fmt. for _, f := range info.GoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } for _, f := range info.CgoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } for _, f := range info.TestGoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } for _, f := range info.XTestGoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } sort.Strings(p.gofiles) srcss := [][]string{ p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, } Stale: for _, srcs := range srcss { for _, src := range srcs { if fi, err := os.Stat(filepath.Join(p.Dir, src)); err != nil || fi.ModTime().After(built) { //println("STALE", p.ImportPath, "needs", src, err) p.Stale = true break Stale } } } importPaths := p.Imports // Packages that use cgo import runtime/cgo implicitly, // except runtime/cgo itself. if len(info.CgoFiles) > 0 && (!p.Standard || p.ImportPath != "runtime/cgo") { importPaths = append(importPaths, "runtime/cgo") } // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") } // Record package under both import path and full directory name. packageCache[dir] = p packageCache[importPath] = p // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) deps := make(map[string]bool) for _, path := range importPaths { if path == "C" { continue } deps[path] = true p1 := loadPackage(path, stk) imports = append(imports, p1) for _, dep := range p1.Deps { deps[dep] = true } if p1.Stale { p.Stale = true } if p1.Incomplete { p.Incomplete = true } // p1.target can be empty only if p1 is not a real package, // such as package unsafe or the temporary packages // created during go test. if !p.Stale && p1.target != "" { if fi, err := os.Stat(p1.target); err != nil || fi.ModTime().After(built) { //println("STALE", p.ImportPath, "needs", p1.target, err) //println("BUILT", built.String(), "VS", fi.ModTime().String()) p.Stale = true } } } p.imports = imports p.Deps = make([]string, 0, len(deps)) for dep := range deps { p.Deps = append(p.Deps, dep) } sort.Strings(p.Deps) for _, dep := range p.Deps { p1 := packageCache[dep] if p1 == nil { panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath) } p.deps = append(p.deps, p1) if p1.Error != nil { p.DepsErrors = append(p.DepsErrors, p1.Error) } } // unsafe is a fake package and is never out-of-date. if p.Standard && p.ImportPath == "unsafe" { p.Stale = false p.target = "" } return p }
func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string, stk *importStack, useAllFiles bool) *Package { // Read the files in the directory to learn the structure // of the package. p := &Package{ ImportPath: importPath, Dir: dir, Standard: t.Goroot && !strings.Contains(importPath, "."), t: t, } packageCache[dir] = p packageCache[importPath] = p ctxt.UseAllFiles = useAllFiles info, err := ctxt.ScanDir(dir) useAllFiles = false // flag does not apply to dependencies if err != nil { p.Error = &PackageError{ ImportStack: stk.copy(), Err: err.Error(), } // Look for parser errors. if err, ok := err.(scanner.ErrorList); ok { // Prepare error with \n before each message. // When printed in something like context: %v // this will put the leading file positions each on // its own line. It will also show all the errors // instead of just the first, as err.Error does. var buf bytes.Buffer for _, e := range err { buf.WriteString("\n") buf.WriteString(e.Error()) } p.Error.Err = buf.String() } p.Incomplete = true return p } p.info = info p.Name = info.Package p.Doc = doc.Synopsis(info.PackageComment.Text()) p.Imports = info.Imports p.GoFiles = info.GoFiles p.TestGoFiles = info.TestGoFiles p.XTestGoFiles = info.XTestGoFiles p.CFiles = info.CFiles p.HFiles = info.HFiles p.SFiles = info.SFiles p.CgoFiles = info.CgoFiles p.CgoCFLAGS = info.CgoCFLAGS p.CgoLDFLAGS = info.CgoLDFLAGS if info.Package == "main" { _, elem := filepath.Split(importPath) full := ctxt.GOOS + "_" + ctxt.GOARCH + "/" + elem if t.Goroot && isGoTool[p.ImportPath] { p.target = filepath.Join(t.Path, "pkg/tool", full) } else { if ctxt.GOOS != toolGOOS || ctxt.GOARCH != toolGOARCH { // Install cross-compiled binaries to subdirectories of bin. elem = full } p.target = filepath.Join(t.BinDir(), elem) } if ctxt.GOOS == "windows" { p.target += ".exe" } } else { dir := t.PkgDir() // For gccgo, rewrite p.target with the expected library name. if _, ok := buildToolchain.(gccgoToolchain); ok { dir = filepath.Join(filepath.Dir(dir), "gccgo", filepath.Base(dir)) } p.target = buildToolchain.pkgpath(dir, p) } var built time.Time if fi, err := os.Stat(p.target); err == nil { built = fi.ModTime() } // Build list of full paths to all Go files in the package, // for use by commands like go fmt. for _, f := range info.GoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } for _, f := range info.CgoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } for _, f := range info.TestGoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } for _, f := range info.XTestGoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) } sort.Strings(p.gofiles) srcss := [][]string{ p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, } Stale: for _, srcs := range srcss { for _, src := range srcs { if fi, err := os.Stat(filepath.Join(p.Dir, src)); err != nil || fi.ModTime().After(built) { //println("STALE", p.ImportPath, "needs", src, err) p.Stale = true break Stale } } } importPaths := p.Imports // Packages that use cgo import runtime/cgo implicitly, // except runtime/cgo itself. if len(info.CgoFiles) > 0 && (!p.Standard || p.ImportPath != "runtime/cgo") { importPaths = append(importPaths, "runtime/cgo") } // Everything depends on runtime, except runtime and unsafe. if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") { importPaths = append(importPaths, "runtime") } // Record package under both import path and full directory name. packageCache[dir] = p packageCache[importPath] = p // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) deps := make(map[string]bool) for _, path := range importPaths { if path == "C" { continue } deps[path] = true p1 := loadPackage(path, stk) if p1.Error != nil { if info.ImportPos != nil && len(info.ImportPos[path]) > 0 { pos := info.ImportPos[path][0] p1.Error.Pos = pos.String() } } imports = append(imports, p1) for _, dep := range p1.Deps { deps[dep] = true } if p1.Stale { p.Stale = true } if p1.Incomplete { p.Incomplete = true } // p1.target can be empty only if p1 is not a real package, // such as package unsafe or the temporary packages // created during go test. if !p.Stale && p1.target != "" { if fi, err := os.Stat(p1.target); err != nil || fi.ModTime().After(built) { //println("STALE", p.ImportPath, "needs", p1.target, err) //println("BUILT", built.String(), "VS", fi.ModTime().String()) p.Stale = true } } } p.imports = imports p.Deps = make([]string, 0, len(deps)) for dep := range deps { p.Deps = append(p.Deps, dep) } sort.Strings(p.Deps) for _, dep := range p.Deps { p1 := packageCache[dep] if p1 == nil { panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath) } p.deps = append(p.deps, p1) if p1.Error != nil { p.DepsErrors = append(p.DepsErrors, p1.Error) } } // unsafe is a fake package and is never out-of-date. if p.Standard && p.ImportPath == "unsafe" { p.Stale = false p.target = "" } p.Target = p.target return p }