mirror of https://github.com/go-python/gopy.git
188 lines
4.0 KiB
Go
188 lines
4.0 KiB
Go
// 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 bind
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"go/types"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
func isErrorType(typ types.Type) bool {
|
|
return typ == types.Universe.Lookup("error").Type()
|
|
}
|
|
|
|
func isStringer(obj types.Object) bool {
|
|
switch obj := obj.(type) {
|
|
case *types.Func:
|
|
if obj.Name() != "String" {
|
|
return false
|
|
}
|
|
sig, ok := obj.Type().(*types.Signature)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if sig.Recv() == nil {
|
|
return false
|
|
}
|
|
if sig.Params().Len() != 0 {
|
|
return false
|
|
}
|
|
res := sig.Results()
|
|
if res.Len() != 1 {
|
|
return false
|
|
}
|
|
ret := res.At(0).Type()
|
|
if ret != types.Universe.Lookup("string").Type() {
|
|
return false
|
|
}
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func hasError(sig *types.Signature) bool {
|
|
res := sig.Results()
|
|
if res == nil || res.Len() <= 0 {
|
|
return false
|
|
}
|
|
|
|
nerr := 0
|
|
for i := 0; i < res.Len(); i++ {
|
|
ret := res.At(i)
|
|
if isErrorType(ret.Type()) {
|
|
nerr++
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case nerr == 0:
|
|
return false
|
|
case nerr == 1:
|
|
return true
|
|
default:
|
|
panic(fmt.Errorf(
|
|
"gopy: invalid number of comma-errors (%d)",
|
|
nerr,
|
|
))
|
|
}
|
|
}
|
|
|
|
func isConstructor(sig *types.Signature) bool {
|
|
//TODO(sbinet)
|
|
return false
|
|
}
|
|
|
|
type pyconfig struct {
|
|
version int
|
|
cflags string
|
|
ldflags string
|
|
}
|
|
|
|
// getPythonConfig returns the needed python configuration for the given
|
|
// python VM (python, python2, python3, pypy, etc...)
|
|
func getPythonConfig(vm string) (pyconfig, error) {
|
|
code := `import sys
|
|
import distutils.sysconfig as ds
|
|
import json
|
|
print(json.dumps({
|
|
"version": sys.version_info.major,
|
|
"incdir": ds.get_python_inc(),
|
|
"libdir": ds.get_config_var("LIBDIR"),
|
|
"libpy": ds.get_config_var("LIBRARY"),
|
|
"shlibs": ds.get_config_var("SHLIBS"),
|
|
"syslibs": ds.get_config_var("SYSLIBS"),
|
|
"shlinks": ds.get_config_var("LINKFORSHARED"),
|
|
}))
|
|
`
|
|
|
|
var cfg pyconfig
|
|
bin, err := exec.LookPath(vm)
|
|
if err != nil {
|
|
return cfg, errors.Wrapf(err, "could not locate python vm %q", vm)
|
|
}
|
|
|
|
buf := new(bytes.Buffer)
|
|
cmd := exec.Command(bin, "-c", code)
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = buf
|
|
cmd.Stderr = os.Stderr
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
return cfg, errors.Wrap(err, "could not run python-config script")
|
|
}
|
|
|
|
var raw struct {
|
|
Version int `json:"version"`
|
|
IncDir string `json:"incdir"`
|
|
LibDir string `json:"libdir"`
|
|
LibPy string `json:"libpy"`
|
|
ShLibs string `json:"shlibs"`
|
|
SysLibs string `json:"syslibs"`
|
|
}
|
|
err = json.NewDecoder(buf).Decode(&raw)
|
|
if err != nil {
|
|
return cfg, errors.Wrapf(err, "could not decode JSON script output")
|
|
}
|
|
|
|
if strings.HasSuffix(raw.LibPy, ".a") {
|
|
raw.LibPy = raw.LibPy[:len(raw.LibPy)-len(".a")]
|
|
}
|
|
if strings.HasPrefix(raw.LibPy, "lib") {
|
|
raw.LibPy = raw.LibPy[len("lib"):]
|
|
}
|
|
|
|
cfg.version = raw.Version
|
|
cfg.cflags = strings.Join([]string{
|
|
"-I" + raw.IncDir,
|
|
}, " ")
|
|
cfg.ldflags = strings.Join([]string{
|
|
"-L" + raw.LibDir,
|
|
"-l" + raw.LibPy,
|
|
raw.ShLibs,
|
|
raw.SysLibs,
|
|
}, " ")
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func getGoVersion(version string) (int64, int64, error) {
|
|
version_regex := regexp.MustCompile(`^go((\d+)(\.(\d+))*)`)
|
|
match := version_regex.FindStringSubmatch(version)
|
|
if match == nil {
|
|
return -1, -1, fmt.Errorf("gopy: invalid Go version information: %q", version)
|
|
}
|
|
version_info := strings.Split(match[1], ".")
|
|
major, _ := strconv.ParseInt(version_info[0], 10, 0)
|
|
minor, _ := strconv.ParseInt(version_info[1], 10, 0)
|
|
return major, minor, nil
|
|
}
|
|
|
|
func extractPythonName(gname, gdoc string) (string, string, error) {
|
|
const PythonName = "\ngopy:name "
|
|
i := strings.Index(gdoc, PythonName)
|
|
if i < 0 {
|
|
return gname, gdoc, nil
|
|
}
|
|
s := gdoc[i+len(PythonName):]
|
|
if end := strings.Index(s, "\n"); end > 0 {
|
|
validIdPattern := regexp.MustCompile(`^[\pL_][\pL_\pN]+$`)
|
|
if !validIdPattern.MatchString(s[:end]) {
|
|
return "", "", fmt.Errorf("gopy: invalid identifier: %s", s[:end])
|
|
}
|
|
return s[:end], gdoc[:i] + s[end:], nil
|
|
}
|
|
return gname, gdoc, nil
|
|
}
|