1// Copyright 2015 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package context 6 7import ( 8 "fmt" 9 "hash" 10 "io" 11 "os" 12 "path" 13 "path/filepath" 14 "sort" 15 "strings" 16 17 "github.com/kardianos/govendor/internal/pathos" 18 "github.com/pkg/errors" 19) 20 21type fileInfoSort []os.FileInfo 22 23func (l fileInfoSort) Len() int { 24 return len(l) 25} 26func (l fileInfoSort) Less(i, j int) bool { 27 a := l[i] 28 b := l[j] 29 if a.IsDir() == b.IsDir() { 30 return l[i].Name() < l[j].Name() 31 } 32 return !a.IsDir() 33} 34func (l fileInfoSort) Swap(i, j int) { 35 l[i], l[j] = l[j], l[i] 36} 37 38// CopyPackage copies the files from the srcPath to the destPath, destPath 39// folder and parents are are created if they don't already exist. 40func (ctx *Context) CopyPackage(destPath, srcPath, lookRoot, pkgPath string, ignoreFiles []string, tree bool, h hash.Hash, beforeCopy func(deps []string) error) error { 41 if pathos.FileStringEquals(destPath, srcPath) { 42 return fmt.Errorf("Attempting to copy package to same location %q.", destPath) 43 } 44 err := os.MkdirAll(destPath, 0777) 45 if err != nil { 46 return err 47 } 48 49 // Ensure the dest is empty of files. 50 destDir, err := os.Open(destPath) 51 if err != nil { 52 return err 53 } 54 ignoreTest := false 55 for _, ignore := range ctx.ignoreTag { 56 if ignore == "test" { 57 ignoreTest = true 58 break 59 } 60 } 61 62 fl, err := destDir.Readdir(-1) 63 destDir.Close() 64 if err != nil { 65 return err 66 } 67 for _, fi := range fl { 68 if fi.IsDir() { 69 if tree { 70 err = errors.Wrap(os.RemoveAll(filepath.Join(destPath, fi.Name())), "remove all existing tree entries") 71 if err != nil { 72 return err 73 } 74 } 75 continue 76 } 77 err = errors.Wrap(os.Remove(filepath.Join(destPath, fi.Name())), "remove existing file") 78 if err != nil { 79 return err 80 } 81 } 82 83 // Copy files into dest. 84 srcDir, err := os.Open(srcPath) 85 if err != nil { 86 return errors.Wrap(err, "open srcPath directory") 87 } 88 89 fl, err = srcDir.Readdir(-1) 90 srcDir.Close() 91 if err != nil { 92 return errors.Wrap(err, "src readdir") 93 } 94 if h != nil { 95 // Write relative path to GOPATH. 96 h.Write([]byte(strings.Trim(pkgPath, "/"))) 97 // Sort file list to present a stable hash. 98 sort.Sort(fileInfoSort(fl)) 99 } 100fileLoop: 101 for _, fi := range fl { 102 name := fi.Name() 103 if name[0] == '.' { 104 continue 105 } 106 if fi.IsDir() { 107 isTestdata := name == "testdata" 108 if !tree && !isTestdata { 109 continue 110 } 111 if name[0] == '_' { 112 continue 113 } 114 if ignoreTest { 115 if strings.HasSuffix(name, "_test") || isTestdata { 116 continue 117 } 118 } 119 nextDestPath := filepath.Join(destPath, name) 120 nextSrcPath := filepath.Join(srcPath, name) 121 var nextIgnoreFiles, deps []string 122 if !isTestdata && !strings.Contains(pkgPath, "/testdata/") { 123 nextIgnoreFiles, deps, err = ctx.getIgnoreFiles(nextSrcPath) 124 if err != nil { 125 return err 126 } 127 } 128 if beforeCopy != nil { 129 err = beforeCopy(deps) 130 if err != nil { 131 return errors.Wrap(err, "beforeCopy") 132 } 133 } 134 err = ctx.CopyPackage(nextDestPath, nextSrcPath, lookRoot, path.Join(pkgPath, name), nextIgnoreFiles, true, h, beforeCopy) 135 if err != nil { 136 return errors.Wrapf(err, 137 "CopyPackage dest=%q src=%q lookRoot=%q pkgPath=%q ignoreFiles=%q tree=%t has beforeCopy=%t", 138 nextDestPath, nextSrcPath, lookRoot, path.Join(pkgPath, name), nextIgnoreFiles, true, beforeCopy != nil, 139 ) 140 } 141 continue 142 } 143 for _, ignore := range ignoreFiles { 144 if pathos.FileStringEquals(name, ignore) { 145 continue fileLoop 146 } 147 } 148 if h != nil { 149 h.Write([]byte(name)) 150 } 151 err = copyFile( 152 filepath.Join(destPath, name), 153 filepath.Join(srcPath, name), 154 h, 155 ) 156 if err != nil { 157 return errors.Wrapf(err, "copyFile dest=%q src=%q", filepath.Join(destPath, name), filepath.Join(srcPath, name)) 158 } 159 } 160 161 return errors.Wrapf(licenseCopy(lookRoot, srcPath, filepath.Join(ctx.RootDir, ctx.VendorFolder), pkgPath), "licenseCopy srcPath=%q", srcPath) 162} 163 164func copyFile(destPath, srcPath string, h hash.Hash) error { 165 ss, err := os.Stat(srcPath) 166 if err != nil { 167 return errors.Wrap(err, "copyFile Stat") 168 } 169 src, err := os.Open(srcPath) 170 if err != nil { 171 return errors.Wrapf(err, "open src=%q", srcPath) 172 } 173 defer src.Close() 174 // Ensure we are not trying to copy a directory. May happen with symlinks. 175 if st, err := src.Stat(); err == nil { 176 if st.IsDir() { 177 return nil 178 } 179 } 180 181 dest, err := os.Create(destPath) 182 if err != nil { 183 return errors.Wrapf(err, "create dest=%q", destPath) 184 } 185 186 r := io.Reader(src) 187 188 if h != nil { 189 r = io.TeeReader(src, h) 190 } 191 192 _, err = io.Copy(dest, r) 193 // Close before setting mod and time. 194 dest.Close() 195 if err != nil { 196 return errors.Wrap(err, "copy") 197 } 198 err = os.Chmod(destPath, ss.Mode()) 199 if err != nil { 200 return err 201 } 202 return os.Chtimes(destPath, ss.ModTime(), ss.ModTime()) 203} 204