Last active
August 29, 2015 14:02
-
-
Save tiborvass/42dd1d65ad2024c91f1e to your computer and use it in GitHub Desktop.
Go 1.2.2 -> 1.3 os/exec diff
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- ./go1.2.2/src/pkg/os/exec/exec.go 2014-05-05 14:00:49.000000000 -0400 | |
+++ ./go1.3/src/pkg/os/exec/exec.go 2014-06-18 20:43:40.000000000 -0400 | |
@@ -12,7 +12,10 @@ | |
"errors" | |
"io" | |
"os" | |
+ "path/filepath" | |
+ "runtime" | |
"strconv" | |
+ "strings" | |
"sync" | |
"syscall" | |
) | |
@@ -33,7 +36,8 @@ | |
// Path is the path of the command to run. | |
// | |
// This is the only field that must be set to a non-zero | |
- // value. | |
+ // value. If Path is relative, it is evaluated relative | |
+ // to Dir. | |
Path string | |
// Args holds command line arguments, including the command as Args[0]. | |
@@ -84,7 +88,7 @@ | |
// available after a call to Wait or Run. | |
ProcessState *os.ProcessState | |
- err error // last error (from LookPath, stdin, stdout, stderr) | |
+ lookPathErr error // LookPath error, if any. | |
finished bool // when Wait was called | |
childFiles []*os.File | |
closeAfterStart []io.Closer | |
@@ -96,8 +100,7 @@ | |
// Command returns the Cmd struct to execute the named program with | |
// the given arguments. | |
// | |
-// It sets Path and Args in the returned structure and zeroes the | |
-// other fields. | |
+// It sets only the Path and Args in the returned structure. | |
// | |
// If name contains no path separators, Command uses LookPath to | |
// resolve the path to a complete name if possible. Otherwise it uses | |
@@ -107,19 +110,22 @@ | |
// followed by the elements of arg, so arg should not include the | |
// command name itself. For example, Command("echo", "hello") | |
func Command(name string, arg ...string) *Cmd { | |
- aname, err := LookPath(name) | |
- if err != nil { | |
- aname = name | |
- } | |
- return &Cmd{ | |
- Path: aname, | |
+ cmd := &Cmd{ | |
+ Path: name, | |
Args: append([]string{name}, arg...), | |
- err: err, | |
} | |
+ if filepath.Base(name) == name { | |
+ if lp, err := LookPath(name); err != nil { | |
+ cmd.lookPathErr = err | |
+ } else { | |
+ cmd.Path = lp | |
+ } | |
+ } | |
+ return cmd | |
} | |
// interfaceEqual protects against panics from doing equality tests on | |
-// two interfaces with non-comparable underlying types | |
+// two interfaces with non-comparable underlying types. | |
func interfaceEqual(a, b interface{}) bool { | |
defer func() { | |
recover() | |
@@ -233,12 +239,50 @@ | |
return c.Wait() | |
} | |
+// lookExtensions finds windows executable by its dir and path. | |
+// It uses LookPath to try appropriate extensions. | |
+// lookExtensions does not search PATH, instead it converts `prog` into `.\prog`. | |
+func lookExtensions(path, dir string) (string, error) { | |
+ if filepath.Base(path) == path { | |
+ path = filepath.Join(".", path) | |
+ } | |
+ if dir == "" { | |
+ return LookPath(path) | |
+ } | |
+ if filepath.VolumeName(path) != "" { | |
+ return LookPath(path) | |
+ } | |
+ if len(path) > 1 && os.IsPathSeparator(path[0]) { | |
+ return LookPath(path) | |
+ } | |
+ dirandpath := filepath.Join(dir, path) | |
+ // We assume that LookPath will only add file extension. | |
+ lp, err := LookPath(dirandpath) | |
+ if err != nil { | |
+ return "", err | |
+ } | |
+ ext := strings.TrimPrefix(lp, dirandpath) | |
+ return path + ext, nil | |
+} | |
+ | |
// Start starts the specified command but does not wait for it to complete. | |
+// | |
+// The Wait method will return the exit code and release associated resources | |
+// once the command exits. | |
func (c *Cmd) Start() error { | |
- if c.err != nil { | |
+ if c.lookPathErr != nil { | |
c.closeDescriptors(c.closeAfterStart) | |
c.closeDescriptors(c.closeAfterWait) | |
- return c.err | |
+ return c.lookPathErr | |
+ } | |
+ if runtime.GOOS == "windows" { | |
+ lp, err := lookExtensions(c.Path, c.Dir) | |
+ if err != nil { | |
+ c.closeDescriptors(c.closeAfterStart) | |
+ c.closeDescriptors(c.closeAfterWait) | |
+ return err | |
+ } | |
+ c.Path = lp | |
} | |
if c.Process != nil { | |
return errors.New("exec: already started") | |
@@ -300,6 +344,8 @@ | |
// If the command fails to run or doesn't complete successfully, the | |
// error is of type *ExitError. Other error types may be | |
// returned for I/O problems. | |
+// | |
+// Wait releases any resources associated with the Cmd. | |
func (c *Cmd) Wait() error { | |
if c.Process == nil { | |
return errors.New("exec: not started") | |
@@ -383,15 +429,17 @@ | |
type closeOnce struct { | |
*os.File | |
- close sync.Once | |
- closeErr error | |
+ once sync.Once | |
+ err error | |
} | |
func (c *closeOnce) Close() error { | |
- c.close.Do(func() { | |
- c.closeErr = c.File.Close() | |
- }) | |
- return c.closeErr | |
+ c.once.Do(c.close) | |
+ return c.err | |
+} | |
+ | |
+func (c *closeOnce) close() { | |
+ c.err = c.File.Close() | |
} | |
// StdoutPipe returns a pipe that will be connected to the command's |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment