func TestTool(t *testing.T) { if e := Remotize(Value2Spec("github.com/josvazg/remotize/tool", new(ToolTester))); e != nil { t.Fatal(e) } dir, e := build.ScanDir(".", false) if e != nil { t.Fatal(e) } dir.GoFiles = append(dir.GoFiles, "tool_test.go") //fmt.Println("dir", dir) tree, pkg, e := build.FindTree(".") if e != nil { t.Fatal(e) } //fmt.Println("tree", tree, "pkg", pkg) script, e := build.Build(tree, pkg, dir) if e != nil { t.Fatal(e) } //fmt.Println("script", script) e = script.Run() if e != nil { t.Fatal(e) } }
// installPackage installs the specified package and its dependencies. func installPackage(pkg, parent string, tree *build.Tree, retry bool) (installErr error) { printf("%s: install\n", pkg) // Read package information. dir := filepath.Join(tree.SrcDir(), filepath.FromSlash(pkg)) dirInfo, err := build.ScanDir(dir) if err != nil { return &PackageError{pkg, err} } // We reserve package main to identify commands. if parent != "" && dirInfo.Package == "main" { return &PackageError{pkg, fmt.Errorf("found only package main in %s; cannot import", dir)} } // Run gofix if we fail to build and -fix is set. defer func() { if retry || installErr == nil || !*doGofix { return } if e, ok := (installErr).(*DependencyError); ok { // If this package failed to build due to a // DependencyError, only attempt to gofix it if its // dependency failed for some reason other than a // DependencyError or BuildError. // (If a dep or one of its deps doesn't build there's // no way that gofixing this package can help.) switch e.err.(type) { case *DependencyError: return case *BuildError: return } } gofix(pkg, dir, dirInfo) installErr = installPackage(pkg, parent, tree, true) // retry }() // Install prerequisites. for _, p := range dirInfo.Imports { if p == "C" { continue } if err := install(p, pkg); err != nil { return &DependencyError{pkg, err} } } // Install this package. err = domake(dir, pkg, tree, dirInfo.IsCommand()) if err != nil { return &BuildError{pkg, err} } return nil }
func (w *Walker) WalkPackage(name, dir string) { log.Printf("package %s", name) pop := w.pushScope("pkg " + name) defer pop() info, err := build.ScanDir(dir) if err != nil { log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) } apkg := &ast.Package{ Files: make(map[string]*ast.File), } files := append(append([]string{}, info.GoFiles...), info.CgoFiles...) for _, file := range files { f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0) if err != nil { log.Fatalf("error parsing package %s, file %s: %v", name, file, err) } apkg.Files[file] = f } w.curPackageName = name w.curPackage = apkg w.prevConstType = map[string]string{} for name, afile := range apkg.Files { w.walkFile(filepath.Join(dir, name), afile) } // Now that we're done walking types, vars and consts // in the *ast.Package, use go/doc to do the rest // (functions and methods). This is done here because // go/doc is destructive. We can't use the // *ast.Package after this. dpkg := doc.New(apkg, name, 0) for _, t := range dpkg.Types { // Move funcs up to the top-level, not hiding in the Types. dpkg.Funcs = append(dpkg.Funcs, t.Funcs...) for _, m := range t.Methods { w.walkFuncDecl(m.Decl) } } for _, f := range dpkg.Funcs { w.walkFuncDecl(f.Decl) } }
// allPackagesInFS is like allPackages but is passed a pattern // beginning ./ or ../, meaning it should scan the tree rooted // at the given directory. There are ... in the pattern too. func allPackagesInFS(pattern string) []string { // Find directory to begin the scan. // Could be smarter but this one optimization // is enough for now, since ... is usually at the // end of a path. i := strings.Index(pattern, "...") dir, _ := path.Split(pattern[:i]) // pattern begins with ./ or ../. // path.Clean will discard the ./ but not the ../. // We need to preserve the ./ for pattern matching // and in the returned import paths. prefix := "" if strings.HasPrefix(pattern, "./") { prefix = "./" } match := matchPattern(pattern) var pkgs []string filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() { return nil } // Avoid .foo, _foo, and testdata directory trees. _, elem := filepath.Split(path) if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := prefix + filepath.ToSlash(path) if !match(name) { return nil } if _, err = build.ScanDir(path); err != nil { return nil } pkgs = append(pkgs, name) return nil }) if len(pkgs) == 0 { fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) } return pkgs }
// getTestFileNames gets the set of files we're looking at. // If gotest has no arguments, it scans the current directory // for test files. func getTestFileNames() { names := fileNames if len(names) == 0 { info, err := build.ScanDir(".", true) if err != nil { Fatalf("scanning directory: %v", err) } names = append(info.TestGoFiles, info.XTestGoFiles...) sort.Strings(names) if len(names) == 0 { Fatalf("no test files found in current directory") } } for _, n := range names { fd, err := os.Open(n) if err != nil { Fatalf("%s: %s", n, err) } f := &File{name: n, file: fd} files = append(files, f) } }
// WalkPackage walks all files in package `name'. // WalkPackage does nothing if the package has already been loaded. func (w *Walker) WalkPackage(name string) { switch w.packageState[name] { case loading: log.Fatalf("import cycle loading package %q?", name) case loaded: return } w.packageState[name] = loading defer func() { w.packageState[name] = loaded }() dir := filepath.Join(w.tree.SrcDir(), filepath.FromSlash(name)) var info *build.DirInfo var err error if ctx := w.context; ctx != nil { info, err = ctx.ScanDir(dir) } else { info, err = build.ScanDir(dir) } if err != nil { if strings.Contains(err.Error(), "no Go source files") { return } log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, dir, err) } apkg := &ast.Package{ Files: make(map[string]*ast.File), } files := append(append([]string{}, info.GoFiles...), info.CgoFiles...) for _, file := range files { f, err := parser.ParseFile(w.fset, filepath.Join(dir, file), nil, 0) if err != nil { log.Fatalf("error parsing package %s, file %s: %v", name, file, err) } apkg.Files[file] = f for _, dep := range fileDeps(f) { w.WalkPackage(dep) } } log.Printf("package %s", name) pop := w.pushScope("pkg " + name) defer pop() w.curPackageName = name w.curPackage = apkg w.constDep = map[string]string{} for _, afile := range apkg.Files { w.recordTypes(afile) } // Register all function declarations first. for _, afile := range apkg.Files { for _, di := range afile.Decls { if d, ok := di.(*ast.FuncDecl); ok { w.peekFuncDecl(d) } } } for _, afile := range apkg.Files { w.walkFile(afile) } w.resolveConstantDeps() // Now that we're done walking types, vars and consts // in the *ast.Package, use go/doc to do the rest // (functions and methods). This is done here because // go/doc is destructive. We can't use the // *ast.Package after this. dpkg := doc.New(apkg, name, doc.AllMethods) for _, t := range dpkg.Types { // Move funcs up to the top-level, not hiding in the Types. dpkg.Funcs = append(dpkg.Funcs, t.Funcs...) for _, m := range t.Methods { w.walkFuncDecl(m.Decl) } } for _, f := range dpkg.Funcs { w.walkFuncDecl(f.Decl) } }
// 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 }
// install installs the package named by path, which is needed by parent. func install(pkg, parent string) { // Make sure we're not already trying to install pkg. switch visit[pkg] { case done: return case visiting: fmt.Fprintf(os.Stderr, "%s: package dependency cycle\n", argv0) printDeps(parent) fmt.Fprintf(os.Stderr, "\t%s\n", pkg) os.Exit(2) } parents[pkg] = parent visit[pkg] = visiting defer func() { visit[pkg] = done }() // Don't allow trailing '/' if _, f := filepath.Split(pkg); f == "" { errorf("%s should not have trailing '/'\n", pkg) return } // Check whether package is local or remote. // If remote, download or update it. tree, pkg, err := build.FindTree(pkg) // Don't build the standard library. if err == nil && tree.Goroot && isStandardPath(pkg) { if parent == "" { errorf("%s: can not goinstall the standard library\n", pkg) } else { printf("%s: skipping standard library\n", pkg) } return } // Download remote packages if not found or forced with -u flag. remote, public := isRemote(pkg), false if remote { if err == build.ErrNotFound || (err == nil && *update) { // Download remote package. printf("%s: download\n", pkg) public, err = download(pkg, tree.SrcDir()) } else { // Test if this is a public repository // (for reporting to dashboard). m, _ := findPublicRepo(pkg) public = m != nil } } if err != nil { terrorf(tree, "%s: %v\n", pkg, err) return } dir := filepath.Join(tree.SrcDir(), pkg) // Install prerequisites. dirInfo, err := build.ScanDir(dir, parent == "") if err != nil { terrorf(tree, "%s: %v\n", pkg, err) return } if len(dirInfo.GoFiles)+len(dirInfo.CgoFiles) == 0 { terrorf(tree, "%s: package has no files\n", pkg) return } for _, p := range dirInfo.Imports { if p != "C" { install(p, pkg) } } if errors { return } // Install this package. if *useMake { err := domake(dir, pkg, tree, dirInfo.IsCommand()) if err != nil { terrorf(tree, "%s: install: %v\n", pkg, err) return } } else { script, err := build.Build(tree, pkg, dirInfo) if err != nil { terrorf(tree, "%s: install: %v\n", pkg, err) return } if *nuke { printf("%s: nuke\n", pkg) script.Nuke() } else if *clean { printf("%s: clean\n", pkg) script.Clean() } if *doInstall { if script.Stale() { printf("%s: install\n", pkg) if err := script.Run(); err != nil { terrorf(tree, "%s: install: %v\n", pkg, err) return } } else { printf("%s: up-to-date\n", pkg) } } } if remote { // mark package as installed in goinstall.log logged := logPackage(pkg, tree) // report installation to the dashboard if this is the first // install from a public repository. if logged && public { maybeReportToDashboard(pkg) } } }
// allPackages returns all the packages that can be found // under the $GOPATH directories and $GOROOT matching pattern. // The pattern is either "all" (all packages), "std" (standard packages) // or a path including "...". func allPackages(pattern string) []string { match := func(string) bool { return true } if pattern != "all" && pattern != "std" { match = matchPattern(pattern) } have := map[string]bool{ "builtin": true, // ignore pseudo-package that exists only for documentation } if !buildContext.CgoEnabled { have["runtime/cgo"] = true // ignore during walk } var pkgs []string // Commands goroot := build.Path[0].Path cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator) filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() { return nil } name := path[len(cmd):] // Commands are all in cmd/, not in subdirectories. if strings.Contains(name, string(filepath.Separator)) { return filepath.SkipDir } _, err = build.ScanDir(path) if err != nil { return nil } // We use, e.g., cmd/gofmt as the pseudo import path for gofmt. name = "cmd/" + name if !have[name] { have[name] = true if match(name) { pkgs = append(pkgs, name) } } return nil }) for _, t := range build.Path { if pattern == "std" && !t.Goroot { continue } src := t.SrcDir() + string(filepath.Separator) filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { if err != nil || !fi.IsDir() { return nil } // Avoid .foo, _foo, and testdata directory trees. _, elem := filepath.Split(path) if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { return filepath.SkipDir } name := filepath.ToSlash(path[len(src):]) if pattern == "std" && strings.Contains(name, ".") { return filepath.SkipDir } if have[name] { return nil } have[name] = true _, err = build.ScanDir(path) if err != nil { return nil } if match(name) { pkgs = append(pkgs, name) } // Avoid go/build test data. // TODO: Move it into a testdata directory. if path == filepath.Join(build.Path[0].SrcDir(), "go/build") { return filepath.SkipDir } return nil }) } if len(pkgs) == 0 { fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) } return pkgs }