// FindVcs determines the version control information given a package dir and // lowest root dir. func FindVcs(root, packageDir string) (info *VcsInfo, err error) { path := packageDir for i := 0; i <= looplimit; i++ { for _, vcs := range vcsRegistry { info, err = vcs.Find(path) if err != nil { return nil, err } if info != nil { return info, nil } } nextPath := filepath.Clean(filepath.Join(path, "..")) // Check for root. if nextPath == path { return nil, nil } if pathos.FileHasPrefix(nextPath, root) == false { return nil, nil } path = nextPath } panic("loop limit") }
func (ctx *Context) modifyRemove(pkg *Package) error { if len(pkg.Dir) == 0 { return nil } // Protect non-project paths from being removed. if pathos.FileHasPrefix(pkg.Dir, ctx.RootDir) == false { return nil } if pkg.Status == StatusLocal { return nil } if pkg.Status == StatusProgram && pkg.inVendor == false { return nil } ctx.Operation = append(ctx.Operation, &Operation{ Pkg: pkg, Src: pkg.Dir, Dest: "", }) // Update vendor file with correct Local field. vp := ctx.VendorFilePackagePath(pkg.Canonical) if vp != nil { vp.Remove = true } mvSet := make(map[*Package]struct{}, 3) ctx.makeSet(pkg, mvSet) for r := range mvSet { dprintf("RULE: %s -> %s\n", r.Local, r.Canonical) ctx.RewriteRule[r.Local] = r.Canonical } return nil }
// findImportDir finds the absolute directory. If rel is empty vendor folders // are not looked in. func (ctx *Context) findImportDir(relative, importPath string) (dir, gopath string, err error) { if importPath == "builtin" || importPath == "unsafe" || importPath == "C" { return filepath.Join(ctx.Goroot, importPath), ctx.Goroot, nil } if len(relative) != 0 { rel := relative for { look := filepath.Join(rel, "vendor", importPath) nextRel := filepath.Join(rel, "..") if rel == nextRel { break } rel = nextRel fi, err := os.Stat(look) if os.IsNotExist(err) { continue } if err != nil { continue } if fi.IsDir() == false { continue } for _, gopath = range ctx.GopathList { if pathos.FileHasPrefix(look, gopath) { hasGo, err := hasGoFileInFolder(look) if err != nil { return "", "", err } if hasGo { return look, gopath, nil } } } } } for _, gopath = range ctx.GopathList { dir := filepath.Join(gopath, importPath) fi, err := os.Stat(dir) if os.IsNotExist(err) { continue } if fi.IsDir() == false { continue } hasGo, err := hasGoFileInFolder(dir) if err != nil { return "", "", err } if hasGo { return dir, gopath, nil } return "", "", ErrNotInGOPATH{fmt.Sprintf("Import: %q relative: %q", importPath, relative)} } return "", "", ErrNotInGOPATH{importPath} }
// findImportPath takes a absolute directory and returns the import path and go path. func (ctx *Context) findImportPath(dir string) (importPath, gopath string, err error) { for _, gopath := range ctx.GopathList { if pathos.FileHasPrefix(dir, gopath) { importPath = pathos.FileTrimPrefix(dir, gopath) importPath = pathos.SlashToImportPath(importPath) return importPath, gopath, nil } } return "", "", ErrNotInGOPATH{dir} }
// updatePackageReferences populates the referenced field in each Package. func (ctx *Context) updatePackageReferences() { canonicalUnderDirLookup := make(map[string]map[string]*Package) findCanonicalUnderDir := func(dir, canonical string) *Package { if importMap, found := canonicalUnderDirLookup[dir]; found { if pkg, found2 := importMap[canonical]; found2 { return pkg } } else { canonicalUnderDirLookup[dir] = make(map[string]*Package) } for _, pkg := range ctx.Package { if !pkg.inVendor { continue } removeFromEnd := len(pkg.Canonical) + len(ctx.VendorDiscoverFolder) + 2 nextLen := len(pkg.Dir) - removeFromEnd if nextLen < 0 { continue } checkDir := pkg.Dir[:nextLen] if !pathos.FileHasPrefix(dir, checkDir) { continue } if pkg.Canonical != canonical { continue } canonicalUnderDirLookup[dir][canonical] = pkg return pkg } canonicalUnderDirLookup[dir][canonical] = nil return nil } for _, pkg := range ctx.Package { pkg.referenced = make(map[string]*Package, len(pkg.referenced)) } for _, pkg := range ctx.Package { for _, f := range pkg.Files { for _, imp := range f.Imports { if vpkg := findCanonicalUnderDir(pkg.Dir, imp); vpkg != nil { vpkg.referenced[pkg.Local] = pkg continue } if other, found := ctx.Package[imp]; found { other.referenced[pkg.Local] = pkg continue } } } } }
func (ctx *Context) addSingleImport(pkgInDir, imp string) error { if _, found := ctx.Package[imp]; found { return nil } // Also need to check for vendor paths that won't use the local path in import path. for _, pkg := range ctx.Package { if pkg.Canonical == imp && pkg.inVendor && pathos.FileHasPrefix(pkg.Dir, pkgInDir) { return nil } } dir, gopath, err := ctx.findImportDir(pkgInDir, imp) if err != nil { if _, is := err.(ErrNotInGOPATH); is { ctx.setPackage("", imp, imp, "", StatusMissing) return nil } return err } if pathos.FileStringEquals(gopath, ctx.Goroot) { ctx.setPackage(dir, imp, imp, ctx.Goroot, StatusStandard) return nil } df, err := os.Open(dir) if err != nil { return err } info, err := df.Readdir(-1) df.Close() if err != nil { return err } for _, fi := range info { if fi.IsDir() { continue } switch fi.Name()[0] { case '.', '_': continue } if pathos.FileStringEquals(dir, pkgInDir) { continue } path := filepath.Join(dir, fi.Name()) err = ctx.addFileImports(path, gopath) if err != nil { return err } } return nil }
func readVendorFile(vendorFilePath string) (*vendorfile.File, error) { vf := &vendorfile.File{} f, err := os.Open(vendorFilePath) if err != nil { return nil, err } defer f.Close() err = vf.Unmarshal(f) if err != nil { return nil, err } // Determine if local field is relative to GOPATH or vendor file. // Change to relative to vendor file as needed. folder, _ := filepath.Split(vendorFilePath) relToFile := 0 relToGOPATH := 0 for _, pkg := range vf.Package { p := filepath.Join(folder, pathos.SlashToFilepath(pkg.Local)) _, err := os.Stat(p) if os.IsNotExist(err) { relToGOPATH++ continue } relToFile++ } if relToFile > relToGOPATH || len(vf.Package) == 0 { return vf, nil } gopathList := strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator)) gopath := "" for _, gp := range gopathList { if pathos.FileHasPrefix(folder, gp) { gopath = gp break } } if len(gopath) == 0 { return vf, nil } prefix := pathos.SlashToImportPath(pathos.FileTrimPrefix(folder, filepath.Join(gopath, "src"))) prefix = strings.TrimPrefix(prefix, "/") for _, pkg := range vf.Package { pkg.Local = strings.TrimPrefix(pkg.Local, prefix) } return vf, nil }
// updatePackageReferences populates the referenced field in each Package. func (ctx *Context) updatePackageReferences() { findCanonicalUnderDir := func(dir, canonical string) *Package { for _, pkg := range ctx.Package { if pkg.Status != StatusVendor { continue } removeFromEnd := len(pkg.Canonical) + len(ctx.VendorDiscoverFolder) + 2 checkDir := pkg.Dir[:len(pkg.Dir)-removeFromEnd] if !pathos.FileHasPrefix(dir, checkDir) { continue } if pkg.Canonical != canonical { continue } return pkg } return nil } for _, pkg := range ctx.Package { pkg.referenced = make(map[string]*Package, len(pkg.referenced)) } for _, pkg := range ctx.Package { for _, f := range pkg.Files { for _, imp := range f.Imports { if vpkg := findCanonicalUnderDir(pkg.Dir, imp); vpkg != nil { vpkg.referenced[pkg.Local] = pkg continue } if other, found := ctx.Package[imp]; found { other.referenced[pkg.Local] = pkg continue } } } } }
// Rewrite rewrites files to the local path. func (ctx *Context) rewrite() error { if !ctx.rewriteImports { return nil } if ctx.dirty { ctx.loadPackage() } ctx.dirty = true fileImports := make(map[string]map[string]*File) // map[ImportPath]map[FilePath]File for _, pkg := range ctx.Package { for _, f := range pkg.Files { for _, imp := range f.Imports { fileList := fileImports[imp] if fileList == nil { fileList = make(map[string]*File, 1) fileImports[imp] = fileList } fileList[f.Path] = f } } } filePaths := make(map[string]*File, len(ctx.RewriteRule)) for from := range ctx.RewriteRule { for _, f := range fileImports[from] { filePaths[f.Path] = f } } /* RULE: co2/internal/co3/pk3 -> co1/internal/co3/pk3 i co1/internal/co2/pk2 [co2/pk2] < ["co1/pk1"] i co1/internal/co3/pk3 [co3/pk3] < ["co1/pk1"] e co2/internal/co3/pk3 [co3/pk3] < ["co1/internal/co2/pk2"] l co1/pk1 < [] s strings < ["co1/internal/co3/pk3" "co2/internal/co3/pk3"] Rewrite the package "co1/internal/co2/pk2" because it references a package with a rewrite.from package. */ ctx.updatePackageReferences() for from := range ctx.RewriteRule { pkg := ctx.Package[from] if pkg == nil { continue } for _, ref := range pkg.referenced { for _, f := range ref.Files { dprintf("REF RW %s\n", f.Path) filePaths[f.Path] = f } } } defer func() { ctx.RewriteRule = make(map[string]string, 3) }() if len(ctx.RewriteRule) == 0 { return nil } goprint := &printer.Config{ Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8, } for _, fileInfo := range filePaths { if pathos.FileHasPrefix(fileInfo.Path, ctx.RootDir) == false { continue } // Read the file into AST, modify the AST. fileset := token.NewFileSet() f, err := parser.ParseFile(fileset, fileInfo.Path, nil, parser.ParseComments) if err != nil { return err } dprintf("RW:: File: %s\n", fileInfo.Path) for _, impNode := range f.Imports { imp, err := strconv.Unquote(impNode.Path.Value) if err != nil { return err } for from, to := range ctx.RewriteRule { if imp != from { continue } impNode.Path.Value = strconv.Quote(to) for i, metaImport := range fileInfo.Imports { if from == metaImport { dprintf("\tImport: %s -> %s\n", from, to) fileInfo.Imports[i] = to } } break } } // Remove import comment. st := fileInfo.Package.Status if st == StatusVendor || st == StatusUnused { var ic *ast.Comment if f.Name != nil { pos := f.Name.Pos() big: // Find the next comment after the package name. for _, cblock := range f.Comments { for _, c := range cblock.List { if c.Pos() > pos { ic = c break big } } } } if ic != nil { // If it starts with the import text, assume it is the import comment and remove. if index := strings.Index(ic.Text, " import "); index > 0 && index < 5 { ic.Text = strings.Repeat(" ", len(ic.Text)) } } } // Don't sort or modify the imports to minimize diffs. // Write the AST back to disk. fi, err := os.Stat(fileInfo.Path) if err != nil { return err } w, err := safefile.Create(fileInfo.Path, fi.Mode()) if err != nil { return err } err = goprint.Fprint(w, fileset, f) if err != nil { w.Close() return err } err = w.Commit() if err != nil { return err } } return nil }