Esempio n. 1
0
func (w *PkgWalker) Import(parentDir string, name string, conf *PkgConfig) (pkg *types.Package, err error) {
	defer func() {
		err := recover()
		if err != nil && typesVerbose {
			log.Println(err)
		}
	}()

	if strings.HasPrefix(name, ".") && parentDir != "" {
		name = filepath.Join(parentDir, name)
	}
	pkg = w.imported[name]
	if pkg != nil {
		//		if pkg == &w.importing {
		//			return nil, fmt.Errorf("cycle importing package %q", name)
		//		}
		return pkg, nil
	}

	if typesVerbose {
		log.Println("parser pkg", name)
	}

	bp, err := w.importPath(name, 0)
	if err != nil {
		return nil, err
	}

	checkName := name

	if bp.ImportPath == "." {
		checkName = bp.Name
	} else {
		checkName = bp.ImportPath
	}

	if w.importingName[checkName] {
		return nil, fmt.Errorf("cycle importing package %q", name)
	}

	w.importingName[checkName] = true

	//	if err != nil {
	//		return nil, err
	//		//if _, nogo := err.(*build.NoGoError); nogo {
	//		//	return
	//		//}
	//		//return
	//		//log.Fatalf("pkg %q, dir %q: ScanDir: %v", name, info.Dir, err)
	//	}

	filenames := append(append([]string{}, bp.GoFiles...), bp.CgoFiles...)
	if conf.WithTestFiles {
		filenames = append(filenames, bp.TestGoFiles...)
	}

	if name == "runtime" {
		n := fmt.Sprintf("zgoos_%s.go", w.context.GOOS)
		if !contains(filenames, n) {
			filenames = append(filenames, n)
		}

		n = fmt.Sprintf("zgoarch_%s.go", w.context.GOARCH)
		if !contains(filenames, n) {
			filenames = append(filenames, n)
		}
	}

	parserFiles := func(filenames []string, cursor *FileCursor, xtest bool) (files []*ast.File) {
		for _, file := range filenames {
			var f *ast.File
			if cursor != nil && cursor.fileName == file {
				f, err = w.parseFile(bp.Dir, file, cursor.src)
				cursor.pos = token.Pos(w.fset.File(f.Pos()).Base()) + token.Pos(cursor.cursorPos)
				cursor.fileDir = bp.Dir
				cursor.xtest = xtest
			} else {
				f, err = w.parseFile(bp.Dir, file, nil)
			}
			if err != nil && typesVerbose {
				log.Printf("error parsing package %s: %s\n", name, err)
			}
			files = append(files, f)
		}
		return
	}
	files := parserFiles(filenames, conf.Cursor, false)
	xfiles := parserFiles(bp.XTestGoFiles, conf.Cursor, true)

	typesConf := types.Config{
		IgnoreFuncBodies: conf.IgnoreFuncBodies,
		FakeImportC:      true,
		Packages:         w.gcimporter,
		Import: func(imports map[string]*types.Package, name string) (pkg *types.Package, err error) {
			if pkg != nil {
				return pkg, nil
			}
			if conf.AllowBinary && w.isBinaryPkg(name) {
				pkg = w.gcimporter[name]
				if pkg != nil && pkg.Complete() {
					return
				}
				pkg, err = gcimporter.Import(imports, name)
				if pkg != nil && pkg.Complete() {
					w.gcimporter[name] = pkg
					return
				}
			}
			return w.Import(bp.Dir, name, &PkgConfig{IgnoreFuncBodies: true, AllowBinary: true, WithTestFiles: false})
		},
		Error: func(err error) {
			if typesVerbose {
				log.Println(err)
			}
		},
	}
	if pkg == nil {
		pkg, err = typesConf.Check(checkName, w.fset, files, conf.Info)
		conf.Pkg = pkg
	}
	w.importingName[checkName] = false
	w.imported[name] = pkg

	if len(xfiles) > 0 {
		xpkg, _ := typesConf.Check(checkName+"_test", w.fset, xfiles, conf.XInfo)
		w.imported[checkName+"_test"] = xpkg
		conf.XPkg = xpkg
	}
	return
}
Esempio n. 2
0
func (imp *Importer) realImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// types.Importer does not seem to be designed for recursive
	// parsing like we're doing here. Specifically, each nested import
	// will maintain its own imports map. This will lead to duplicate
	// imports and in turn packages, which will lead to funny errors
	// such as "cannot pass argument ip (variable of type net.IP) to
	// variable of type net.IP"
	//
	// To work around this, we keep a global imports map, allImports,
	// to which we add all nested imports, and which we use as the
	// cache, instead of imports.
	//
	// Since all nested imports will also use this importer, there
	// should be no way to end up with duplicate imports.

	// We first try to use GcImport directly. This has the downside of
	// using possibly out-of-date packages, but it has the upside of
	// not having to parse most of the Go standard library.

	imported := func(pkg *types.Package) {
		// We don't use imports, but per API we have to add the package.
		imports[pkg.Path()] = pkg
		imp.Imports[pkg.Path()] = pkg
	}

	buildPkg, buildErr := build.Import(path, ".", 0)
	// If we found no build dir, assume we're dealing with installed
	// but no source. If we found a build dir, only use GcImport if
	// it's in GOROOT. This way we always use up-to-date code for
	// normal packages but avoid parsing the standard library.
	if (buildErr == nil && buildPkg.Goroot) || buildErr != nil {
		pkg, err = gcimporter.Import(imp.Imports, path)
		if err == nil {
			imported(pkg)
			return pkg, nil
		}
	}

	// See if we already imported this package
	if pkg = imp.Imports[path]; pkg != nil && pkg.Complete() {
		return pkg, nil
	}

	// allImports failed, try to use go/build
	if buildErr != nil {
		return nil, fmt.Errorf("build.Import failed: %s", buildErr)
	}

	// TODO check if the .a file is up to date and use it instead
	fileSet := token.NewFileSet()

	isGoFile := func(d os.FileInfo) bool {
		allFiles := make([]string, 0, len(buildPkg.GoFiles)+len(buildPkg.CgoFiles))
		allFiles = append(allFiles, buildPkg.GoFiles...)
		allFiles = append(allFiles, buildPkg.CgoFiles...)

		for _, file := range allFiles {
			if file == d.Name() {
				return true
			}
		}
		return false
	}
	pkgs, err := parser.ParseDir(fileSet, buildPkg.Dir, isGoFile, 0)
	if err != nil {
		return nil, err
	}

	delete(pkgs, "documentation")
	var astPkg *ast.Package
	var name string
	for name, astPkg = range pkgs {
		// Use the first non-main package, or the only package we
		// found.
		//
		// NOTE(dh) I can't think of a reason why there should be
		// multiple packages in a single directory, but ParseDir
		// accommodates for that possibility.
		if len(pkgs) == 1 || name != "main" {
			break
		}
	}

	if astPkg == nil {
		return nil, fmt.Errorf("can't find import: %s", name)
	}

	var ff []*ast.File
	for _, f := range astPkg.Files {
		ff = append(ff, f)
	}

	if imp.cycleSeen[path] {
		return nil, fmt.Errorf("import cycle %s -> %s", strings.Join(imp.cyclesStack, " -> "), path)
	}
	imp.cycleSeen[path] = true
	imp.cyclesStack = append(imp.cyclesStack, path)

	context := types.Config{
		Import: imp.realImport,
	}

	pkg, err = context.Check(name, fileSet, ff, nil)
	if err != nil {
		// As a special case, if type checking failed due cgo, try
		// again by using GcImport. That way we can extract all
		// required type information, but we risk importing an
		// outdated version.
		if imp.Config.UseGcFallback && strings.Contains(err.Error(), `cannot find package "C" in`) {
			gcPkg, gcErr := gcimporter.Import(imp.Imports, path)
			if gcErr == nil {
				imported(gcPkg)
				imp.Fallbacks = append(imp.Fallbacks, path)
				return gcPkg, nil
			}
		}
		return pkg, err
	}

	imports[path] = pkg
	imp.Imports[path] = pkg
	return pkg, nil
}