diff --git a/mockgen/reflect.go b/mockgen/reflect.go index b0988679..915f1331 100644 --- a/mockgen/reflect.go +++ b/mockgen/reflect.go @@ -20,7 +20,7 @@ import ( "bytes" "encoding/gob" "flag" - "io" + "go/build" "io/ioutil" "os" "os/exec" @@ -32,69 +32,27 @@ import ( ) var ( - progOnly = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout.") + progOnly = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout and exit.") execOnly = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.") buildFlags = flag.String("build_flags", "", "(reflect mode) Additional flags for go build.") ) -func Reflect(importPath string, symbols []string) (*model.Package, error) { - // TODO: sanity check arguments - - progPath := *execOnly - if *execOnly == "" { - pwd, _ := os.Getwd() - // We use TempDir instead of TempFile so we can control the filename. - // Try to place the TempDir under pwd, so that if there is some package in - // vendor directory, 'go build' can also load/mock it. - tmpDir, err := ioutil.TempDir(pwd, "gomock_reflect_") - if err != nil { - return nil, err - } - defer func() { os.RemoveAll(tmpDir) }() - const progSource = "prog.go" - var progBinary = "prog.bin" - if runtime.GOOS == "windows" { - // Windows won't execute a program unless it has a ".exe" suffix. - progBinary += ".exe" - } - - // Generate program. - var program bytes.Buffer - data := reflectData{ - ImportPath: importPath, - Symbols: symbols, - } - if err := reflectProgram.Execute(&program, &data); err != nil { - return nil, err - } - if *progOnly { - io.Copy(os.Stdout, &program) - os.Exit(0) - } - if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program.Bytes(), 0600); err != nil { - return nil, err - } - - cmdArgs := []string{} - cmdArgs = append(cmdArgs, "build") - if *buildFlags != "" { - cmdArgs = append(cmdArgs, *buildFlags) - } - cmdArgs = append(cmdArgs, "-o", progBinary, progSource) - - // Build the program. - cmd := exec.Command("go", cmdArgs...) - cmd.Dir = tmpDir - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - if err := cmd.Run(); err != nil { - return nil, err - } - progPath = filepath.Join(tmpDir, progBinary) +func writeProgram(importPath string, symbols []string) ([]byte, error) { + var program bytes.Buffer + data := reflectData{ + ImportPath: importPath, + Symbols: symbols, + } + if err := reflectProgram.Execute(&program, &data); err != nil { + return nil, err } + return program.Bytes(), nil +} - // Run it. - cmd := exec.Command(progPath) +// run the given command and parse the output as a model.Package. +func run(command string) (*model.Package, error) { + // Run the program. + cmd := exec.Command(command) var stdout bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = os.Stderr @@ -110,6 +68,79 @@ func Reflect(importPath string, symbols []string) (*model.Package, error) { return &pkg, nil } +// runInDir writes the given program into the given dir, runs it there, and +// parses the output as a model.Package. +func runInDir(program []byte, dir string) (*model.Package, error) { + // We use TempDir instead of TempFile so we can control the filename. + tmpDir, err := ioutil.TempDir(dir, "gomock_reflect_") + if err != nil { + return nil, err + } + defer func() { os.RemoveAll(tmpDir) }() + const progSource = "prog.go" + var progBinary = "prog.bin" + if runtime.GOOS == "windows" { + // Windows won't execute a program unless it has a ".exe" suffix. + progBinary += ".exe" + } + + if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program, 0600); err != nil { + return nil, err + } + + cmdArgs := []string{} + cmdArgs = append(cmdArgs, "build") + if *buildFlags != "" { + cmdArgs = append(cmdArgs, *buildFlags) + } + cmdArgs = append(cmdArgs, "-o", progBinary, progSource) + + // Build the program. + cmd := exec.Command("go", cmdArgs...) + cmd.Dir = tmpDir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return nil, err + } + return run(filepath.Join(tmpDir, progBinary)) +} + +func Reflect(importPath string, symbols []string) (*model.Package, error) { + // TODO: sanity check arguments + + if *execOnly != "" { + return run(*execOnly) + } + + program, err := writeProgram(importPath, symbols) + if err != nil { + return nil, err + } + + if *progOnly { + os.Stdout.Write(program) + os.Exit(0) + } + + wd, _ := os.Getwd() + + // Try to run the program in the same directory as the input package. + if p, err := build.Import(importPath, wd, build.FindOnly); err == nil { + dir := p.Dir + if p, err := runInDir(program, dir); err == nil { + return p, nil + } + } + + // Since that didn't work, try to run it in the current working directory. + if p, err := runInDir(program, wd); err == nil { + return p, nil + } + // Since that didn't work, try to run it in a standard temp directory. + return runInDir(program, "") +} + type reflectData struct { ImportPath string Symbols []string diff --git a/mockgen/tests/internal_pkg/generate.go b/mockgen/tests/internal_pkg/generate.go new file mode 100644 index 00000000..541ed647 --- /dev/null +++ b/mockgen/tests/internal_pkg/generate.go @@ -0,0 +1,3 @@ +//go:generate mockgen -destination subdir/internal/pkg/reflect_output/mock.go github.com/golang/mock/mockgen/tests/internal_pkg/subdir/internal/pkg Intf +//go:generate mockgen -source subdir/internal/pkg/input.go -destination subdir/internal/pkg/source_output/mock.go +package test diff --git a/mockgen/tests/internal_pkg/subdir/internal/pkg/input.go b/mockgen/tests/internal_pkg/subdir/internal/pkg/input.go new file mode 100644 index 00000000..d05f6a7f --- /dev/null +++ b/mockgen/tests/internal_pkg/subdir/internal/pkg/input.go @@ -0,0 +1,9 @@ +package pkg + +type Arg interface { + Foo() int +} + +type Intf interface { + F() Arg +} diff --git a/mockgen/tests/internal_pkg/subdir/internal/pkg/reflect_output/mock.go b/mockgen/tests/internal_pkg/subdir/internal/pkg/reflect_output/mock.go new file mode 100644 index 00000000..47ccf0c5 --- /dev/null +++ b/mockgen/tests/internal_pkg/subdir/internal/pkg/reflect_output/mock.go @@ -0,0 +1,46 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/golang/mock/mockgen/tests/internal_pkg/subdir/internal/pkg (interfaces: Intf) + +// Package mock_pkg is a generated GoMock package. +package mock_pkg + +import ( + gomock "github.com/golang/mock/gomock" + pkg "github.com/golang/mock/mockgen/tests/internal_pkg/subdir/internal/pkg" + reflect "reflect" +) + +// MockIntf is a mock of Intf interface +type MockIntf struct { + ctrl *gomock.Controller + recorder *MockIntfMockRecorder +} + +// MockIntfMockRecorder is the mock recorder for MockIntf +type MockIntfMockRecorder struct { + mock *MockIntf +} + +// NewMockIntf creates a new mock instance +func NewMockIntf(ctrl *gomock.Controller) *MockIntf { + mock := &MockIntf{ctrl: ctrl} + mock.recorder = &MockIntfMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockIntf) EXPECT() *MockIntfMockRecorder { + return m.recorder +} + +// F mocks base method +func (m *MockIntf) F() pkg.Arg { + ret := m.ctrl.Call(m, "F") + ret0, _ := ret[0].(pkg.Arg) + return ret0 +} + +// F indicates an expected call of F +func (mr *MockIntfMockRecorder) F() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "F", reflect.TypeOf((*MockIntf)(nil).F)) +} diff --git a/mockgen/tests/internal_pkg/subdir/internal/pkg/source_output/mock.go b/mockgen/tests/internal_pkg/subdir/internal/pkg/source_output/mock.go new file mode 100644 index 00000000..32595ab1 --- /dev/null +++ b/mockgen/tests/internal_pkg/subdir/internal/pkg/source_output/mock.go @@ -0,0 +1,81 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: subdir/internal/pkg/input.go + +// Package mock_pkg is a generated GoMock package. +package mock_pkg + +import ( + gomock "github.com/golang/mock/gomock" + pkg "github.com/golang/mock/mockgen/tests/internal_pkg/subdir/internal/pkg" + reflect "reflect" +) + +// MockArg is a mock of Arg interface +type MockArg struct { + ctrl *gomock.Controller + recorder *MockArgMockRecorder +} + +// MockArgMockRecorder is the mock recorder for MockArg +type MockArgMockRecorder struct { + mock *MockArg +} + +// NewMockArg creates a new mock instance +func NewMockArg(ctrl *gomock.Controller) *MockArg { + mock := &MockArg{ctrl: ctrl} + mock.recorder = &MockArgMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockArg) EXPECT() *MockArgMockRecorder { + return m.recorder +} + +// Foo mocks base method +func (m *MockArg) Foo() int { + ret := m.ctrl.Call(m, "Foo") + ret0, _ := ret[0].(int) + return ret0 +} + +// Foo indicates an expected call of Foo +func (mr *MockArgMockRecorder) Foo() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Foo", reflect.TypeOf((*MockArg)(nil).Foo)) +} + +// MockIntf is a mock of Intf interface +type MockIntf struct { + ctrl *gomock.Controller + recorder *MockIntfMockRecorder +} + +// MockIntfMockRecorder is the mock recorder for MockIntf +type MockIntfMockRecorder struct { + mock *MockIntf +} + +// NewMockIntf creates a new mock instance +func NewMockIntf(ctrl *gomock.Controller) *MockIntf { + mock := &MockIntf{ctrl: ctrl} + mock.recorder = &MockIntfMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockIntf) EXPECT() *MockIntfMockRecorder { + return m.recorder +} + +// F mocks base method +func (m *MockIntf) F() pkg.Arg { + ret := m.ctrl.Call(m, "F") + ret0, _ := ret[0].(pkg.Arg) + return ret0 +} + +// F indicates an expected call of F +func (mr *MockIntfMockRecorder) F() *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "F", reflect.TypeOf((*MockIntf)(nil).F)) +} diff --git a/mockgen/tests/vendor_pkg/doc.go b/mockgen/tests/vendor_pkg/doc.go index ee7609a5..b5bde11c 100644 --- a/mockgen/tests/vendor_pkg/doc.go +++ b/mockgen/tests/vendor_pkg/doc.go @@ -1,3 +1,3 @@ package vendor_pkg -//go:generate mockgen a Ifc +//go:generate mockgen -destination mock.go -package vendor_pkg a Ifc diff --git a/mockgen/tests/vendor_pkg/mock.go b/mockgen/tests/vendor_pkg/mock.go new file mode 100644 index 00000000..e5e78e51 --- /dev/null +++ b/mockgen/tests/vendor_pkg/mock.go @@ -0,0 +1,99 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: a (interfaces: Ifc) + +// Package vendor_pkg is a generated GoMock package. +package vendor_pkg + +import ( + gomock "github.com/golang/mock/gomock" + reflect "reflect" +) + +// MockIfc is a mock of Ifc interface +type MockIfc struct { + ctrl *gomock.Controller + recorder *MockIfcMockRecorder +} + +// MockIfcMockRecorder is the mock recorder for MockIfc +type MockIfcMockRecorder struct { + mock *MockIfc +} + +// NewMockIfc creates a new mock instance +func NewMockIfc(ctrl *gomock.Controller) *MockIfc { + mock := &MockIfc{ctrl: ctrl} + mock.recorder = &MockIfcMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use +func (m *MockIfc) EXPECT() *MockIfcMockRecorder { + return m.recorder +} + +// A mocks base method +func (m *MockIfc) A(arg0 string) string { + ret := m.ctrl.Call(m, "A", arg0) + ret0, _ := ret[0].(string) + return ret0 +} + +// A indicates an expected call of A +func (mr *MockIfcMockRecorder) A(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "A", reflect.TypeOf((*MockIfc)(nil).A), arg0) +} + +// B mocks base method +func (m *MockIfc) B(arg0 int) int { + ret := m.ctrl.Call(m, "B", arg0) + ret0, _ := ret[0].(int) + return ret0 +} + +// B indicates an expected call of B +func (mr *MockIfcMockRecorder) B(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "B", reflect.TypeOf((*MockIfc)(nil).B), arg0) +} + +// C mocks base method +func (m *MockIfc) C(arg0 chan int) chan int { + ret := m.ctrl.Call(m, "C", arg0) + ret0, _ := ret[0].(chan int) + return ret0 +} + +// C indicates an expected call of C +func (mr *MockIfcMockRecorder) C(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "C", reflect.TypeOf((*MockIfc)(nil).C), arg0) +} + +// D mocks base method +func (m *MockIfc) D(arg0 interface{}) { + m.ctrl.Call(m, "D", arg0) +} + +// D indicates an expected call of D +func (mr *MockIfcMockRecorder) D(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "D", reflect.TypeOf((*MockIfc)(nil).D), arg0) +} + +// E mocks base method +func (m *MockIfc) E(arg0 map[string]interface{}) { + m.ctrl.Call(m, "E", arg0) +} + +// E indicates an expected call of E +func (mr *MockIfcMockRecorder) E(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "E", reflect.TypeOf((*MockIfc)(nil).E), arg0) +} + +// F mocks base method +func (m *MockIfc) F(arg0 []float64) { + m.ctrl.Call(m, "F", arg0) +} + +// F indicates an expected call of F +func (mr *MockIfcMockRecorder) F(arg0 interface{}) *gomock.Call { + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "F", reflect.TypeOf((*MockIfc)(nil).F), arg0) +}