fileutils: add support for copying symlinks to Copypath
This was removed instead of being special cased in constabulary/gb#185,
because gb has no direct need for it (see constabulary/gb#184). However
it's useful for gvt, as it needs to retain test fixtures intact (as vendor/
packages are testable) and needs to support symlink-based packages (since
the go build tool does).
Fixes #12
diff --git a/fileutils/fileutils.go b/fileutils/fileutils.go
index a0bba9f..7a3ead3 100644
--- a/fileutils/fileutils.go
+++ b/fileutils/fileutils.go
@@ -32,14 +32,12 @@
return nil
}
+ dst := filepath.Join(dst, path[len(src):])
+
if info.Mode()&os.ModeSymlink != 0 {
- if debugCopypath {
- fmt.Printf("skipping symlink: %v\n", path)
- }
- return nil
+ return Copylink(dst, path)
}
- dst := filepath.Join(dst, path[len(src):])
return Copyfile(dst, path)
})
if err != nil {
@@ -71,6 +69,23 @@
return err
}
+func Copylink(dst, src string) error {
+ target, err := os.Readlink(src)
+ if err != nil {
+ return fmt.Errorf("copylink: readlink: %v", err)
+ }
+ if err := mkdir(filepath.Dir(dst)); err != nil {
+ return fmt.Errorf("copylink: mkdirall: %v", err)
+ }
+ if err := os.Symlink(target, dst); err != nil {
+ return fmt.Errorf("copylink: symlink: %v", err)
+ }
+ if debugCopyfile {
+ fmt.Printf("copylink(dst: %v, src: %v, tgt: %s)\n", dst, src, target)
+ }
+ return nil
+}
+
// RemoveAll removes path and any children it contains. Unlike os.RemoveAll it
// deletes read only files on Windows.
func RemoveAll(path string) error {
diff --git a/fileutils/fileutils_test.go b/fileutils/fileutils_test.go
index cfc40a2..24b1af2 100644
--- a/fileutils/fileutils_test.go
+++ b/fileutils/fileutils_test.go
@@ -2,21 +2,29 @@
import (
"io/ioutil"
+ "os"
"path/filepath"
"runtime"
"testing"
)
-func TestCopypathSkipsSymlinks(t *testing.T) {
+func TestCopypathSymlinks(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("no symlinks on windows y'all")
}
dst := mktemp(t)
defer RemoveAll(dst)
- src := filepath.Join("_testdata", "copyfile", "a")
+ src := filepath.Join("_testdata", "copyfile")
if err := Copypath(dst, src); err != nil {
t.Fatalf("copypath(%s, %s): %v", dst, src, err)
}
+ res, err := os.Readlink(filepath.Join(dst, "a", "rick"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if res != "/never/going/to/give/you/up" {
+ t.Fatalf("target == %s, expected /never/going/to/give/you/up", res)
+ }
}
func mktemp(t *testing.T) string {