// loadPackage is like loadImport but is used for command-line arguments, // not for paths found in import statements. In addition to ordinary import paths, // loadPackage accepts pseudo-paths beginning with cmd/ to denote commands // in the Go command directory, as well as paths to those directories. func loadPackage(arg string, stk *importStack) *Package { if build.IsLocalImport(arg) { dir := arg if !filepath.IsAbs(dir) { if abs, err := filepath.Abs(dir); err == nil { // interpret relative to current directory dir = abs } } if sub, ok := hasSubdir(gorootSrc, dir); ok && strings.HasPrefix(sub, "cmd/") && !strings.Contains(sub[4:], "/") { arg = sub } } if strings.HasPrefix(arg, "cmd/") && !strings.Contains(arg[4:], "/") { if p := cmdCache[arg]; p != nil { return p } stk.push(arg) defer stk.pop() bp, err := buildContext.ImportDir(filepath.Join(gorootSrc, arg), 0) bp.ImportPath = arg bp.Goroot = true bp.BinDir = gorootBin if gobin != "" { bp.BinDir = gobin } bp.Root = goroot bp.SrcRoot = gorootSrc p := new(Package) cmdCache[arg] = p p.load(stk, bp, err) if p.Error == nil && p.Name != "main" { p.Error = &PackageError{ ImportStack: stk.copy(), Err: fmt.Sprintf("expected package main but found package %s in %s", p.Name, p.Dir), } } return p } // Wasn't a command; must be a package. // If it is a local import path but names a standard package, // we treat it as if the user specified the standard package. // This lets you run go test ./ioutil in package io and be // referring to io/ioutil rather than a hypothetical import of // "./ioutil". if build.IsLocalImport(arg) { bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } } return loadImport(arg, cwd, stk, nil) }
func (i *Instrumentable) instrumentPatchable(outdir, relpath string, pkg *patch.PatchablePkg, f func(file *patch.PatchableFile) patch.Patches) error { path := "" if build.IsLocalImport(relpath) { path = filepath.Join("locals", relpath) path = strings.Replace(path, "..", "__", -1) } else if relpath != "" { path = filepath.Join("gopath", i.pkg.ImportPath) } if err := os.MkdirAll(filepath.Join(outdir, path), 0755); err != nil { return err } for filename, file := range pkg.Files { if outfile, err := os.Create(filepath.Join(outdir, path, filepath.Base(filename))); err != nil { return err } else { patches := f(file) // TODO(elazar): check the relative path from current location (aka relpath, path), to the import path // (aka v) for _, imp := range file.File.Imports { switch v := imp.Path.Value[1 : len(imp.Path.Value)-1]; { case v == i.pkg.ImportPath: patches = appendNoContradict(patches, patch.Replace(imp.Path, `"."`)) case !i.relevantImport(v): continue case build.IsLocalImport(v): rel, err := filepath.Rel(path, filepath.Join("locals", v)) if err != nil { return err } patches = appendNoContradict(patches, patch.Replace(imp.Path, `"./`+rel+`"`)) default: if v == i.name { v = "" } else { v = filepath.Join("gopath", v) } rel, err := filepath.Rel(path, v) if err != nil { return err } patches = appendNoContradict(patches, patch.Replace(imp.Path, `"./`+rel+`"`)) } } file.FprintPatched(outfile, file.File, patches) if err := outfile.Close(); err != nil { return err } } } return nil }
// Import imports a gc-generated package given its import path, adds the // corresponding package object to the packages map, and returns the object. // Local import paths are interpreted relative to the current working directory. // The packages map must contain all packages already imported. // func Import(packages map[string]*types.Package, path string) (pkg *types.Package, err error) { // package "unsafe" is handled by the type checker if path == "unsafe" { panic(`gcimporter.Import called for package "unsafe"`) } srcDir := "." if build.IsLocalImport(path) { srcDir, err = os.Getwd() if err != nil { return } } filename, id := FindPkg(path, srcDir) if filename == "" { err = fmt.Errorf("can't find import: %s", id) return } // no need to re-import if the package was imported completely before if pkg = packages[id]; pkg != nil && pkg.Complete() { return } // open file f, err := os.Open(filename) if err != nil { return } defer func() { f.Close() if err != nil { // add file name to error err = fmt.Errorf("reading export data: %s: %v", filename, err) } }() var hdr string buf := bufio.NewReader(f) if hdr, err = FindExportData(buf); err != nil { return } switch hdr { case "$$\n": return ImportData(packages, filename, id, buf) case "$$B\n": var data []byte data, err = ioutil.ReadAll(buf) if err == nil { _, pkg, err = BImportData(packages, data, path) return } default: err = fmt.Errorf("unknown export data header: %q", hdr) } return }
func (imp *Importer) importPkg(imports map[string]*types.Package, path string) (*types.Package, error) { if path == "unsafe" { return types.Unsafe, nil } var ( srcDir string err error ) if build.IsLocalImport(path) { srcDir, err = os.Getwd() if err != nil { return nil, err } } bp, err := buildCtx.Import(path, srcDir, build.AllowBinary) if err != nil { return nil, err } if pkg := imports[path]; pkg != nil && pkg.Complete() { return pkg, nil } buf, err := loadExports(bp) if err != nil { return nil, err } _, pkg, err := importer.ImportData(imports, buf) return pkg, err }
func retrieveImport(importMap map[string]bool, path string, dir string) error { build.Default.SrcDirs() pkg, err := build.Import(path, dir, build.AllowBinary) if err != nil { if _, ok := err.(*build.NoGoError); ok { return nil } else { return err } } for _, path := range pkg.Imports { if isStandardImport(path) { continue } if importMap[path] { continue } if !build.IsLocalImport(path) { importMap[path] = true } err := retrieveImport(importMap, path, pkg.Dir) if err != nil { return err } } return nil }
// ImportPaths returns the import paths to use for the given command line. func ImportPaths(args []string) ([]string, []error) { args = ImportPathsNoDotExpansion(args) var out []string var errs []error for _, a := range args { if strings.Contains(a, "...") { if build.IsLocalImport(a) { all, err := AllPackagesInFS(a) out = append(out, all...) if err != nil { errs = append(errs, err) } } else { all, err := AllPackages(a) out = append(out, all...) if err != nil { errs = append(errs, err) } } continue } out = append(out, a) } return out, errs }
func scanDirectory(path, srcDir string) (ret []string, err error) { pkg, err := build.Import(path, srcDir, build.AllowBinary) if err != nil { return ret, err } for _, imp := range pkg.Imports { switch { case isStandardImport(imp): // Ignore standard packages case !build.IsLocalImport(imp): // Add the external package ret = appendPkg(ret, imp) fallthrough default: // Does the recursive walk pkgs, err := scanDirectory(imp, pkg.Dir) if err != nil { return ret, err } ret = appendPkgs(ret, pkgs) } } return ret, err }
func localize(pkg string) string { if build.IsLocalImport(pkg) { // TODO(elazar): check if `import "./a/../a"` is equivalent to "./a" pkg := filepath.Clean(pkg) return filepath.Join(".", "locals", strings.Replace(pkg, ".", "_", -1)) } return filepath.Join("gopath", pkg) }
// relevantImport will determine whether this import should be instrumented as well func (i *Instrumentable) relevantImport(imp string) bool { if i.basepkg == "*" || build.IsLocalImport(imp) { return true } else if i.IsInGopath() || i.basepkg != "" { return filepath.HasPrefix(imp, i.basepkg) || filepath.HasPrefix(i.basepkg, imp) } return false }
// loadPackage recursively resolves path and its imports and if successful // stores those packages in the Context's internal package cache. func loadPackage(c *Context, stack []string, path string) (*Package, error) { if build.IsLocalImport(path) { // sanity check return nil, fmt.Errorf("%q is not a valid import path", path) } if pkg, ok := c.pkgs[path]; ok { // already loaded, just return return pkg, nil } push := func(path string) { stack = append(stack, path) } pop := func(path string) { stack = stack[:len(stack)-1] } onStack := func(path string) bool { for _, p := range stack { if p == path { return true } } return false } p, err := c.Context.Import(path, c.Projectdir(), 0) if err != nil { return nil, err } standard := p.Goroot && p.ImportPath != "" && !strings.Contains(p.ImportPath, ".") push(path) var stale bool for _, i := range p.Imports { if c.shouldignore(i) { continue } if onStack(i) { push(i) return nil, fmt.Errorf("import cycle detected: %s", strings.Join(stack, " -> ")) } pkg, err := loadPackage(c, stack, i) if err != nil { return nil, err } stale = stale || pkg.Stale } pop(path) pkg := Package{ Context: c, Package: p, Standard: standard, } pkg.Stale = stale || isStale(&pkg) c.pkgs[path] = &pkg return &pkg, nil }
// loadPackage recursively resolves path and its imports and if successful // stores those packages in the Context's internal package cache. func (c *Context) loadPackage(stack []string, path string) (*Package, error) { if build.IsLocalImport(path) { // sanity check return nil, fmt.Errorf("%q is not a valid import path", path) } if pkg, ok := c.pkgs[path]; ok { // already loaded, just return return pkg, nil } push := func(path string) { stack = append(stack, path) } pop := func(path string) { stack = stack[:len(stack)-1] } onStack := func(path string) bool { for _, p := range stack { if p == path { return true } } return false } p, err := c.Context.Import(path, c.Projectdir(), 0) if err != nil { return nil, err } push(path) var stale bool for _, i := range p.Imports { if Stdlib[i] { continue } if onStack(i) { push(i) return nil, fmt.Errorf("import cycle detected: %s", strings.Join(stack, " -> ")) } pkg, err := c.loadPackage(stack, i) if err != nil { return nil, err } stale = stale || pkg.Stale } pop(path) pkg := Package{ Context: c, Package: p, } pkg.Stale = stale || isStale(&pkg) Debugf("loadPackage: %v %v (%v)", path, pkg.Stale, pkg.Dir) c.pkgs[path] = &pkg return &pkg, nil }
func main() { _ = go11tag flag.Usage = usage flag.Parse() log.SetFlags(0) args := flag.Args() if len(args) < 1 { usage() } if args[0] == "help" { help(args[1:]) return } // Diagnose common mistake: GOPATH==GOROOT. // This setting is equivalent to not setting GOPATH at all, // which is not what most people want when they do it. if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) } else { for _, p := range filepath.SplitList(gopath) { // Note: using HasPrefix instead of Contains because a ~ can appear // in the middle of directory elements, such as /tmp/git-1.8.2~rc3 // or C:\PROGRA~1. Only ~ as a path prefix has meaning to the shell. if strings.HasPrefix(p, "~") { fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p) os.Exit(2) } if build.IsLocalImport(p) { fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) os.Exit(2) } } } for _, cmd := range commands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { args = args[1:] } else { cmd.Flag.Parse(args[1:]) args = cmd.Flag.Args() } cmd.Run(cmd, args) exit() return } } fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0]) setExitStatus(2) exit() }
func (i *Instrumentable) doimport(pkg string) (*Instrumentable, error) { if build.IsLocalImport(pkg) { return ImportDir(i.basepkg, filepath.Join(i.pkg.Dir, pkg)) } // TODO: A bit hackish r, err := Import(i.basepkg, pkg) if err != nil { return r, err } r.name = i.name return r, nil }
// loadImport scans the directory named by path, which must be an import path, // but possibly a local import path (an absolute file system path or one beginning // with ./ or ../). A local relative path is interpreted relative to srcDir. // It returns a *Package describing the package found in that directory. func loadImport(path string, srcDir string, stk *importStack, importPos []token.Position) *Package { stk.push(path) defer stk.pop() // Determine canonical identifier for this package. // For a local import the identifier is the pseudo-import path // we create from the full directory to the package. // Otherwise it is the usual import path. importPath := path isLocal := build.IsLocalImport(path) if isLocal { importPath = dirToImportPath(filepath.Join(srcDir, path)) } if p := packageCache[importPath]; p != nil { if perr := disallowInternal(srcDir, p, stk); perr != p { return perr } return reusePackage(p, stk) } p := new(Package) p.local = isLocal p.ImportPath = importPath packageCache[importPath] = p // Load package. // Import always returns bp != nil, even if an error occurs, // in order to return partial information. // // TODO: After Go 1, decide when to pass build.AllowBinary here. // See issue 3268 for mistakes to avoid. bp, err := buildContext.Import(path, srcDir, build.ImportComment) bp.ImportPath = importPath if gobin != "" { bp.BinDir = gobin } if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path { err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment) } p.load(stk, bp, err) if p.Error != nil && len(importPos) > 0 { pos := importPos[0] pos.Filename = shortPath(pos.Filename) p.Error.Pos = pos.String() } if perr := disallowInternal(srcDir, p, stk); perr != p { return perr } return p }
func main() { flag.Usage = usage flag.Parse() log.SetFlags(0) args := flag.Args() if len(args) < 1 { usage() } if args[0] == "help" { help(args[1:]) return } // Diagnose common mistake: GOPATH==GOROOT. // This setting is equivalent to not setting GOPATH at all, // which is not what most people want when they do it. if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() { fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath) } else { for _, p := range filepath.SplitList(gopath) { if strings.Contains(p, "~") && runtime.GOOS != "windows" { fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\n", p) os.Exit(2) } if build.IsLocalImport(p) { fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) os.Exit(2) } } } for _, cmd := range commands { if cmd.Name() == args[0] && cmd.Run != nil { cmd.Flag.Usage = func() { cmd.Usage() } if cmd.CustomFlags { args = args[1:] } else { cmd.Flag.Parse(args[1:]) args = cmd.Flag.Args() } cmd.Run(cmd, args) exit() return } } fmt.Fprintf(os.Stderr, "go: unknown subcommand %q\nRun 'go help' for usage.\n", args[0]) setExitStatus(2) exit() }
// RecursiveImports recursively imports a set of Go targets, returning a map from each package to // its path. func RecursiveImports(args ...string) map[string]string { imports := map[string]string{} for _, arg := range args { var p *build.Package if build.IsLocalImport(arg) { p, _ = build.Default.ImportDir(arg, build.AllowBinary) } else { p, _ = build.Default.Import(arg, "", build.AllowBinary) } recursiveImport(p, arg, imports) } return imports }
// relevantImport will determine whether this import should be instrumented as well func (i *Instrumentable) relevantImport(imp string) bool { switch { case imp == "C": return false case i.gorootPkgs[imp] && !i.InstrumentGoroot: return false case i.basepkg == "*" || build.IsLocalImport(imp): return true case i.IsInGopath() || i.basepkg != "": return filepath.HasPrefix(imp, i.basepkg) || filepath.HasPrefix(i.basepkg, imp) } return false }
/* This is how godoc does it: // Determine paths. // // If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc, // we need to map that path somewhere in the fs name space so that routines // like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target" // for this. That is, if we get passed a directory like the above, we map that // directory so that getPageInfo sees it as /target. const target = "/target" const cmdPrefix = "cmd/" path := flag.Arg(0) var forceCmd bool var abspath, relpath string if filepath.IsAbs(path) { fs.Bind(target, OS(path), "/", bindReplace) abspath = target } else if build.IsLocalImport(path) { cwd, _ := os.Getwd() // ignore errors path = filepath.Join(cwd, path) fs.Bind(target, OS(path), "/", bindReplace) abspath = target } else if strings.HasPrefix(path, cmdPrefix) { path = path[len(cmdPrefix):] forceCmd = true } else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" { fs.Bind(target, OS(bp.Dir), "/", bindReplace) abspath = target relpath = bp.ImportPath } else { abspath = pathpkg.Join(pkgHandler.fsRoot, path) } if relpath == "" { relpath = abspath } */ func buildImport(target string) (*build.Package, error) { if filepath.IsAbs(target) { return build.Default.ImportDir(target, build.FindOnly) } else if build.IsLocalImport(target) { base, _ := os.Getwd() path := filepath.Join(base, target) return build.Default.ImportDir(path, build.FindOnly) } else if pkg, _ := build.Default.Import(target, "", build.FindOnly); pkg.Dir != "" && pkg.ImportPath != "" { return pkg, nil } path, _ := filepath.Abs(target) // Even if there is an error, still try? return build.Default.ImportDir(path, build.FindOnly) }
// loadPackage is like loadImport but is used for command-line arguments, // not for paths found in import statements. In addition to ordinary import paths, // loadPackage accepts pseudo-paths beginning with cmd/ to denote commands // in the Go command directory, as well as paths to those directories. func loadPackage(arg string, stk *importStack) *Package { // If it is a local import path but names a standard package, // we treat it as if the user specified the standard package. // This lets you run go test ./ioutil in package io and be // referring to io/ioutil rather than a hypothetical import of // "./ioutil". if build.IsLocalImport(arg) { bp, _ := buildContext.ImportDir(filepath.Join(cwd, arg), build.FindOnly) if bp.ImportPath != "" && bp.ImportPath != "." { arg = bp.ImportPath } } return loadImport(arg, cwd, nil, stk, nil) }
func egc(ppath string) error { srcDir := "" if build.IsLocalImport(ppath) { var err error if srcDir, err = os.Getwd(); err != nil { return err } } bp, err := buildCtx.Import(ppath, srcDir, 0) if err != nil { return err } return compile(bp) }
// FindPkg returns the filename and unique package id for an import // path based on package information provided by build.Import (using // the build.Default build.Context). A relative srcDir is interpreted // relative to the current working directory. // If no file was found, an empty filename is returned. // func FindPkg(path, srcDir string) (filename, id string) { if path == "" { return } var noext string switch { default: // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" // Don't require the source files to be present. if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 srcDir = abs } bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) if bp.PkgObj == "" { return } noext = strings.TrimSuffix(bp.PkgObj, ".a") id = bp.ImportPath case build.IsLocalImport(path): // "./x" -> "/this/directory/x.ext", "/this/directory/x" noext = filepath.Join(srcDir, path) id = noext case filepath.IsAbs(path): // for completeness only - go/build.Import // does not support absolute imports // "/x" -> "/x.ext", "/x" noext = path id = path } if false { // for debugging if path != id { fmt.Printf("%s -> %s\n", path, id) } } // try extensions for _, ext := range pkgExts { filename = noext + ext if f, err := os.Stat(filename); err == nil && !f.IsDir() { return } } filename = "" // not found return }
func (i *Instrumentable) instrumentTo(processed map[string]bool, istest bool, outdir, relpath string, f func(file *patch.PatchableFile) patch.Patches) error { if processed[relpath] { return nil } processed[i.pkg.ImportPath] = true imps := i.pkg.Imports if istest { imps = append(imps, i.pkg.TestImports...) imps = append(imps, i.pkg.XTestImports...) } for _, imp := range imps { if i.relevantImport(imp) { pkg, err := i.doimport(imp) if err != nil { return err } if build.IsLocalImport(imp) { imp = "./" + filepath.Join(relpath, imp) } if err := pkg.instrumentTo(processed, false, outdir, imp, f); err != nil { return err } } } if !istest { pkg := patch.NewPatchablePkg() if err := pkg.ParseFiles(i.Files()...); err != nil { return err } if err := i.instrumentPatchable(outdir, relpath, pkg, f); err != nil { return err } } else { pkg := patch.NewPatchablePkg() if err := pkg.ParseFiles(i.TestFiles()...); err != nil { return err } if err := i.instrumentPatchable(outdir, relpath, pkg, f); err != nil { return err } pkg = patch.NewPatchablePkg() if err := pkg.ParseFiles(i.XTestFiles()...); err != nil { return err } if err := i.instrumentPatchable(outdir, relpath, pkg, f); err != nil { return err } } return nil }
// Start watches changes in directories of compiled packages. If there // is any change, enters in the directory of the command to compile, and it is // sent a signal Restart. // // The argument cmdPath is the import path of source code for the command to run // by gostart. // Whether the logger is nil then it is created one by default. The logger // is always returned. // Each path in pkgTowatch has to be an import or filesystem path found in // $GOROOT or $GOPATH. func Start(cmdPath string, l *log.Logger, pkgTowatch ...string) (*pkgWatcher, error) { srv := new(pkgWatcher) if l == nil { srv.Log = log.New(os.Stdout, LOG_PREFIX, log.LstdFlags) } // Command if cmdPath == "" || build.IsLocalImport(cmdPath) { return srv, errors.New("FAIL! import path of command can not be local") } else { pkg, err := build.Import(cmdPath, build.Default.GOPATH, 0) if err != nil { return srv, fmt.Errorf("FAIL! at getting command directory: %s", err) } if !pkg.IsCommand() { return srv, fmt.Errorf("FAIL! no command: %s", cmdPath) } cmdPath = pkg.Dir } // Packages pkgTowatch, pkgFiles, err := checkPkgPath(pkgTowatch, srv.Log) if err != nil { return srv, err } w, err := sysWatcher(cmdPath, pkgTowatch, srv.Log) if err != nil { return srv, err } if USE_KERNEL { go w.watcher(pkgTowatch) } else { if err = w.watcher(pkgFiles); err != nil { return srv, err } } if *Verbose { srv.Log.Print("Start " + _WATCHER_NAME + " watcher for compiled packages") for _, p := range pkgTowatch { srv.Log.Printf("Watching %q", p) } } return w, nil }
func getChildPkgs(ctx *cli.Context, cpath string, ppkg *doc.Pkg, cachePkgs map[string]*doc.Pkg, isTest bool) error { log.Trace("Current Path: 1 %s", cpath) pkgs, err := getGopmPkgs(cpath, isTest) if err != nil { return errors.New("Fail to get gopmfile deps: " + err.Error()) } for name, pkg := range pkgs { pkg.RootPath = doc.GetProjectPath(pkg.ImportPath) if !pkgInCache(pkg.RootPath, cachePkgs) { var newPath string if !build.IsLocalImport(name) && pkg.Type != doc.LOCAL { suf := versionSuffix(pkg.Value) pkgPath := strings.Replace( pkg.ImportPath, pkg.RootPath, pkg.RootPath+suf, 1) newPath = filepath.Join(installRepoPath, pkgPath) if len(suf) == 0 && !ctx.Bool("remote") && com.IsDir(filepath.Join(installGopath, pkgPath)) { newPath = filepath.Join(installGopath, pkgPath) } if pkgName != "" && strings.HasPrefix(pkg.ImportPath, pkgName) { newPath = filepath.Join(curPath, strings.TrimPrefix(pkg.ImportPath, pkgName)) } else { if !com.IsExist(newPath) || ctx.Bool("update") { node := doc.NewNode(pkg.ImportPath, pkg.ImportPath, pkg.Type, pkg.Value, true) nodes := []*doc.Node{node} downloadPackages(ctx, nodes) // TODO: Should handler download failed } } } else { if pkg.Type == doc.LOCAL { newPath, err = filepath.Abs(pkg.Value) } else { newPath, err = filepath.Abs(name) } if err != nil { return err } } cachePkgs[pkg.RootPath] = pkg err = getChildPkgs(ctx, newPath, pkg, cachePkgs, false) if err != nil { return err } } } return nil }
func (r *Runner) makePkgTree() (*pkg, error) { nodes := make(map[string]*pkg) var walkImports func(*build.Package) (*pkg, error) walkImports = func(p *build.Package) (*pkg, error) { root, err := r.makePkg(p) if err != nil { return nil, err } for _, path := range p.Imports { if stdLib[path] { continue } idx := path if build.IsLocalImport(path) { idx = filepath.Clean(filepath.Join(p.Dir, path)) } if nodes[idx] == nil { pp, err := build.Import(path, p.Dir, 0) if err != nil { return nil, err } if pp.Goroot { stdLib[path] = true continue } node, err := walkImports(pp) if err != nil { return nil, err } nodes[idx] = node } root.Imports[path] = nodes[idx] } return root, nil } p, err := build.ImportDir(r.Dir, 0) if err != nil { return nil, err } return walkImports(p) }
// importPaths returns the import paths to use for the given command line. // $GOROOT/src/cmd/main.go:366 func importPaths(args []string) []string { args = importPathsNoDotExpansion(args) var out []string for _, a := range args { if strings.Contains(a, "...") { if build.IsLocalImport(a) { out = append(out, allPackagesInFS(a)...) } else { out = append(out, allPackages(a)...) } continue } out = append(out, a) } return out }
// Import imports a gc-generated package given its import path, adds the // corresponding package object to the imports map, and returns the object. // Local import paths are interpreted relative to the current working directory. // The imports map must contains all packages already imported. // func Import(imports map[string]*types.Package, path string) (pkg *types.Package, err error) { // package "unsafe" is handled by the type checker if path == "unsafe" { panic(`gcimporter.Import called for package "unsafe"`) } srcDir := "." if build.IsLocalImport(path) { srcDir, err = os.Getwd() if err != nil { return } } filename, id := FindPkg(path, srcDir) if filename == "" { err = fmt.Errorf("can't find import: %s", id) return } // no need to re-import if the package was imported completely before if pkg = imports[id]; pkg != nil && pkg.Complete() { return } // open file f, err := os.Open(filename) if err != nil { return } defer func() { f.Close() if err != nil { // add file name to error err = fmt.Errorf("reading export data: %s: %v", filename, err) } }() buf := bufio.NewReader(f) if err = FindExportData(buf); err != nil { return } pkg, err = ImportData(imports, filename, id, buf) return }
// paths determines the paths to use. // // If we are passed an operating system path like . or ./foo or /foo/bar or c:\mysrc, // we need to map that path somewhere in the fs name space so that routines // like getPageInfo will see it. We use the arbitrarily-chosen virtual path "/target" // for this. That is, if we get passed a directory like the above, we map that // directory so that getPageInfo sees it as /target. // Returns the absolute and relative paths. func paths(fs vfs.NameSpace, pres *Presentation, path string) (string, string) { if filepath.IsAbs(path) { fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace) return target, target } if build.IsLocalImport(path) { cwd, _ := os.Getwd() // ignore errors path = filepath.Join(cwd, path) fs.Bind(target, vfs.OS(path), "/", vfs.BindReplace) return target, target } if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" { fs.Bind(target, vfs.OS(bp.Dir), "/", vfs.BindReplace) return target, bp.ImportPath } return pathpkg.Join(pres.PkgFSRoot(), path), path }
// GcImport imports a gc-generated package given its import path, adds the // corresponding package object to the imports map, and returns the object. // Local import paths are interpreted relative to the current working directory. // The imports map must contains all packages already imported. // GcImport satisfies the ast.Importer signature. // func GcImport(imports map[string]*Package, path string) (pkg *Package, err error) { if path == "unsafe" { return Unsafe, nil } srcDir := "." if build.IsLocalImport(path) { srcDir, err = os.Getwd() if err != nil { return } } filename, id := FindPkg(path, srcDir) if filename == "" { err = errors.New("can't find import: " + id) return } // no need to re-import if the package was imported completely before if pkg = imports[id]; pkg != nil && pkg.Complete { return } // open file f, err := os.Open(filename) if err != nil { return } defer func() { f.Close() if err != nil { // add file name to error err = fmt.Errorf("reading export data: %s: %v", filename, err) } }() buf := bufio.NewReader(f) if err = FindGcExportData(buf); err != nil { return } pkg, err = GcImportData(imports, filename, id, buf) return }
func main() { flag.Usage = usage flag.Parse() if flag.NArg() == 0 { report("no package name, path, or file provided") } imp := tryImports if *source != "" { imp = lookup(*source) if imp == nil { report("source (-s argument) must be one of: " + strings.Join(sources, ", ")) } } for _, arg := range flag.Args() { path, name := splitPathIdent(arg) logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name) // generate possible package path prefixes // (at the moment we do this for each argument - should probably cache the generated prefixes) prefixes := make(chan string) go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path)) // import package pkg, err := tryPrefixes(packages, prefixes, path, imp) if err != nil { logf("\t=> ignoring %q: %s\n", path, err) continue } // filter objects if needed var filter func(types.Object) bool if name != "" { filter = func(obj types.Object) bool { // TODO(gri) perhaps use regular expression matching here? return obj.Name() == name } } // print contents print(os.Stdout, pkg, filter) } }