| // Package path contains path and environment utilities for Glide. |
| // |
| // This includes tools to find and manipulate Go path variables, as well as |
| // tools for copying from one path to another. |
| package path |
| |
| import ( |
| "fmt" |
| "io" |
| "os" |
| "os/user" |
| "path/filepath" |
| "strings" |
| ) |
| |
| // DefaultGlideFile is the default name for the glide.yaml file. |
| const DefaultGlideFile = "glide.yaml" |
| |
| // VendorDir is the name of the directory that holds vendored dependencies. |
| // |
| // As of Go 1.5, this is always vendor. |
| var VendorDir = "vendor" |
| |
| // Tmp is the temporary directory Glide should use. Defaults to "" which |
| // signals using the system default. |
| var Tmp = "" |
| |
| // Cache the location of the homedirectory. |
| var homeDir = "" |
| |
| // GlideFile is the name of the Glide file. |
| // |
| // Setting this is not concurrency safe. For consistency, it should really |
| // only be set once, at startup, or not at all. |
| var GlideFile = DefaultGlideFile |
| |
| // LockFile is the default name for the lock file. |
| const LockFile = "glide.lock" |
| |
| // Home returns the Glide home directory ($GLIDE_HOME or ~/.glide, typically). |
| // |
| // This normalizes to an absolute path, and passes through os.ExpandEnv. |
| func Home() string { |
| if homeDir != "" { |
| return homeDir |
| } |
| |
| // Initialize the default user. |
| u, err := user.Current() |
| if err == nil && u.HomeDir != "" { |
| homeDir = filepath.Join(u.HomeDir, ".glide") |
| } else { |
| cwd, err := os.Getwd() |
| if err == nil { |
| homeDir = filepath.Join(cwd, ".glide") |
| } else { |
| homeDir = ".glide" |
| } |
| } |
| |
| return homeDir |
| } |
| |
| // SetHome sets the home directory for Glide. |
| func SetHome(h string) { |
| homeDir = h |
| } |
| |
| // Vendor calculates the path to the vendor directory. |
| // |
| // Based on working directory, VendorDir and GlideFile, this attempts to |
| // guess the location of the vendor directory. |
| func Vendor() (string, error) { |
| cwd, err := os.Getwd() |
| if err != nil { |
| return "", err |
| } |
| |
| // Find the directory that contains glide.yaml |
| yamldir, err := GlideWD(cwd) |
| if err != nil { |
| return cwd, err |
| } |
| |
| gopath := filepath.Join(yamldir, VendorDir) |
| |
| // Resolve symlinks |
| info, err := os.Lstat(gopath) |
| if err != nil { |
| return gopath, nil |
| } |
| for i := 0; IsLink(info) && i < 255; i++ { |
| p, err := os.Readlink(gopath) |
| if err != nil { |
| return gopath, nil |
| } |
| |
| if filepath.IsAbs(p) { |
| gopath = p |
| } else { |
| gopath = filepath.Join(filepath.Dir(gopath), p) |
| } |
| |
| info, err = os.Lstat(gopath) |
| if err != nil { |
| return gopath, nil |
| } |
| } |
| |
| return gopath, nil |
| } |
| |
| // Glide gets the path to the closest glide file. |
| func Glide() (string, error) { |
| cwd, err := os.Getwd() |
| if err != nil { |
| return "", err |
| } |
| |
| // Find the directory that contains glide.yaml |
| yamldir, err := GlideWD(cwd) |
| if err != nil { |
| return cwd, err |
| } |
| |
| gf := filepath.Join(yamldir, GlideFile) |
| return gf, nil |
| } |
| |
| // GlideWD finds the working directory of the glide.yaml file, starting at dir. |
| // |
| // If the glide file is not found in the current directory, it recurses up |
| // a directory. |
| func GlideWD(dir string) (string, error) { |
| fullpath := filepath.Join(dir, GlideFile) |
| |
| if _, err := os.Stat(fullpath); err == nil { |
| return dir, nil |
| } |
| |
| base := filepath.Dir(dir) |
| if base == dir { |
| return "", fmt.Errorf("Cannot resolve parent of %s", base) |
| } |
| |
| return GlideWD(base) |
| } |
| |
| // Gopath gets GOPATH from environment and return the most relevant path. |
| // |
| // A GOPATH can contain a colon-separated list of paths. This retrieves the |
| // GOPATH and returns only the FIRST ("most relevant") path. |
| // |
| // This should be used carefully. If, for example, you are looking for a package, |
| // you may be better off using Gopaths. |
| func Gopath() string { |
| gopaths := Gopaths() |
| if len(gopaths) == 0 { |
| return "" |
| } |
| return gopaths[0] |
| } |
| |
| // Gopaths retrieves the Gopath as a list when there is more than one path |
| // listed in the Gopath. |
| func Gopaths() []string { |
| p := os.Getenv("GOPATH") |
| p = strings.Trim(p, string(filepath.ListSeparator)) |
| return filepath.SplitList(p) |
| } |
| |
| // Basepath returns the current working directory. |
| // |
| // If there is an error getting the working directory, this returns ".", which |
| // should function in cases where the directory is unlinked... Then again, |
| // maybe not. |
| func Basepath() string { |
| base, err := os.Getwd() |
| if err != nil { |
| return "." |
| } |
| return base |
| } |
| |
| // StripBasepath removes the base directory from a passed in path. |
| func StripBasepath(p string) string { |
| bp := Basepath() |
| return strings.TrimPrefix(p, bp+string(os.PathSeparator)) |
| } |
| |
| // IsLink returns true if the given FileInfo references a link. |
| func IsLink(fi os.FileInfo) bool { |
| return fi.Mode()&os.ModeSymlink == os.ModeSymlink |
| } |
| |
| // HasLock returns true if this can stat a lockfile at the givin location. |
| func HasLock(basepath string) bool { |
| _, err := os.Stat(filepath.Join(basepath, LockFile)) |
| return err == nil |
| } |
| |
| // IsDirectoryEmpty checks if a directory is empty. |
| func IsDirectoryEmpty(dir string) (bool, error) { |
| f, err := os.Open(dir) |
| if err != nil { |
| return false, err |
| } |
| defer f.Close() |
| |
| _, err = f.Readdir(1) |
| |
| if err == io.EOF { |
| return true, nil |
| } |
| |
| return false, err |
| } |
| |
| // CopyDir copies an entire source directory to the dest directory. |
| // |
| // This is akin to `cp -a src/* dest/` |
| // |
| // We copy the directory here rather than jumping out to a shell so we can |
| // support multiple operating systems. |
| func CopyDir(source string, dest string) error { |
| |
| // get properties of source dir |
| si, err := os.Stat(source) |
| if err != nil { |
| return err |
| } |
| |
| err = os.MkdirAll(dest, si.Mode()) |
| if err != nil { |
| return err |
| } |
| |
| d, err := os.Open(source) |
| if err != nil { |
| return err |
| } |
| defer d.Close() |
| |
| objects, err := d.Readdir(-1) |
| |
| for _, obj := range objects { |
| |
| sp := filepath.Join(source, "/", obj.Name()) |
| |
| dp := filepath.Join(dest, "/", obj.Name()) |
| |
| if obj.IsDir() { |
| err = CopyDir(sp, dp) |
| if err != nil { |
| return err |
| } |
| } else { |
| // perform copy |
| err = CopyFile(sp, dp) |
| if err != nil { |
| return err |
| } |
| } |
| |
| } |
| return nil |
| } |
| |
| // CopyFile copies a source file to a destination. |
| // |
| // It follows symbolic links and retains modes. |
| func CopyFile(source string, dest string) error { |
| ln, err := os.Readlink(source) |
| if err == nil { |
| return os.Symlink(ln, dest) |
| } |
| s, err := os.Open(source) |
| if err != nil { |
| return err |
| } |
| |
| defer s.Close() |
| |
| d, err := os.Create(dest) |
| if err != nil { |
| return err |
| } |
| |
| defer d.Close() |
| |
| _, err = io.Copy(d, s) |
| if err != nil { |
| return err |
| } |
| |
| si, err := os.Stat(source) |
| if err != nil { |
| return err |
| } |
| err = os.Chmod(dest, si.Mode()) |
| |
| return err |
| } |