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 }
// makeMakefile computes the standard Makefile for the directory dir // installing as package pkg. It includes all *.go files in the directory // except those in package main and those ending in _test.go. func makeMakefile(dir, pkg string, tree *build.Tree, isCmd bool) ([]byte, os.Error) { if !safeName(pkg) { return nil, os.NewError("unsafe name: " + pkg) } targ := pkg targDir := tree.PkgDir() if isCmd { // use the last part of the package name for targ _, targ = filepath.Split(pkg) targDir = tree.BinDir() } dirInfo, err := build.ScanDir(dir, isCmd) if err != nil { return nil, err } cgoFiles := dirInfo.CgoFiles isCgo := make(map[string]bool, len(cgoFiles)) for _, file := range cgoFiles { if !safeName(file) { return nil, os.NewError("bad name: " + file) } isCgo[file] = true } goFiles := make([]string, 0, len(dirInfo.GoFiles)) for _, file := range dirInfo.GoFiles { if !safeName(file) { return nil, os.NewError("unsafe name: " + file) } if !isCgo[file] { goFiles = append(goFiles, file) } } oFiles := make([]string, 0, len(dirInfo.CFiles)+len(dirInfo.SFiles)) cgoOFiles := make([]string, 0, len(dirInfo.CFiles)) for _, file := range dirInfo.CFiles { if !safeName(file) { return nil, os.NewError("unsafe name: " + file) } // When cgo is in use, C files are compiled with gcc, // otherwise they're compiled with gc. if len(cgoFiles) > 0 { cgoOFiles = append(cgoOFiles, file[:len(file)-2]+".o") } else { oFiles = append(oFiles, file[:len(file)-2]+".$O") } } for _, file := range dirInfo.SFiles { if !safeName(file) { return nil, os.NewError("unsafe name: " + file) } oFiles = append(oFiles, file[:len(file)-2]+".$O") } var imports []string for _, t := range build.Path { imports = append(imports, t.PkgDir()) } var buf bytes.Buffer md := makedata{targ, targDir, "pkg", goFiles, oFiles, cgoFiles, cgoOFiles, imports} if isCmd { md.Type = "cmd" } if err := makefileTemplate.Execute(&buf, &md); err != nil { return nil, err } return buf.Bytes(), nil }
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 }