github.com/bir3/gocompiler@v0.9.2202/exec/exec.go (about)

     1  package exec
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  	"time"
    12  )
    13  
    14  type process struct {
    15  	c *Cmd
    16  }
    17  type processState struct {
    18  	c *Cmd
    19  }
    20  
    21  type Cmd struct {
    22  	// Path is the path of the command to run.
    23  	//
    24  	// This is the only field that must be set to a non-zero
    25  	// value. If Path is relative, it is evaluated relative
    26  	// to Dir.
    27  	Path string
    28  
    29  	// Args holds command line arguments, including the command as Args[0].
    30  	// If the Args field is empty or nil, Run uses {Path}.
    31  	//
    32  	// In typical use, both Path and Args are set by calling Command.
    33  	Dir    string
    34  	Args   []string
    35  	Env    []string
    36  	Stdin  io.Reader
    37  	Stdout io.Writer
    38  	Stderr io.Writer
    39  
    40  	Cancel       func() error
    41  	WaitDelay    time.Duration
    42  	Process      process
    43  	ProcessState *processState
    44  
    45  	realCmd *exec.Cmd
    46  
    47  	isCommand        bool
    48  	isCommandContext bool
    49  	ctx              context.Context
    50  	name             string
    51  	arg              []string
    52  
    53  	isTool bool // temp debug
    54  }
    55  
    56  // type Cmd = exec.Cmd
    57  type ExitError = exec.ExitError
    58  type Signal = os.Signal
    59  
    60  var ErrWaitDelay error = exec.ErrWaitDelay
    61  
    62  func (p process) Signal(sig Signal) error {
    63  	return p.c.realCmd.Process.Signal(sig)
    64  }
    65  func (p process) Kill() error {
    66  	return p.c.realCmd.Process.Kill()
    67  }
    68  
    69  func (p processState) Success() bool {
    70  	return p.c.realCmd.ProcessState.Success()
    71  }
    72  func (p processState) UserTime() time.Duration {
    73  	return p.c.realCmd.ProcessState.UserTime()
    74  }
    75  func (p processState) SystemTime() time.Duration {
    76  	return p.c.realCmd.ProcessState.SystemTime()
    77  }
    78  
    79  func Command(name string, arg ...string) *Cmd {
    80  	cmd := &Cmd{name: name, arg: arg, isCommand: true,
    81  		Path: name,
    82  		Args: append([]string{name}, arg...),
    83  	}
    84  	if filepath.Base(name) == name {
    85  		lp, _ := LookPath(name)
    86  		if lp != "" {
    87  			// Update cmd.Path even if err is non-nil.
    88  			// If err is ErrDot (especially on Windows), lp may include a resolved
    89  			// extension (like .exe or .bat) that should be preserved.
    90  			cmd.Path = lp
    91  		}
    92  		//if err != nil {
    93  		//	cmd.Err = err
    94  		//}
    95  	}
    96  	return cmd
    97  }
    98  
    99  func CommandContext(ctx context.Context, name string, arg ...string) *Cmd {
   100  	cmd := Command(name, arg...)
   101  	cmd.ctx = ctx
   102  	return cmd
   103  
   104  }
   105  
   106  func isTool(file string) string {
   107  	// input: $GOROOT/pkg/tool/darwin_arm64/compile
   108  	// - assume GOROOT is absolute path
   109  	if strings.HasPrefix(file, os.Getenv("GOROOT")) {
   110  		dir_os_arch := filepath.Dir(file)     // pkg/tool/darwin_arm64
   111  		dir_tool := filepath.Dir(dir_os_arch) // pkg/tool
   112  
   113  		if filepath.Base(dir_tool) == "tool" {
   114  			return filepath.Base(file)
   115  		}
   116  	}
   117  	return ""
   118  }
   119  
   120  func LookPath(file string) (string, error) {
   121  
   122  	if isTool(file) != "" {
   123  
   124  		return file, nil
   125  	}
   126  	path, err := exec.LookPath(file)
   127  
   128  	return path, err
   129  }
   130  
   131  func (c *Cmd) Run() error {
   132  	c.mirror()
   133  	err := c.realCmd.Run()
   134  	return err
   135  }
   136  func (c *Cmd) Environ() []string {
   137  	c.mirror()
   138  	return c.realCmd.Environ()
   139  }
   140  
   141  func (c *Cmd) mirror() {
   142  	if c.isCommand {
   143  		c.realCmd = exec.Command(c.name, c.arg...)
   144  	} else if c.isCommandContext {
   145  		c.realCmd = exec.CommandContext(c.ctx, c.name, c.arg...)
   146  	} else {
   147  		// manually constructed by the user
   148  		c.realCmd = &exec.Cmd{}
   149  		c.realCmd.Path = c.Path
   150  		c.realCmd.Args = c.Args
   151  	}
   152  
   153  	c.realCmd.Dir = c.Dir
   154  	c.realCmd.Env = c.Env
   155  	c.realCmd.Stdin = c.Stdin
   156  	c.realCmd.Stdout = c.Stdout
   157  	c.realCmd.Stderr = c.Stderr
   158  	c.realCmd.Cancel = c.Cancel
   159  	c.realCmd.WaitDelay = c.WaitDelay
   160  	c.Process.c = c
   161  	if c.ProcessState == nil {
   162  		c.ProcessState = &processState{}
   163  	}
   164  	c.ProcessState.c = c
   165  
   166  	// fork/exec $GOROOT/pkg/tool/darwin_arm64/compile: no such file or directory
   167  	tool := isTool(c.Path)
   168  
   169  	if tool != "" {
   170  		// BUG: could add multiple times since shared var
   171  		if c.realCmd.Env == nil {
   172  			c.realCmd.Env = os.Environ()
   173  		}
   174  		c.realCmd.Env = append(c.realCmd.Env, fmt.Sprintf("BIR3_GOCOMPILER_TOOL=%s", tool))
   175  		c.realCmd.Path, _ = os.Executable()
   176  		c.realCmd.Args[0], _ = os.Executable()
   177  		c.isTool = true
   178  	}
   179  }
   180  
   181  func (c *Cmd) Start() error {
   182  	c.mirror()
   183  	return c.realCmd.Start()
   184  }
   185  func (c *Cmd) Wait() error {
   186  	c.mirror()
   187  	return c.realCmd.Wait()
   188  }
   189  func (c *Cmd) CombinedOutput() ([]byte, error) {
   190  	c.mirror()
   191  	buf, err := c.realCmd.CombinedOutput()
   192  
   193  	return buf, err
   194  
   195  }
   196  func (c *Cmd) Output() ([]byte, error) {
   197  	c.mirror()
   198  	buf, err := c.realCmd.Output()
   199  
   200  	return buf, err
   201  }
   202  func (c *Cmd) StdoutPipe() (io.ReadCloser, error) {
   203  	c.mirror()
   204  	return c.realCmd.StdoutPipe()
   205  }
   206  func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
   207  	c.mirror()
   208  	return c.realCmd.StdinPipe()
   209  }
   210  func (c *Cmd) StderrPipe() (io.ReadCloser, error) {
   211  	c.mirror()
   212  	return c.realCmd.StderrPipe()
   213  }
   214  
   215  /*
   216  
   217  # github.com/bir3/gocompiler/src/cmd/cgo
   218  ../gocompiler/src/cmd/cgo/util.go:64:25: undefined: exec.ExitError
   219  # github.com/bir3/gocompiler/src/cmd/gocmd/internal/cache
   220  ../gocompiler/src/cmd/gocmd/internal/cache/prog.go:164:14: undefined: exec.CommandContext
   221  # github.com/bir3/gocompiler/src/cmd/gocmd/internal/tool
   222  ../gocompiler/src/cmd/gocmd/internal/tool/tool.go:128:26: undefined: exec.ExitError
   223  # github.com/bir3/gocompiler/src/cmd/gocmd/internal/vcs
   224  ../gocompiler/src/cmd/gocmd/internal/vcs/vcs.go:699:28: undefined: exec.ExitError
   225  # github.com/bir3/gocompiler/src/cmd/gocmd/internal/modfetch/codehost
   226  ../gocompiler/src/cmd/gocmd/internal/modfetch/codehost/codehost.go:363:12: undefined: exec.CommandContext
   227  ../gocompiler/src/cmd/gocmd/internal/modfetch/codehost/git.go:844:42: undefined: exec.ExitError
   228  # github.com/bir3/gocompiler/src/cmd/compile/internal/importer
   229  ../gocompiler/src/cmd/compile/internal/importer/gcimporter.go:50:30: undefined: exec.ExitError
   230  
   231  
   232  */