github.com/faceair/glide@v0.13.2-0.20180618124022-54b926f67677/path/path.go (about)

     1  // Package path contains path and environment utilities for Glide.
     2  //
     3  // This includes tools to find and manipulate Go path variables, as well as
     4  // tools for copying from one path to another.
     5  package path
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/mitchellh/go-homedir"
    16  )
    17  
    18  // DefaultGlideFile is the default name for the glide.yaml file.
    19  const DefaultGlideFile = "glide.yaml"
    20  
    21  // VendorDir is the name of the directory that holds vendored dependencies.
    22  //
    23  // As of Go 1.5, this is always vendor.
    24  var VendorDir = "vendor"
    25  
    26  // Tmp is the temporary directory Glide should use. Defaults to "" which
    27  // signals using the system default.
    28  var Tmp = ""
    29  
    30  // Cache the location of the homedirectory.
    31  var homeDir = ""
    32  
    33  // GlideFile is the name of the Glide file.
    34  //
    35  // Setting this is not concurrency safe. For consistency, it should really
    36  // only be set once, at startup, or not at all.
    37  var GlideFile = DefaultGlideFile
    38  
    39  // LockFile is the default name for the lock file.
    40  const LockFile = "glide.lock"
    41  
    42  func init() {
    43  
    44  	// As of Go 1.8 the GOPATH is no longer required to be set. Instead there
    45  	// is a default value. If there is no GOPATH check for the default value.
    46  	// Note, checking the GOPATH first to avoid invoking the go toolchain if
    47  	// possible.
    48  	if gopaths = os.Getenv("GOPATH"); len(gopaths) == 0 {
    49  		goExecutable := os.Getenv("GLIDE_GO_EXECUTABLE")
    50  		if len(goExecutable) <= 0 {
    51  			goExecutable = "go"
    52  		}
    53  		out, err := exec.Command(goExecutable, "env", "GOPATH").Output()
    54  		if err == nil {
    55  			gopaths = strings.TrimSpace(string(out))
    56  		}
    57  	}
    58  }
    59  
    60  // Home returns the Glide home directory ($GLIDE_HOME or ~/.glide, typically).
    61  //
    62  // This normalizes to an absolute path, and passes through os.ExpandEnv.
    63  func Home() string {
    64  	if homeDir != "" {
    65  		return homeDir
    66  	}
    67  
    68  	if h, err := homedir.Dir(); err == nil {
    69  		homeDir = filepath.Join(h, ".glide")
    70  	} else {
    71  		cwd, err := os.Getwd()
    72  		if err == nil {
    73  			homeDir = filepath.Join(cwd, ".glide")
    74  		} else {
    75  			homeDir = ".glide"
    76  		}
    77  	}
    78  
    79  	return homeDir
    80  }
    81  
    82  // SetHome sets the home directory for Glide.
    83  func SetHome(h string) {
    84  	homeDir = h
    85  }
    86  
    87  // Vendor calculates the path to the vendor directory.
    88  //
    89  // Based on working directory, VendorDir and GlideFile, this attempts to
    90  // guess the location of the vendor directory.
    91  func Vendor() (string, error) {
    92  	cwd, err := os.Getwd()
    93  	if err != nil {
    94  		return "", err
    95  	}
    96  
    97  	// Find the directory that contains glide.yaml
    98  	yamldir, err := GlideWD(cwd)
    99  	if err != nil {
   100  		return cwd, err
   101  	}
   102  
   103  	gopath := filepath.Join(yamldir, VendorDir)
   104  
   105  	// Resolve symlinks
   106  	info, err := os.Lstat(gopath)
   107  	if err != nil {
   108  		return gopath, nil
   109  	}
   110  	for i := 0; IsLink(info) && i < 255; i++ {
   111  		p, err := os.Readlink(gopath)
   112  		if err != nil {
   113  			return gopath, nil
   114  		}
   115  
   116  		if filepath.IsAbs(p) {
   117  			gopath = p
   118  		} else {
   119  			gopath = filepath.Join(filepath.Dir(gopath), p)
   120  		}
   121  
   122  		info, err = os.Lstat(gopath)
   123  		if err != nil {
   124  			return gopath, nil
   125  		}
   126  	}
   127  
   128  	return gopath, nil
   129  }
   130  
   131  // Glide gets the path to the closest glide file.
   132  func Glide() (string, error) {
   133  	cwd, err := os.Getwd()
   134  	if err != nil {
   135  		return "", err
   136  	}
   137  
   138  	// Find the directory that contains glide.yaml
   139  	yamldir, err := GlideWD(cwd)
   140  	if err != nil {
   141  		return cwd, err
   142  	}
   143  
   144  	gf := filepath.Join(yamldir, GlideFile)
   145  	return gf, nil
   146  }
   147  
   148  // GlideWD finds the working directory of the glide.yaml file, starting at dir.
   149  //
   150  // If the glide file is not found in the current directory, it recurses up
   151  // a directory.
   152  func GlideWD(dir string) (string, error) {
   153  	fullpath := filepath.Join(dir, GlideFile)
   154  
   155  	if _, err := os.Stat(fullpath); err == nil {
   156  		return dir, nil
   157  	}
   158  
   159  	base := filepath.Dir(dir)
   160  	if base == dir {
   161  		return "", fmt.Errorf("Cannot resolve parent of %s", base)
   162  	}
   163  
   164  	return GlideWD(base)
   165  }
   166  
   167  // Stores the gopaths so they do not get repeatedly looked up. This is especially
   168  // true when the default value needs to be retrieved from `go env GOPATH`.
   169  // TODO(mattfarina): Instead of a singleton an application context would be a
   170  // better place to store things like this.
   171  var gopaths string
   172  
   173  // Gopath gets GOPATH from environment and return the most relevant path.
   174  //
   175  // A GOPATH can contain a colon-separated list of paths. This retrieves the
   176  // GOPATH and returns only the FIRST ("most relevant") path.
   177  //
   178  // This should be used carefully. If, for example, you are looking for a package,
   179  // you may be better off using Gopaths.
   180  func Gopath() string {
   181  	gopaths := Gopaths()
   182  	if len(gopaths) == 0 {
   183  		return ""
   184  	}
   185  	return gopaths[0]
   186  }
   187  
   188  // Gopaths retrieves the Gopath as a list when there is more than one path
   189  // listed in the Gopath.
   190  func Gopaths() []string {
   191  	p := strings.Trim(gopaths, string(filepath.ListSeparator))
   192  	return filepath.SplitList(p)
   193  }
   194  
   195  // Basepath returns the current working directory.
   196  //
   197  // If there is an error getting the working directory, this returns ".", which
   198  // should function in cases where the directory is unlinked... Then again,
   199  // maybe not.
   200  func Basepath() string {
   201  	base, err := os.Getwd()
   202  	if err != nil {
   203  		return "."
   204  	}
   205  	return base
   206  }
   207  
   208  // StripBasepath removes the base directory from a passed in path.
   209  func StripBasepath(p string) string {
   210  	bp := Basepath()
   211  	return strings.TrimPrefix(p, bp+string(os.PathSeparator))
   212  }
   213  
   214  // IsLink returns true if the given FileInfo references a link.
   215  func IsLink(fi os.FileInfo) bool {
   216  	return fi.Mode()&os.ModeSymlink == os.ModeSymlink
   217  }
   218  
   219  // HasLock returns true if this can stat a lockfile at the givin location.
   220  func HasLock(basepath string) bool {
   221  	_, err := os.Stat(filepath.Join(basepath, LockFile))
   222  	return err == nil
   223  }
   224  
   225  // IsDirectoryEmpty checks if a directory is empty.
   226  func IsDirectoryEmpty(dir string) (bool, error) {
   227  	f, err := os.Open(dir)
   228  	if err != nil {
   229  		return false, err
   230  	}
   231  	defer f.Close()
   232  
   233  	_, err = f.Readdir(1)
   234  
   235  	if err == io.EOF {
   236  		return true, nil
   237  	}
   238  
   239  	return false, err
   240  }
   241  
   242  // CopyDir copies an entire source directory to the dest directory.
   243  //
   244  // This is akin to `cp -a src/* dest/`
   245  //
   246  // We copy the directory here rather than jumping out to a shell so we can
   247  // support multiple operating systems.
   248  func CopyDir(source string, dest string) error {
   249  
   250  	// get properties of source dir
   251  	si, err := os.Stat(source)
   252  	if err != nil {
   253  		return err
   254  	}
   255  
   256  	err = os.MkdirAll(dest, si.Mode())
   257  	if err != nil {
   258  		return err
   259  	}
   260  
   261  	d, err := os.Open(source)
   262  	if err != nil {
   263  		return err
   264  	}
   265  	defer d.Close()
   266  
   267  	objects, err := d.Readdir(-1)
   268  
   269  	for _, obj := range objects {
   270  
   271  		sp := filepath.Join(source, "/", obj.Name())
   272  
   273  		dp := filepath.Join(dest, "/", obj.Name())
   274  
   275  		if obj.IsDir() {
   276  			err = CopyDir(sp, dp)
   277  			if err != nil {
   278  				return err
   279  			}
   280  		} else {
   281  			// perform copy
   282  			err = CopyFile(sp, dp)
   283  			if err != nil {
   284  				return err
   285  			}
   286  		}
   287  
   288  	}
   289  	return nil
   290  }
   291  
   292  // CopyFile copies a source file to a destination.
   293  //
   294  // It follows symbolic links and retains modes.
   295  func CopyFile(source string, dest string) error {
   296  	ln, err := os.Readlink(source)
   297  	if err == nil {
   298  		return os.Symlink(ln, dest)
   299  	}
   300  	s, err := os.Open(source)
   301  	if err != nil {
   302  		return err
   303  	}
   304  
   305  	defer s.Close()
   306  
   307  	d, err := os.Create(dest)
   308  	if err != nil {
   309  		return err
   310  	}
   311  
   312  	defer d.Close()
   313  
   314  	_, err = io.Copy(d, s)
   315  	if err != nil {
   316  		return err
   317  	}
   318  
   319  	si, err := os.Stat(source)
   320  	if err != nil {
   321  		return err
   322  	}
   323  	err = os.Chmod(dest, si.Mode())
   324  
   325  	return err
   326  }