pkg/tool/file: allow OS-independent filenames

More or less. Absolute paths are still different.

Change-Id: I4198184bd323e7ab02feaa53560a29eff5fb306c
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2283
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/pkg/tool/file/file.cue b/pkg/tool/file/file.cue
index ebad2a5..23fe7f6 100644
--- a/pkg/tool/file/file.cue
+++ b/pkg/tool/file/file.cue
@@ -19,6 +19,9 @@
 	kind: "tool/file.Read"
 
 	// filename names the file to read.
+	//
+	// Relative names are taken relative to the current working directory.
+	// Slashes are converted to the native OS path separator.
 	filename: !=""
 
 	// contents is the read contents. If the contents are constraint to bytes
@@ -32,6 +35,9 @@
 	kind: "tool/file.Append"
 
 	// filename names the file to append.
+	//
+	// Relative names are taken relative to the current working directory.
+	// Slashes are converted to the native OS path separator.
 	filename: !=""
 
 	// permissions defines the permissions to use if the file does not yet exist.
@@ -46,6 +52,9 @@
 	kind: "tool/file.Create"
 
 	// filename names the file to write.
+	//
+	// Relative names are taken relative to the current working directory.
+	// Slashes are converted to the native OS path separator.
 	filename: !=""
 
 	// permissions defines the permissions to use if the file does not yet exist.
@@ -55,9 +64,14 @@
 	contents: bytes | string
 }
 
+// Glob returns a list of files.
 Glob: {
 	kind: "tool/file.Glob"
 
+	// glob specifies the pattern to match files with.
+	//
+	// A relative pattern is taken relative to the current working directory.
+	// Slashes are converted to the native OS path separator.
 	glob: !=""
 	files: [...string]
 }
diff --git a/pkg/tool/file/file.go b/pkg/tool/file/file.go
index 29bf6d9..b64a64d 100644
--- a/pkg/tool/file/file.go
+++ b/pkg/tool/file/file.go
@@ -64,6 +64,7 @@
 
 func (c *cmdAppend) Run(ctx *task.Context, v cue.Value) (res interface{}, err error) {
 	filename := lookupStr(v, "filename")
+	filename = filepath.FromSlash(filename)
 	mode, err := v.Lookup("permissions").Int64()
 	if err != nil {
 		return nil, err
@@ -84,6 +85,7 @@
 
 func (c *cmdCreate) Run(ctx *task.Context, v cue.Value) (res interface{}, err error) {
 	filename := lookupStr(v, "filename")
+	filename = filepath.FromSlash(filename)
 	mode, err := v.Lookup("permissions").Int64()
 	if err != nil {
 		return nil, err
@@ -94,6 +96,10 @@
 }
 
 func (c *cmdGlob) Run(ctx *task.Context, v cue.Value) (res interface{}, err error) {
-	m, err := filepath.Glob(lookupStr(v, "glob"))
+	glob := filepath.FromSlash(lookupStr(v, "glob"))
+	m, err := filepath.Glob(glob)
+	for i, s := range m {
+		m[i] = filepath.ToSlash(s)
+	}
 	return m, err
 }
diff --git a/pkg/tool/file/file_test.go b/pkg/tool/file/file_test.go
index 474432f..e82081f 100644
--- a/pkg/tool/file/file_test.go
+++ b/pkg/tool/file/file_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"reflect"
 	"testing"
 
@@ -73,6 +74,7 @@
 	name := f.Name()
 	defer os.Remove(name)
 	f.Close()
+	name = filepath.ToSlash(name)
 
 	v := parse(t, "tool/file.Append", fmt.Sprintf(`{
 		filename: "%s"
@@ -101,6 +103,7 @@
 	name := f.Name()
 	defer os.Remove(name)
 	f.Close()
+	name = filepath.ToSlash(name)
 
 	v := parse(t, "tool/file.Create", fmt.Sprintf(`{
 		filename: "%s"