github.com/rgonomic/rgo@v0.2.2-0.20220708095818-4747f0905d6e/internal/rgo/info.go (about) 1 // Copyright ©2019 The rgonomic Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Copyright 2019 The Go Authors. All rights reserved. 6 // Use of this source code is governed by a BSD-style 7 // license that can be found in the LICENSE file. 8 9 package rgo 10 11 import ( 12 "context" 13 "flag" 14 "fmt" 15 "io" 16 "os" 17 "runtime/debug" 18 ) 19 20 // version implements the version command. 21 type version struct { 22 app *Application 23 } 24 25 func (v *version) Name() string { return "version" } 26 func (v *version) Usage() string { return "" } 27 func (v *version) ShortHelp() string { return "print the rgo version information" } 28 func (v *version) DetailedHelp(f *flag.FlagSet) { 29 fmt.Fprint(f.Output(), ``) 30 f.PrintDefaults() 31 } 32 33 // Run prints rgo version information. 34 func (v *version) Run(ctx context.Context, args ...string) error { 35 printBuildInfo(os.Stdout, v.app.Verbose) 36 return nil 37 } 38 39 func printBuildInfo(w io.Writer, verbose bool) { 40 if info, ok := debug.ReadBuildInfo(); ok { 41 fmt.Fprintf(w, "%v %v\n", info.Path, info.Main.Version) 42 if verbose { 43 for _, dep := range info.Deps { 44 printModuleInfo(w, dep) 45 } 46 } 47 } else { 48 fmt.Fprintf(w, "version unknown, built in $GOPATH mode\n") 49 } 50 } 51 52 func printModuleInfo(w io.Writer, m *debug.Module) { 53 fmt.Fprintf(w, " %s@%s", m.Path, m.Version) 54 if m.Sum != "" { 55 fmt.Fprintf(w, " %s", m.Sum) 56 } 57 if m.Replace != nil { 58 fmt.Fprintf(w, " => %v", m.Replace.Path) 59 } 60 fmt.Fprintf(w, "\n") 61 } 62 63 // help implements the help command. 64 type help struct{} 65 66 func (*help) Name() string { return "help" } 67 func (*help) Usage() string { return "" } 68 func (*help) ShortHelp() string { return "output rgo help information" } 69 func (*help) DetailedHelp(f *flag.FlagSet) { 70 fmt.Fprint(f.Output(), ``) 71 f.PrintDefaults() 72 } 73 74 // Run outputs the help text. 75 func (*help) Run(ctx context.Context, args ...string) error { 76 fmt.Fprintf(os.Stdout, "%s", helpText) 77 return nil 78 } 79 80 // Keep this reasonably in sync with the README.md. 81 const helpText = ` 82 Workflow 83 84 Initialize the go package module. 85 86 $ go mod init example.org/path/to/module 87 88 Specify dependency versions using the go tool if needed. 89 90 $ go get example.org/path/to/dependency@vX.Y.Z 91 92 Set up the rgo default configurations and files. 93 94 $ rgo init example.org/path/to/pkg 95 96 Edit the configuration file, rgo.json, if needed. The rgo.json file 97 corresponds to the following Go struct. 98 99 type Config struct { 100 // PkgPath is the package import path for the package 101 // to be wrapped by rgo. It depends on the go.mod file 102 // at the root of the destination rgo package. 103 PkgPath string 104 105 // AllowedFuncs is a pattern matching names of 106 // functions that may be wrapped. If AllowedFuncs 107 // is empty all wrappable functions are wrapped. 108 AllowedFuncs string 109 110 // Words is a set of known words that can be provided 111 // to ensure camel-case to snake case breaks words 112 // correctly. If words is nil, "NaN" and "NA" are 113 // used. Set words to []string{} to provide an empty 114 // set of words. 115 Words []string 116 117 // LicenseDir is the directory to put license files 118 // when more than one license exists. 119 LicenseDir string 120 121 // LicensePattern is the pattern for license file 122 // names to check. The pattern is used with the 123 // case-insensitive flag. 124 LicensePattern string 125 } 126 127 Generate the wrapper code, documentation and other associated files. 128 129 $ rgo build 130 131 Make necessary changes to the DESCRIPTION file. 132 If the R package is intended to be packaged license information for 133 dependencies of the Go code will need to be included since Go links 134 statically and the generated .so lib file will be part of the 135 distribution. rgo build will collect all the licenses that it finds in 136 the source module and place them in the LicenseDir directory. You should 137 remove any that are not relevant to the package you are wrapping. 138 139 Then package into a bundle for distribution and build and install the R 140 package. 141 142 $ R CMD INSTALL . 143 144 145 Type mappings 146 147 rgo has builtin type mappings between Go and R types. See the project 148 README for the details of these. 149 150 151 Go struct tags 152 153 Go struct tags with the name rgo may be used to change the R value's 154 name mapping. For example, 155 156 type GoType struct { 157 Count int ` + "`rgo:\"number\"`" + ` 158 } 159 160 will correspond to an R list with a single named element "number". 161 162 163 Multiple return values 164 165 Go functions returning multiple values will have these values packaged 166 into a list with elements named for the return values in the case of Go 167 functions named returns, or 'r<n>' for unnamed returns where '<n>' is 168 the index of the return value. 169 170 171 Panics 172 173 Go panics are recovered and result in an R error call. 174 175 176 Limitations 177 178 R and Go have differences in indexing; R is one-based and Go is 179 zero-based. This means that care needs to be taken when using indexes 180 generated in the other environment. 181 182 R lacks 64-bit integers, so rgo will refuse to wrap functions that have 183 64-bit integer inputs or results (int64 and uint64). It also refuses to 184 wrap function that take or return uintptr values. On Go architectures 185 with 64-bit int and uint types, results are truncated to 32 bits. This 186 behaviour will not change until R gets 64-bit integer types. 187 188 R Matrix values are not currently handled and will need to be 189 destructured to a vector and a pair of dimensions (see the example in 190 examples/cca in the project repo for how to do this). 191 192 Currently the extraction of type identities is weaker than it should be. 193 This will be improved. 194 195 Data exchange between R and Go depends on Cgo calls and so is not free. 196 The exact performance impact depends on the type due to R's baroque type 197 system and its implementation; briefly though, R vectors that have a 198 direct correspondence with Go scalar types or slices will perform the 199 best (integer and int32/uint32, double and float64, complex and 200 complex128, and raw and int8/uint8). To check the likely performance of 201 data exchange, look at the generated Go code in the src/rgo directory of 202 the package you are building. The generated code is intended to be 203 reasonably human readable. 204 205 206 Input parameter mutation 207 208 For types that have direct memory layout equivalents between Go and R 209 (raw and []int8/[]uint8, integer and []int32/[]uint32, double/float64, 210 and complex and []complex128) the vector is passed directly to Go. This 211 means that the Go code can mutate elements. This needs to be considered 212 when writing Go code that works on slices to avoid unwanted mutation of 213 R values that are passed to Go. It can also be used for allocation free 214 work on R vectors. Values passed back to R from Go are copied to satisfy 215 Go's runtime restrictions on pointer passing. 216 `