From 9d0889d18434d30997e72cd9b3e911c459bba647 Mon Sep 17 00:00:00 2001 From: Sebastien Binet Date: Fri, 30 Jan 2015 18:01:15 +0100 Subject: [PATCH] gopy-gen: first import --- LICENSE | 23 +++++++++++ README.md | 92 +++++++++++++++++++++++++++++++++++++++++++ doc.go | 26 +++++++++++++ gen.go | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.go | 88 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 343 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 doc.go create mode 100644 gen.go create mode 100644 main.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e86880c --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +Copyright ©2015 The go-python Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the gonum project nor the names of its authors and + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..81072f1 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +gopy-gen +======== + +`gopy-gen` generates a `CPython` extension module from a `go` package. + +## Installation + +```sh +$ go get github.com/go-python/gopy-gen +``` + +## Documentation + +Documentation is available on [godoc](https://godoc.org): + https://godoc.org/github.com/go-python/gopy-gen + +or directly from the command-line prompt: + +```sh +$ gopy-gen -help +gopy-gen generates Python language bindings for Go. + +Usage: + +$ gopy-gen [options] + + +For usage details, see godoc: + +$ godoc github.com/go-python/gopy-gen + -lang="python": target language for bindings + -odir="": output directory for bindings +``` + + +## Examples + +```sh +$ gopy-gen -lang=python github.com/go-python/gopy-gen/_examples/hi +$ gopy-gen -lang=go github.com/go-python/gopy-gen/_examples/hi +``` + +Have also a look at [_examples/py_hi/gen.go](_examples/py_hi/gen.go): +`gopy-gen` can be used via `go generate`. + +Running `go generate` in `_examples/py_hi/gen.go` will generate `hi.c` +and `hi.go`. + +The `py_hi` package can then be used and imported from a +`go-python`-based `main`. +See [_examples/gopy-test](_examples/gopy-test): + +```go +// a go wrapper around py-main +package main + +import ( + "os" + + "github.com/go-python/gopy-gen/_examples/py_hi" + python "github.com/sbinet/go-python" +) + +func init() { + err := python.Initialize() + if err != nil { + panic(err.Error()) + } + + py_hi.Import() +} + +func main() { + rc := python.Py_Main(os.Args) + os.Exit(rc) +} +``` + +running `gopy-test`: + +```python +>>> import hi +>>> hi.Add(1,2) +3 + +>>> hi.Hi() +hi from go + +>>> hi.Hello("you") +hello you from go + +``` diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..a7a51f2 --- /dev/null +++ b/doc.go @@ -0,0 +1,26 @@ +// Copyright 2015 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +gopy-gen generates language bindings that make it possible to call Go code +and pass objects from Java. + +Using gopy-gen + +gopy-gen takes a Go package and generates bindings for all of the exported +symbols. The exported symbols define the cross-language interface. + +The gopy-gen tool generates both an API stub in Python, and binding code in +Go. Start with a Go package: + + package hi + + import "fmt" + + func Hello(name string) { + fmt.Println("Hello, %s!\n", name) + } + +*/ +package main diff --git a/gen.go b/gen.go new file mode 100644 index 0000000..45c2c9c --- /dev/null +++ b/gen.go @@ -0,0 +1,114 @@ +// Copyright 2015 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/scanner" + "go/token" + "os" + "path/filepath" + + "github.com/go-python/py/bind" + "golang.org/x/tools/go/loader" +) + +var ( + fset = token.NewFileSet() +) + +func genPkg(odir string, pkg *build.Package) error { + var err error + var o *os.File + + files, err := parseFiles(pkg.Dir, pkg.GoFiles) + if err != nil { + return err + } + + conf := loader.Config{ + SourceImports: true, + Fset: fset, + } + conf.TypeChecker.Error = func(e error) { + errorf("%v\n", e) + err = e + } + + conf.CreateFromFiles(pkg.ImportPath, files...) + program, err := conf.Load() + if err != nil { + errorf("%v\n", err) + return err + } + p := program.Created[0].Pkg + + switch *lang { + case "python", "py": + o, err = os.Create(filepath.Join(odir, p.Name()+".c")) + if err != nil { + return err + } + err = bind.GenCPython(o, fset, p) + case "go": + o, err = os.Create(filepath.Join(odir, p.Name()+".go")) + if err != nil { + return err + } + err = bind.GenGo(o, fset, p) + default: + return fmt.Errorf("unknown target language: %q\n", *lang) + } + + if err != nil { + if list, _ := err.(bind.ErrorList); len(list) > 0 { + for _, err := range list { + errorf("%v\n", err) + } + } else { + errorf("%v\n", err) + } + } + + if err != nil { + return err + } + + err = o.Close() + if err != nil { + return err + } + + return err +} + +func parseFiles(dir string, fnames []string) ([]*ast.File, error) { + var ( + files []*ast.File + err error + ) + + for _, fname := range fnames { + path := filepath.Join(dir, fname) + file, errf := parser.ParseFile(fset, path, nil, parser.AllErrors) + if errf != nil { + err = errf + if list, _ := err.(scanner.ErrorList); len(list) > 0 { + for _, err := range list { + + errorf("%v\n", err) + } + } else { + errorf("%v\n", err) + } + } + files = append(files, file) + } + + return files, err +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..bef4377 --- /dev/null +++ b/main.go @@ -0,0 +1,88 @@ +// Copyright 2015 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "flag" + "fmt" + "go/build" + "log" + "os" + "path/filepath" +) + +var ( + lang = flag.String("lang", "python", "target language for bindings") + odir = flag.String("odir", "", "output directory for bindings") + //pkg = flag.String("pkg", "", "package name of the bindings") + + usage = `gopy-gen generates Python language bindings for Go. + +Usage: + +$ gopy-gen [options] + + +For usage details, see godoc: + +$ godoc github.com/go-python/gopy-gen +` +) + +func errorf(format string, args ...interface{}) (int, error) { + return fmt.Fprintf(os.Stderr, format, args...) +} + +func main() { + var err error + + flag.Usage = func() { + fmt.Fprintf(os.Stderr, usage) + flag.PrintDefaults() + } + + flag.Parse() + + if flag.NArg() != 1 { + log.Printf("expect a fully qualified go package name as argument\n") + flag.Usage() + os.Exit(1) + } + + cwd, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + + if *odir == "" { + *odir = cwd + } else { + err = os.MkdirAll(*odir, 0755) + if err != nil { + log.Printf("could not create output directory: %v\n", err) + os.Exit(1) + } + } + + *odir, err = filepath.Abs(*odir) + if err != nil { + log.Printf("could not infer absolute path to output directory: %v\n", err) + os.Exit(1) + } + + path := flag.Arg(0) + pkg, err := build.Import(path, cwd, 0) + if err != nil { + fmt.Fprintf(os.Stderr, "%s: %v\n", path, err) + os.Exit(1) + } + + err = genPkg(*odir, pkg) + if err != nil { + log.Fatal(err) + } + + os.Exit(0) +}