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 {