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 &notifyCommand{
    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  }