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