• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package main_test
6
7import (
8	"cmd/go/internal/script"
9	"cmd/go/internal/script/scripttest"
10	"cmd/go/internal/work"
11	"errors"
12	"fmt"
13	"os"
14	"os/exec"
15	"strings"
16	"sync"
17	"time"
18)
19
20func scriptCommands(interrupt os.Signal, waitDelay time.Duration) map[string]script.Cmd {
21	cmds := scripttest.DefaultCmds()
22
23	// Customize the "exec" interrupt signal and grace period.
24	var cancel func(cmd *exec.Cmd) error
25	if interrupt != nil {
26		cancel = func(cmd *exec.Cmd) error {
27			return cmd.Process.Signal(interrupt)
28		}
29	}
30
31	cmdExec := script.Exec(cancel, waitDelay)
32	cmds["exec"] = cmdExec
33
34	add := func(name string, cmd script.Cmd) {
35		if _, ok := cmds[name]; ok {
36			panic(fmt.Sprintf("command %q is already registered", name))
37		}
38		cmds[name] = cmd
39	}
40
41	add("cc", scriptCC(cmdExec))
42	cmdGo := scriptGo(cancel, waitDelay)
43	add("go", cmdGo)
44	add("stale", scriptStale(cmdGo))
45
46	return cmds
47}
48
49// scriptCC runs the C compiler along with platform specific options.
50func scriptCC(cmdExec script.Cmd) script.Cmd {
51	return script.Command(
52		script.CmdUsage{
53			Summary: "run the platform C compiler",
54			Args:    "args...",
55		},
56		func(s *script.State, args ...string) (script.WaitFunc, error) {
57			b := work.NewBuilder(s.Getwd())
58			wait, err := cmdExec.Run(s, append(b.GccCmd(".", ""), args...)...)
59			if err != nil {
60				return wait, err
61			}
62			waitAndClean := func(s *script.State) (stdout, stderr string, err error) {
63				stdout, stderr, err = wait(s)
64				if closeErr := b.Close(); err == nil {
65					err = closeErr
66				}
67				return stdout, stderr, err
68			}
69			return waitAndClean, nil
70		})
71}
72
73var scriptGoInvoked sync.Map // testing.TB → go command was invoked
74
75// scriptGo runs the go command.
76func scriptGo(cancel func(*exec.Cmd) error, waitDelay time.Duration) script.Cmd {
77	cmd := script.Program(testGo, cancel, waitDelay)
78	// Inject code to update scriptGoInvoked before invoking the Go command.
79	return script.Command(*cmd.Usage(), func(state *script.State, s ...string) (script.WaitFunc, error) {
80		t, ok := tbFromContext(state.Context())
81		if !ok {
82			return nil, errors.New("script Context unexpectedly missing testing.TB key")
83		}
84		_, dup := scriptGoInvoked.LoadOrStore(t, true)
85		if !dup {
86			t.Cleanup(func() { scriptGoInvoked.Delete(t) })
87		}
88		return cmd.Run(state, s...)
89	})
90}
91
92// scriptStale checks that the named build targets are stale.
93func scriptStale(cmdGo script.Cmd) script.Cmd {
94	return script.Command(
95		script.CmdUsage{
96			Summary: "check that build targets are stale",
97			Args:    "target...",
98		},
99		func(s *script.State, args ...string) (script.WaitFunc, error) {
100			if len(args) == 0 {
101				return nil, script.ErrUsage
102			}
103			tmpl := "{{if .Error}}{{.ImportPath}}: {{.Error.Err}}" +
104				"{{else}}{{if not .Stale}}{{.ImportPath}} ({{.Target}}) is not stale{{end}}" +
105				"{{end}}"
106
107			wait, err := cmdGo.Run(s, append([]string{"list", "-e", "-f=" + tmpl}, args...)...)
108			if err != nil {
109				return nil, err
110			}
111
112			stdout, stderr, err := wait(s)
113			if len(stderr) != 0 {
114				s.Logf("%s", stderr)
115			}
116			if err != nil {
117				return nil, err
118			}
119			if out := strings.TrimSpace(stdout); out != "" {
120				return nil, errors.New(out)
121			}
122			return nil, nil
123		})
124}
125