1// package fileutils provides utililty methods to copy and move files and directories. 2package fileutils 3 4import ( 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "runtime" 11 "strings" 12) 13 14// https://golang.org/cmd/go/#hdr-File_types 15var goFileTypes = []string{ 16 ".go", 17 ".c", ".h", 18 ".cc", ".cpp", ".cxx", ".hh", ".hpp", ".hxx", 19 ".m", 20 ".s", ".S", 21 ".swig", ".swigcxx", 22 ".syso", 23} 24 25var licenseFiles = []string{ 26 "LICENSE", "LICENCE", "UNLICENSE", "COPYING", "COPYRIGHT", 27} 28 29func ShouldSkip(path string, info os.FileInfo, tests, all bool) bool { 30 name := filepath.Base(path) 31 32 relevantFile := false 33 for _, ext := range goFileTypes { 34 if strings.HasSuffix(name, ext) { 35 relevantFile = true 36 break 37 } 38 } 39 40 testdata := false 41 for _, n := range strings.Split(filepath.Dir(path), string(filepath.Separator)) { 42 if n == "testdata" || n == "_testdata" { 43 testdata = true 44 } 45 } 46 47 skip := false 48 switch { 49 case all && !(name == ".git" && info.IsDir()) && name != ".bzr" && name != ".hg": 50 skip = false 51 52 // Include all files in a testdata folder 53 case tests && testdata: 54 skip = false 55 56 // https://golang.org/cmd/go/#hdr-Description_of_package_lists 57 case strings.HasPrefix(name, "."): 58 skip = true 59 case strings.HasPrefix(name, "_") && name != "_testdata": 60 skip = true 61 62 case !tests && name == "_testdata" && info.IsDir(): 63 skip = true 64 case !tests && name == "testdata" && info.IsDir(): 65 skip = true 66 case !tests && strings.HasSuffix(name, "_test.go") && !info.IsDir(): 67 skip = true 68 69 case !relevantFile && !info.IsDir(): 70 skip = true 71 } 72 73 return skip 74} 75 76// Copypath copies the contents of src to dst, excluding any file that is not 77// relevant to the Go compiler. 78func Copypath(dst string, src string, tests, all bool) error { 79 err := filepath.Walk(src, func(path string, info os.FileInfo, err error) error { 80 if err != nil { 81 return err 82 } 83 84 skip := ShouldSkip(path, info, tests, all) 85 86 if skip { 87 if info.IsDir() { 88 return filepath.SkipDir 89 } 90 return nil 91 } 92 93 if info.IsDir() { 94 return nil 95 } 96 97 dst := filepath.Join(dst, path[len(src):]) 98 99 if info.Mode()&os.ModeSymlink != 0 { 100 return Copylink(dst, path) 101 } 102 103 return Copyfile(dst, path) 104 }) 105 if err != nil { 106 // if there was an error during copying, remove the partial copy. 107 RemoveAll(dst) 108 } 109 return err 110} 111 112func Copyfile(dst, src string) error { 113 err := mkdir(filepath.Dir(dst)) 114 if err != nil { 115 return fmt.Errorf("copyfile: mkdirall: %v", err) 116 } 117 r, err := os.Open(src) 118 if err != nil { 119 return fmt.Errorf("copyfile: open(%q): %v", src, err) 120 } 121 defer r.Close() 122 w, err := os.Create(dst) 123 if err != nil { 124 return fmt.Errorf("copyfile: create(%q): %v", dst, err) 125 } 126 defer w.Close() 127 _, err = io.Copy(w, r) 128 return err 129} 130 131func Copylink(dst, src string) error { 132 target, err := os.Readlink(src) 133 if err != nil { 134 return fmt.Errorf("copylink: readlink: %v", err) 135 } 136 if err := mkdir(filepath.Dir(dst)); err != nil { 137 return fmt.Errorf("copylink: mkdirall: %v", err) 138 } 139 if err := os.Symlink(target, dst); err != nil { 140 return fmt.Errorf("copylink: symlink: %v", err) 141 } 142 return nil 143} 144 145// RemoveAll removes path and any children it contains. Unlike os.RemoveAll it 146// deletes read only files on Windows. 147func RemoveAll(path string) error { 148 if runtime.GOOS == "windows" { 149 // Simple case: if Remove works, we're done. 150 err := os.Remove(path) 151 if err == nil || os.IsNotExist(err) { 152 return nil 153 } 154 // make sure all files are writable so we can delete them 155 filepath.Walk(path, func(path string, info os.FileInfo, err error) error { 156 if err != nil { 157 // walk gave us some error, give it back. 158 return err 159 } 160 mode := info.Mode() 161 if mode|0200 == mode { 162 return nil 163 } 164 return os.Chmod(path, mode|0200) 165 }) 166 } 167 return os.RemoveAll(path) 168} 169 170// CopyLicense copies the license file from folder src to folder dst. 171func CopyLicense(dst, src string) error { 172 files, err := ioutil.ReadDir(src) 173 if err != nil { 174 return err 175 } 176 for _, f := range files { 177 if f.IsDir() { 178 continue 179 } 180 for _, candidate := range licenseFiles { 181 if strings.ToLower(candidate) == strings.TrimSuffix( 182 strings.TrimSuffix(strings.ToLower(f.Name()), ".md"), ".txt") { 183 if err := Copyfile(filepath.Join(dst, f.Name()), 184 filepath.Join(src, f.Name())); err != nil { 185 return err 186 } 187 } 188 } 189 } 190 return nil 191} 192 193func mkdir(path string) error { 194 return os.MkdirAll(path, 0755) 195} 196