cmd/cue/cmd: make task references absolute

This prepares for allowing top-level tasks and a
more general task specification overall.

Change-Id: I45fd9eed123026da6db09a2a13bcba512dafb75b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4907
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/custom.go b/cmd/cue/cmd/custom.go
index ab4cc24..2ae10f7 100644
--- a/cmd/cue/cmd/custom.go
+++ b/cmd/cue/cmd/custom.go
@@ -124,22 +124,13 @@
 type taskKey string
 
 func (r *customRunner) keyForTask(t *task) taskKey {
-	a := []string{commandSection, r.name}
-	return keyForReference(append(a, t.path...)...)
+	return keyForReference(t.path...)
 }
 
 func keyForReference(ref ...string) (k taskKey) {
 	return taskKey(strings.Join(ref, "\000") + "\000")
 }
 
-func (r *customRunner) taskPath(t *task) []string {
-	return append([]string{commandSection, r.name}, t.path...)
-}
-
-func (r *customRunner) lookupTasks() cue.Value {
-	return r.root.Lookup(commandSection, r.name)
-}
-
 func doTasks(cmd *Command, typ, command string, root *cue.Instance) error {
 	err := executeTasks(cmd, typ, command, root)
 	exitIfErr(cmd, root, err, true)
@@ -181,7 +172,7 @@
 }
 
 func (r *customRunner) findTask(ref []string) *task {
-	for ; len(ref) > 2; ref = ref[:len(ref)-1] {
+	for ; len(ref) > 0; ref = ref[:len(ref)-1] {
 		if t := r.index[keyForReference(ref...)]; t != nil {
 			return t
 		}
@@ -207,8 +198,12 @@
 	}
 
 	for iter, _ := v.Fields(); iter.Next(); {
+		l := iter.Label()
+		if strings.HasPrefix(l, "$") || l == "command" || l == "commands" {
+			continue
+		}
 		var err error
-		q, err = getTasks(q, iter.Value(), append(stack, iter.Label()))
+		q, err = getTasks(q, iter.Value(), append(stack, l))
 		if err != nil {
 			return nil, err
 		}
@@ -226,10 +221,10 @@
 		root:  inst,
 		index: map[taskKey]*task{},
 	}
-	tasks := cr.lookupTasks()
 
 	// Create task entries from spec.
-	queue, err := getTasks(nil, tasks, nil)
+	base := []string{commandSection, cr.name}
+	queue, err := getTasks(nil, cr.root.Lookup(base...), base)
 	if err != nil {
 		return err
 	}
@@ -240,7 +235,7 @@
 
 	// Mark dependencies for unresolved nodes.
 	for _, t := range queue {
-		task := tasks.Lookup(t.path...)
+		task := cr.root.Lookup(t.path...)
 
 		// Inject dependency in `$after` field
 		after := task.Lookup("$after")
@@ -300,7 +295,7 @@
 			// code does not look up new strings in the index and that the
 			// full configuration, as used by the tasks, is pre-evaluated.
 			m.Lock()
-			obj := tasks.Lookup(t.path...)
+			obj := cr.root.Lookup(t.path...)
 			// NOTE: ignore the linter warning for the following line:
 			// itask.Context is an internal type and we want to break if any
 			// fields are added.
@@ -310,11 +305,7 @@
 				err = c.Err
 			}
 			if err == nil && update != nil {
-				cr.root, err = cr.root.Fill(update, cr.taskPath(t)...)
-
-				if err == nil {
-					tasks = cr.lookupTasks()
-				}
+				cr.root, err = cr.root.Fill(update, t.path...)
 			}
 			m.Unlock()
 
@@ -435,7 +426,7 @@
 	return &task{
 		Runner: runner,
 		index:  index,
-		path:   path,
+		path:   append([]string{}, path...), // make a copy.
 		done:   make(chan error),
 		dep:    make(map[*task]bool),
 	}, nil
diff --git a/cmd/cue/cmd/testdata/script/cmd_run.txt b/cmd/cue/cmd/testdata/script/cmd_run.txt
index 821fcf6..6c1e04e 100644
--- a/cmd/cue/cmd/testdata/script/cmd_run.txt
+++ b/cmd/cue/cmd/testdata/script/cmd_run.txt
@@ -8,23 +8,26 @@
 -- task_tool.cue --
 package home
 
-command: run: runBase & {
+command: run: RunBase & {
 	task: echo: cmd: "echo \(message)"
 }
 
 -- base_tool.cue --
 package home
 
+import (
+	"tool/cli"
+	"tool/exec"
+)
+
 // deliberately put in another file to test resolving top-level identifiers
 // in different files.
-runBase: {
-	task: echo: {
-		kind:   "exec"
+RunBase :: {
+	task: echo: exec.Run & {
 		stdout: string
 	}
 
-	task: display: {
-		kind: "print"
+	task: display: cli.Print & {
 		text: task.echo.stdout
 	}
 }