func (m *mover) checkValid() error { const prefix = "invalid move destination" match, err := regexp.MatchString("^[_\\pL][_\\pL\\p{Nd}]*$", path.Base(m.to)) if err != nil { panic("regexp.MatchString failed") } if !match { return fmt.Errorf("%s: %s; gomvpkg does not support move destinations "+ "whose base names are not valid go identifiers", prefix, m.to) } if buildutil.FileExists(m.ctxt, m.toDir) { return fmt.Errorf("%s: %s conflicts with file %s", prefix, m.to, m.toDir) } if buildutil.IsDir(m.ctxt, m.toDir) { return fmt.Errorf("%s: %s conflicts with directory %s", prefix, m.to, m.toDir) } for _, toSubPkg := range m.destinations { if _, err := m.ctxt.Import(toSubPkg, "", build.FindOnly); err == nil { return fmt.Errorf("%s: %s; package or subpackage %s already exists", prefix, m.to, toSubPkg) } } return nil }
// srcDir returns the absolute path of the srcdir containing pkg. func srcDir(ctxt *build.Context, pkg string) (string, error) { for _, srcDir := range ctxt.SrcDirs() { path := buildutil.JoinPath(ctxt, srcDir, pkg) if buildutil.IsDir(ctxt, path) { return srcDir, nil } } return "", fmt.Errorf("src dir not found for package: %s", pkg) }
// Move, given a package path and a destination package path, will try // to move the given package to the new path. The Move function will // first check for any conflicts preventing the move, such as a // package already existing at the destination package path. If the // move can proceed, it builds an import graph to find all imports of // the packages whose paths need to be renamed. This includes uses of // the subpackages of the package to be moved as those packages will // also need to be moved. It then renames all imports to point to the // new paths, and then moves the packages to their new paths. func Move(ctxt *build.Context, from, to, moveTmpl string) error { srcDir, err := srcDir(ctxt, from) if err != nil { return err } // This should be the only place in the program that constructs // file paths. // TODO(matloob): test on Microsoft Windows. fromDir := buildutil.JoinPath(ctxt, srcDir, filepath.FromSlash(from)) toDir := buildutil.JoinPath(ctxt, srcDir, filepath.FromSlash(to)) toParent := filepath.Dir(toDir) if !buildutil.IsDir(ctxt, toParent) { return fmt.Errorf("parent directory does not exist for path %s", toDir) } // Build the import graph and figure out which packages to update. fwd, rev, errors := importgraph.Build(ctxt) if len(errors) > 0 { fmt.Fprintf(os.Stderr, "While scanning Go workspace:\n") for path, err := range errors { fmt.Fprintf(os.Stderr, "Package %q: %s.\n", path, err) } return fmt.Errorf("failed to construct import graph") } // Determine the affected packages---the set of packages whose import // statements need updating. affectedPackages := map[string]bool{from: true} destinations := map[string]string{} // maps old dir to new dir for pkg := range subpackages(ctxt, srcDir, from) { for r := range rev[pkg] { affectedPackages[r] = true } destinations[pkg] = strings.Replace(pkg, // Ensure directories have a trailing "/". filepath.Join(from, ""), filepath.Join(to, ""), 1) } // Load all the affected packages. iprog, err := loadProgram(ctxt, affectedPackages) if err != nil { return err } // Prepare the move command, if one was supplied. var cmd string if moveTmpl != "" { if cmd, err = moveCmd(moveTmpl, fromDir, toDir); err != nil { return err } } m := mover{ ctxt: ctxt, fwd: fwd, rev: rev, iprog: iprog, from: from, to: to, fromDir: fromDir, toDir: toDir, affectedPackages: affectedPackages, destinations: destinations, cmd: cmd, } if err := m.checkValid(); err != nil { return err } m.move() return nil }