mirror of https://github.com/go-python/gopy.git
274 lines
4.8 KiB
Go
274 lines
4.8 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 main
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/build"
|
|
"go/doc"
|
|
"go/importer"
|
|
"go/parser"
|
|
"go/scanner"
|
|
"go/token"
|
|
"go/types"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/go-python/gopy/bind"
|
|
)
|
|
|
|
var (
|
|
fset = token.NewFileSet()
|
|
)
|
|
|
|
func genPkg(odir string, p *bind.Package, lang string) error {
|
|
var err error
|
|
var o *os.File
|
|
|
|
switch lang {
|
|
case "python", "py":
|
|
lang, err = getPythonVersion()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
// no-op
|
|
}
|
|
|
|
pyvers := 2
|
|
switch lang {
|
|
case "python2", "py2":
|
|
pyvers = 2
|
|
case "python3", "py3":
|
|
pyvers = 3
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
switch lang {
|
|
case "python2", "py2":
|
|
o, err = os.Create(filepath.Join(odir, p.Name()+".c"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer o.Close()
|
|
err = bind.GenCPython(o, fset, p, 2)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
case "python3", "py3":
|
|
return fmt.Errorf("gopy: python-3 support not yet implemented")
|
|
|
|
case "go":
|
|
o, err = os.Create(filepath.Join(odir, p.Name()+".go"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer o.Close()
|
|
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bindpkg, err := build.Import("github.com/go-python/gopy/bind", cwd, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, fname := range []string{
|
|
"cgopy_seq_cpy.h",
|
|
"cgopy_seq_cpy.c",
|
|
"cgopy_seq_cpy.go",
|
|
} {
|
|
ftmpl, err := os.Open(filepath.Join(bindpkg.Dir, "_cpy", fname))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer ftmpl.Close()
|
|
|
|
fout, err := os.Create(filepath.Join(odir, fname))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fout.Close()
|
|
|
|
_, err = io.Copy(fout, ftmpl)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = fout.Close() // explicit to catch filesystem errors
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = bind.GenGo(o, fset, p, pyvers)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var tmpdir string
|
|
tmpdir, err = ioutil.TempDir("", "gopy-go-cgo-")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
hdr := filepath.Join(odir, p.Name()+".h")
|
|
cmd := exec.Command(
|
|
"go", "tool", "cgo",
|
|
"-exportheader", hdr,
|
|
o.Name(),
|
|
)
|
|
cmd.Dir = tmpdir
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
seqhdr := filepath.Join(odir, "_cgopy_seq_export.h")
|
|
cmd = exec.Command(
|
|
"go", "tool", "cgo",
|
|
"-exportheader", seqhdr,
|
|
filepath.Join(odir, "cgopy_seq_cpy.go"),
|
|
)
|
|
cmd.Dir = tmpdir
|
|
cmd.Stdin = os.Stdin
|
|
cmd.Stdout = os.Stdout
|
|
cmd.Stderr = os.Stderr
|
|
err = cmd.Run()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
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 {
|
|
log.Printf("%v\n", err)
|
|
}
|
|
} else {
|
|
log.Printf("%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 {
|
|
|
|
log.Printf("%v\n", err)
|
|
}
|
|
} else {
|
|
log.Printf("%v\n", err)
|
|
}
|
|
}
|
|
files = append(files, file)
|
|
}
|
|
|
|
return files, err
|
|
}
|
|
|
|
func newPackage(path string) (*bind.Package, error) {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
bpkg, err := build.Import(path, cwd, 0)
|
|
if err != nil {
|
|
log.Printf("error resolving import path [%s]: %v\n",
|
|
path,
|
|
err,
|
|
)
|
|
return nil, err
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
return p, err
|
|
}
|
|
|
|
func newPackageFrom(bpkg *build.Package, p *types.Package) (*bind.Package, error) {
|
|
|
|
var pkgast *ast.Package
|
|
pkgs, err := parser.ParseDir(fset, bpkg.Dir, nil, parser.ParseComments)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pkgast = pkgs[p.Name()]
|
|
if pkgast == nil {
|
|
return nil, fmt.Errorf("gopy: could not find AST for package %q", p.Name())
|
|
}
|
|
|
|
pkgdoc := doc.New(pkgast, bpkg.ImportPath, 0)
|
|
|
|
return bind.NewPackage(p, pkgdoc)
|
|
}
|