Skip to content

Commit 25b85ea

Browse files
asadovskyGerrit Code Review
authored and
Gerrit Code Review
committed
Merge "gosh: convert Shell.Rename to Shell.Move"
2 parents b576e2d + 7b12594 commit 25b85ea

File tree

4 files changed

+59
-49
lines changed

4 files changed

+59
-49
lines changed

gosh/.api

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ pkg gosh, method (*Shell) FuncCmd(*Func, ...interface{}) *Cmd
4343
pkg gosh, method (*Shell) HandleError(error)
4444
pkg gosh, method (*Shell) MakeTempDir() string
4545
pkg gosh, method (*Shell) MakeTempFile() *os.File
46+
pkg gosh, method (*Shell) Move(string, string)
4647
pkg gosh, method (*Shell) Ok()
4748
pkg gosh, method (*Shell) Popd()
4849
pkg gosh, method (*Shell) Pushd(string)
49-
pkg gosh, method (*Shell) Rename(string, string)
5050
pkg gosh, method (*Shell) Wait()
5151
pkg gosh, type Cmd struct
5252
pkg gosh, type Cmd struct, Args []string

gosh/internal/gosh_example/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"v.io/x/lib/gosh/internal/gosh_example_lib"
1212
)
1313

14+
// Mirrors TestCmd in shell_test.go.
1415
func ExampleCmd() {
1516
sh := gosh.NewShell(gosh.Opts{})
1617
defer sh.Cleanup()
@@ -33,6 +34,7 @@ var (
3334
serveFunc = gosh.RegisterFunc("serveFunc", lib.Serve)
3435
)
3536

37+
// Mirrors TestFuncCmd in shell_test.go.
3638
func ExampleFuncCmd() {
3739
sh := gosh.NewShell(gosh.Opts{})
3840
defer sh.Cleanup()

gosh/shell.go

+28-31
Original file line numberDiff line numberDiff line change
@@ -128,19 +128,17 @@ func (sh *Shell) Wait() {
128128
sh.HandleError(sh.wait())
129129
}
130130

131-
// Rename renames (moves) a file. It's just like os.Rename, but retries once on
132-
// error.
133-
func (sh *Shell) Rename(oldpath, newpath string) {
131+
// Move moves a file.
132+
func (sh *Shell) Move(oldpath, newpath string) {
134133
sh.Ok()
135-
sh.HandleError(sh.rename(oldpath, newpath))
134+
sh.HandleError(sh.move(oldpath, newpath))
136135
}
137136

138137
// BuildGoPkg compiles a Go package using the "go build" command and writes the
139-
// resulting binary to sh.Opts.BinDir unless the -o flag was specified.
138+
// resulting binary to sh.Opts.BinDir, or to the -o flag location if specified.
140139
// Returns the absolute path to the binary.
141-
// Included in Shell for convenience, but could have just as easily been
142-
// provided as a utility function.
143140
func (sh *Shell) BuildGoPkg(pkg string, flags ...string) string {
141+
// TODO(sadovsky): Convert BuildGoPkg into a utility function.
144142
sh.Ok()
145143
res, err := sh.buildGoPkg(pkg, flags...)
146144
sh.HandleError(err)
@@ -347,34 +345,20 @@ func (sh *Shell) wait() error {
347345
return res
348346
}
349347

350-
func (sh *Shell) rename(oldpath, newpath string) error {
351-
if err := os.Rename(oldpath, newpath); err != nil {
352-
// Concurrent, same-directory rename operations sometimes fail on certain
353-
// filesystems, so we retry once after a random backoff.
354-
time.Sleep(time.Duration(rand.Int63n(1000)) * time.Millisecond)
355-
if err := os.Rename(oldpath, newpath); err != nil {
356-
return err
357-
}
358-
}
359-
return nil
360-
}
361-
362-
func copyfile(from, to string) error {
348+
func copyFile(from, to string) error {
363349
fi, err := os.Stat(from)
364350
if err != nil {
365351
return err
366352
}
367-
mode := fi.Mode()
368353
in, err := os.Open(from)
369354
if err != nil {
370355
return err
371356
}
372357
defer in.Close()
373-
out, err := os.OpenFile(to, os.O_CREATE|os.O_RDWR|os.O_TRUNC, mode)
358+
out, err := os.OpenFile(to, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode().Perm())
374359
if err != nil {
375360
return err
376361
}
377-
defer out.Close()
378362
_, err = io.Copy(out, in)
379363
cerr := out.Close()
380364
if err != nil {
@@ -383,6 +367,24 @@ func copyfile(from, to string) error {
383367
return cerr
384368
}
385369

370+
func (sh *Shell) move(oldpath, newpath string) error {
371+
var err error
372+
if err = os.Rename(oldpath, newpath); err != nil {
373+
// Concurrent, same-directory rename operations sometimes fail on certain
374+
// filesystems, so we retry once after a random backoff.
375+
time.Sleep(time.Duration(rand.Int63n(1000)) * time.Millisecond)
376+
err = os.Rename(oldpath, newpath)
377+
}
378+
// If the error was a LinkError, try copying the file over.
379+
if _, ok := err.(*os.LinkError); !ok {
380+
return err
381+
}
382+
if err := copyFile(oldpath, newpath); err != nil {
383+
return err
384+
}
385+
return os.Remove(oldpath)
386+
}
387+
386388
func extractOutputFlag(flags ...string) (string, []string) {
387389
for i, f := range flags {
388390
if f == "-o" && len(flags) > i {
@@ -408,16 +410,14 @@ func (sh *Shell) buildGoPkg(pkg string, flags ...string) (string, error) {
408410
} else if !os.IsNotExist(err) {
409411
return "", err
410412
}
411-
// Build binary to tempBinPath, then move it to binPath.
413+
// Build binary to tempBinPath (in a fresh temporary directory), then move it
414+
// to binPath.
412415
tempDir, err := ioutil.TempDir(sh.Opts.BinDir, "")
413416
if err != nil {
414417
return "", err
415418
}
416419
defer os.RemoveAll(tempDir)
417420
tempBinPath := filepath.Join(tempDir, path.Base(pkg))
418-
if outputFlag != "" {
419-
tempBinPath = filepath.Join(tempDir, filepath.Base(binPath))
420-
}
421421
args := []string{"build", "-o", tempBinPath}
422422
args = append(args, flags...)
423423
args = append(args, pkg)
@@ -428,10 +428,7 @@ func (sh *Shell) buildGoPkg(pkg string, flags ...string) (string, error) {
428428
if err := c.run(); err != nil {
429429
return "", err
430430
}
431-
if err := sh.rename(tempBinPath, binPath); err != nil {
432-
if _, ok := err.(*os.LinkError); ok {
433-
return "", copyfile(tempBinPath, binPath)
434-
}
431+
if err := sh.move(tempBinPath, binPath); err != nil {
435432
return "", err
436433
}
437434
return binPath, nil

gosh/shell_test.go

+28-17
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ func TestPushdNoPopdCleanup(t *testing.T) {
204204
eq(t, getwdEvalSymlinks(t), startDir)
205205
}
206206

207+
// Mirrors ExampleCmd in internal/gosh_example/main.go.
207208
func TestCmd(t *testing.T) {
208209
sh := gosh.NewShell(gosh.Opts{Fatalf: makeFatalf(t), Logf: t.Logf})
209210
defer sh.Cleanup()
@@ -219,30 +220,14 @@ func TestCmd(t *testing.T) {
219220
binPath = sh.BuildGoPkg("v.io/x/lib/gosh/internal/gosh_example_client")
220221
c = sh.Cmd(binPath, "-addr="+addr)
221222
eq(t, c.Stdout(), "Hello, world!\n")
222-
223-
// Run client built using -o with absolute and relative names
224-
cwd, err := os.Getwd()
225-
if err != nil {
226-
t.Fatal(err)
227-
}
228-
absName := filepath.Join(cwd, "x")
229-
binPath = sh.BuildGoPkg("v.io/x/lib/gosh/internal/gosh_example_client", "-o", absName)
230-
defer os.Remove(absName)
231-
c = sh.Cmd(absName, "-addr="+addr)
232-
eq(t, c.Stdout(), "Hello, world!\n")
233-
234-
binPath = sh.BuildGoPkg("v.io/x/lib/gosh/internal/gosh_example_client", "-o", "y")
235-
relname := filepath.Join(sh.Opts.BinDir, "y")
236-
defer os.Remove(relname)
237-
c = sh.Cmd(relname, "-addr="+addr)
238-
eq(t, c.Stdout(), "Hello, world!\n")
239223
}
240224

241225
var (
242226
getFunc = gosh.RegisterFunc("getFunc", lib.Get)
243227
serveFunc = gosh.RegisterFunc("serveFunc", lib.Serve)
244228
)
245229

230+
// Mirrors ExampleFuncCmd in internal/gosh_example/main.go.
246231
func TestFuncCmd(t *testing.T) {
247232
sh := gosh.NewShell(gosh.Opts{Fatalf: makeFatalf(t), Logf: t.Logf})
248233
defer sh.Cleanup()
@@ -258,6 +243,32 @@ func TestFuncCmd(t *testing.T) {
258243
eq(t, c.Stdout(), "Hello, world!\n")
259244
}
260245

246+
// Tests that BuildGoPkg works when the -o flag is passed.
247+
func TestBuildGoPkg(t *testing.T) {
248+
sh := gosh.NewShell(gosh.Opts{Fatalf: makeFatalf(t), Logf: t.Logf})
249+
defer sh.Cleanup()
250+
251+
binPath := sh.BuildGoPkg("v.io/x/lib/gosh/internal/gosh_example_server")
252+
c := sh.Cmd(binPath)
253+
c.Start()
254+
addr := c.AwaitVars("addr")["addr"]
255+
neq(t, addr, "")
256+
257+
cwd, err := os.Getwd()
258+
ok(t, err)
259+
absName := filepath.Join(cwd, "x")
260+
binPath = sh.BuildGoPkg("v.io/x/lib/gosh/internal/gosh_example_client", "-o", absName)
261+
defer os.Remove(absName)
262+
c = sh.Cmd(absName, "-addr="+addr)
263+
eq(t, c.Stdout(), "Hello, world!\n")
264+
265+
relName := filepath.Join(sh.Opts.BinDir, "y")
266+
binPath = sh.BuildGoPkg("v.io/x/lib/gosh/internal/gosh_example_client", "-o", "y")
267+
defer os.Remove(relName)
268+
c = sh.Cmd(relName, "-addr="+addr)
269+
eq(t, c.Stdout(), "Hello, world!\n")
270+
}
271+
261272
var (
262273
sendVarsFunc = gosh.RegisterFunc("sendVarsFunc", func(vars map[string]string) {
263274
gosh.SendVars(vars)

0 commit comments

Comments
 (0)