gopy: load from export data, not source

This CL, inspired from https://golang.org/cl/16911, drops the use of
`x/tools/go/loader` (importing from sources) and replaces it with the stdlib
`go/importer`, working off the (binary) exported data under
$GOPATH/pkg/pkgname.a.

This allows us:
- to rely completely on the stdlib-1.5,
- to support cgo-based packages (which are not supported by `x/tools/go/loader`)

Fixes #28
Fixes #36
Fixes #68
Fixes #70

Change-Id: I0321645dfeec8738687a675a7e80283053c89123
This commit is contained in:
Sebastien Binet 2015-11-16 13:00:54 +01:00
parent 6403bdc659
commit 90f5fcd36e
4 changed files with 97 additions and 23 deletions

42
_examples/cgo/cgo.go Normal file
View File

@ -0,0 +1,42 @@
// 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 cgo tests bindings of CGo-based packages.
package cgo
//#include <stdio.h>
//#include <string.h>
//#include <stdlib.h>
//const char* cpkg_sprintf(const char *str) {
// char *o = (char*)malloc(strlen(str));
// sprintf(o, "%s", str);
// return o;
//}
import "C"
import (
"fmt"
"unsafe"
)
// Hi returns a string from Go (via C's stdio)
func Hi() string {
cstr := C.CString("hi from go\n")
defer C.free(unsafe.Pointer(cstr))
cout := C.cpkg_sprintf(cstr)
defer C.free(unsafe.Pointer(cout))
return C.GoString(cout)
}
// Hello returns a string via C's stdio
func Hello(s string) string {
if s == "" {
s = "you"
}
cstr := C.CString(fmt.Sprintf("hello %s from go\n", s))
defer C.free(unsafe.Pointer(cstr))
cout := C.cpkg_sprintf(cstr)
defer C.free(unsafe.Pointer(cout))
return C.GoString(cout)
}

12
_examples/cgo/test.py Normal file
View File

@ -0,0 +1,12 @@
# 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.
## py2/py3 compat
from __future__ import print_function
import cgo
print("cgo.doc: %r" % (cgo.__doc__,))
print("cgo.Hi()= %r" % (cgo.Hi(),))
print("cgo.Hello(you)= %r" % (cgo.Hello("you"),))

55
gen.go
View File

@ -9,6 +9,7 @@ import (
"go/ast"
"go/build"
"go/doc"
"go/importer"
"go/parser"
"go/scanner"
"go/token"
@ -20,7 +21,6 @@ import (
"path/filepath"
"github.com/go-python/gopy/bind"
"golang.org/x/tools/go/loader"
)
var (
@ -160,24 +160,40 @@ func newPackage(path string) (*bind.Package, error) {
return nil, err
}
pkg, err := build.Import(path, cwd, 0)
pkgfiles := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
pkgfiles = append(pkgfiles, pkg.GoFiles...)
pkgfiles = append(pkgfiles, pkg.CgoFiles...)
files, err := parseFiles(pkg.Dir, pkgfiles)
cmd := exec.Command("go", "install", path)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = cwd
err = cmd.Run()
if err != nil {
log.Printf("error installing [%s]: %v\n",
path,
err,
)
return nil, err
}
conf := loader.Config{
Fset: fset,
}
conf.TypeChecker.Error = func(e error) {
log.Printf("%v\n", e)
err = e
bpkg, err := build.Import(path, cwd, 0)
if err != nil {
log.Printf("error resolving import path [%s]: %v\n",
path,
err,
)
return nil, err
}
p, err := newPackageFrom(files, &conf, pkg)
pkg, err := importer.Default().Import(bpkg.ImportPath)
if err != nil {
log.Printf("error importing package [%v]: %v\n",
bpkg.ImportPath,
err,
)
return nil, err
}
p, err := newPackageFrom(bpkg, pkg)
if err != nil {
log.Printf("%v\n", err)
return nil, err
@ -186,17 +202,10 @@ func newPackage(path string) (*bind.Package, error) {
return p, err
}
func newPackageFrom(files []*ast.File, conf *loader.Config, pkg *build.Package) (*bind.Package, error) {
conf.CreateFromFiles(pkg.ImportPath, files...)
program, err := conf.Load()
if err != nil {
return nil, err
}
p := program.Created[0].Pkg
func newPackageFrom(bpkg *build.Package, p *types.Package) (*bind.Package, error) {
var pkgast *ast.Package
pkgs, err := parser.ParseDir(fset, pkg.Dir, nil, parser.ParseComments)
pkgs, err := parser.ParseDir(fset, bpkg.Dir, nil, parser.ParseComments)
if err != nil {
return nil, err
}
@ -205,7 +214,7 @@ func newPackageFrom(files []*ast.File, conf *loader.Config, pkg *build.Package)
return nil, fmt.Errorf("gopy: could not find AST for package %q", p.Name())
}
pkgdoc := doc.New(pkgast, pkg.ImportPath, 0)
pkgdoc := doc.New(pkgast, bpkg.ImportPath, 0)
return bind.NewPackage(p, pkgdoc)
}

View File

@ -453,3 +453,14 @@ func TestBindInterfaces(t *testing.T) {
`),
})
}
func TestBindCgoPackage(t *testing.T) {
t.Parallel()
testPkg(t, pkg{
path: "_examples/cgo",
want: []byte(`cgo.doc: 'Package cgo tests bindings of CGo-based packages.\n'
cgo.Hi()= 'hi from go\n'
cgo.Hello(you)= 'hello you from go\n'
`),
})
}