github.com/bazelbuild/bazel-watcher@v0.25.2/internal/ibazel/command/notify_command.go (about) 1 // Copyright 2017 The Bazel Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package command 16 17 import ( 18 "bytes" 19 "io" 20 "os" 21 "sync" 22 23 "github.com/bazelbuild/bazel-watcher/internal/ibazel/log" 24 "github.com/bazelbuild/bazel-watcher/internal/ibazel/process_group" 25 ) 26 27 type notifyCommand struct { 28 target string 29 startupArgs []string 30 bazelArgs []string 31 args []string 32 pg process_group.ProcessGroup 33 stdin io.WriteCloser 34 termSync sync.Once 35 } 36 37 // NotifyCommand is an alternate mode for starting a command. In this mode the 38 // command will be notified on stdin that the source files have changed. 39 func NotifyCommand(startupArgs []string, bazelArgs []string, target string, args []string) Command { 40 return ¬ifyCommand{ 41 startupArgs: startupArgs, 42 target: target, 43 bazelArgs: bazelArgs, 44 args: args, 45 } 46 } 47 48 func (c *notifyCommand) Terminate() { 49 if !c.IsSubprocessRunning() { 50 c.pg = nil 51 return 52 } 53 c.termSync.Do(func() { 54 terminate(c.pg) 55 }) 56 c.pg = nil 57 } 58 59 func (c *notifyCommand) Kill() { 60 if c.pg != nil { 61 kill(c.pg) 62 } 63 } 64 65 func (c *notifyCommand) Start() (*bytes.Buffer, error) { 66 b := bazelNew() 67 b.SetStartupArgs(c.startupArgs) 68 b.SetArguments(c.bazelArgs) 69 70 b.WriteToStderr(true) 71 b.WriteToStdout(true) 72 73 var outputBuffer *bytes.Buffer 74 outputBuffer, c.pg = start(b, c.target, c.args) 75 // Keep the writer around. 76 var err error 77 c.stdin, err = c.pg.RootProcess().StdinPipe() 78 if err != nil { 79 log.Errorf("Error getting stdin pipe: %v", err) 80 return outputBuffer, err 81 } 82 83 c.pg.RootProcess().Env = append(os.Environ(), "IBAZEL_NOTIFY_CHANGES=y") 84 85 if err = c.pg.Start(); err != nil { 86 log.Errorf("Error starting process: %v", err) 87 return outputBuffer, err 88 } 89 log.Log("Starting...") 90 c.termSync = sync.Once{} 91 return outputBuffer, nil 92 } 93 94 func (c *notifyCommand) NotifyOfChanges() *bytes.Buffer { 95 b := bazelNew() 96 b.SetStartupArgs(c.startupArgs) 97 b.SetArguments(c.bazelArgs) 98 99 b.WriteToStderr(true) 100 b.WriteToStdout(true) 101 102 _, err := c.stdin.Write([]byte("IBAZEL_BUILD_STARTED\n")) 103 if err != nil { 104 log.Errorf("Error writing build to stdin: %s", err) 105 } 106 107 outputBuffer, res := b.Build(c.target) 108 if res != nil { 109 log.Errorf("IBAZEL BUILD FAILURE: %v", res) 110 _, err := c.stdin.Write([]byte("IBAZEL_BUILD_COMPLETED FAILURE\n")) 111 if err != nil { 112 log.Errorf("Error writing failure to stdin: %s", err) 113 } 114 } else { 115 log.Log("IBAZEL BUILD SUCCESS") 116 _, err := c.stdin.Write([]byte("IBAZEL_BUILD_COMPLETED SUCCESS\n")) 117 if err != nil { 118 log.Errorf("Error writing success to stdin: %v", err) 119 } 120 if !c.IsSubprocessRunning() { 121 log.Log("Restarting process...") 122 c.Terminate() 123 c.Start() 124 } 125 } 126 return outputBuffer 127 } 128 129 func (c *notifyCommand) IsSubprocessRunning() bool { 130 return c.pg != nil && subprocessRunning(c.pg.RootProcess()) 131 }