github.com/joomcode/pegomock@v2.9.2-0.20220414140958-14f53b6b2a6c+incompatible/modelgen/gomock/reflect.go (about) 1 // Copyright 2012 Google Inc. 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 gomock 16 17 // This file contains the model construction by reflection. 18 19 import ( 20 "bytes" 21 "encoding/gob" 22 "flag" 23 "fmt" 24 "io" 25 "io/ioutil" 26 "os" 27 "os/exec" 28 "path/filepath" 29 "runtime" 30 "text/template" 31 32 "github.com/petergtz/pegomock/model" 33 ) 34 35 var ( 36 progOnly = flag.Bool("prog_only", false, "(reflect mode) Only generate the reflection program; write it to stdout.") 37 execOnly = flag.String("exec_only", "", "(reflect mode) If set, execute this reflection program.") 38 ) 39 40 func Reflect(importPath string, symbols []string) (*model.Package, error) { 41 // TODO: sanity check arguments 42 progPath := *execOnly 43 if *execOnly == "" { 44 workingDir, err := os.Getwd() 45 if err != nil { 46 return nil, err 47 } 48 tmpDir, err := ioutil.TempDir(workingDir, ".tmp_gomock_reflect_") 49 if err != nil { 50 return nil, err 51 } 52 defer func() { os.RemoveAll(tmpDir) }() 53 const progSource = "prog.go" 54 var progBinary = "prog.bin" 55 if runtime.GOOS == "windows" { 56 // Windows won't execute a program unless it has a ".exe" suffix. 57 progBinary += ".exe" 58 } 59 60 // Generate program. 61 var program bytes.Buffer 62 data := reflectData{ 63 ImportPath: importPath, 64 Symbols: symbols, 65 } 66 if err := reflectProgram.Execute(&program, &data); err != nil { 67 return nil, err 68 } 69 if *progOnly { 70 io.Copy(os.Stdout, &program) 71 os.Exit(0) 72 } 73 if err := ioutil.WriteFile(filepath.Join(tmpDir, progSource), program.Bytes(), 0600); err != nil { 74 return nil, err 75 } 76 77 // Build the program. 78 cmd := exec.Command("go", "build", "-o", progBinary, progSource) 79 cmd.Dir = tmpDir 80 stderr := &bytes.Buffer{} 81 cmd.Stderr = stderr 82 if err := cmd.Run(); err != nil { 83 return nil, fmt.Errorf("%v caused by:\n%v", err, stderr.String()) 84 } 85 progPath = filepath.Join(tmpDir, progBinary) 86 } 87 88 // Run it. 89 cmd := exec.Command(progPath) 90 var stdout bytes.Buffer 91 cmd.Stdout = &stdout 92 cmd.Stderr = os.Stderr 93 if err := cmd.Run(); err != nil { 94 return nil, err 95 } 96 97 // Process output. 98 var pkg model.Package 99 if err := gob.NewDecoder(&stdout).Decode(&pkg); err != nil { 100 return nil, err 101 } 102 return &pkg, nil 103 } 104 105 type reflectData struct { 106 ImportPath string 107 Symbols []string 108 } 109 110 // This program reflects on an interface value, and prints the 111 // gob encoding of a model.Package to standard output. 112 // JSON doesn't work because of the model.Type interface. 113 var reflectProgram = template.Must(template.New("program").Parse(` 114 package main 115 116 import ( 117 "encoding/gob" 118 "fmt" 119 "os" 120 "path" 121 "reflect" 122 123 "github.com/petergtz/pegomock/model" 124 "github.com/petergtz/pegomock/modelgen/gomock" 125 126 pkg_ {{printf "%q" .ImportPath}} 127 ) 128 129 func main() { 130 its := []struct{ 131 sym string 132 typ reflect.Type 133 }{ 134 {{range .Symbols}} 135 { {{printf "%q" .}}, reflect.TypeOf((*pkg_.{{.}})(nil)).Elem()}, 136 {{end}} 137 } 138 pkg := &model.Package{ 139 // NOTE: This behaves contrary to documented behaviour if the 140 // package name is not the final component of the import path. 141 // The reflect package doesn't expose the package name, though. 142 Name: path.Base({{printf "%q" .ImportPath}}), 143 } 144 145 for _, it := range its { 146 intf, err := gomock.InterfaceFromInterfaceType(it.typ) 147 if err != nil { 148 fmt.Fprintf(os.Stderr, "Reflection: %v\n", err) 149 os.Exit(1) 150 } 151 intf.Name = it.sym 152 pkg.Interfaces = append(pkg.Interfaces, intf) 153 } 154 if err := gob.NewEncoder(os.Stdout).Encode(pkg); err != nil { 155 fmt.Fprintf(os.Stderr, "gob encode: %v\n", err) 156 os.Exit(1) 157 } 158 } 159 `))