github.com/goplus/gossa@v0.3.25/gopbuild/build.go (about)

     1  package gopbuild
     2  
     3  //go:generate go run ../cmd/qexp -outdir ../pkg github.com/goplus/gop/builtin
     4  //go:generate go run ../cmd/qexp -outdir ../pkg github.com/goplus/gop/builtin/ng
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	goast "go/ast"
    10  	"go/types"
    11  	"path/filepath"
    12  
    13  	"github.com/goplus/gop/ast"
    14  	"github.com/goplus/gop/cl"
    15  	"github.com/goplus/gop/parser"
    16  	"github.com/goplus/gop/token"
    17  	"github.com/goplus/gossa"
    18  	"github.com/goplus/gox"
    19  
    20  	_ "github.com/goplus/gossa/pkg/fmt"
    21  	_ "github.com/goplus/gossa/pkg/github.com/goplus/gop/builtin"
    22  	_ "github.com/goplus/gossa/pkg/github.com/goplus/gop/builtin/ng"
    23  	_ "github.com/goplus/gossa/pkg/math/big"
    24  	_ "github.com/goplus/gossa/pkg/strconv"
    25  	_ "github.com/goplus/gossa/pkg/strings"
    26  )
    27  
    28  var (
    29  	classfile = make(map[string]*cl.Class)
    30  )
    31  
    32  func RegisterClassFileType(projExt, workExt string, pkgPaths ...string) {
    33  	cls := &cl.Class{
    34  		ProjExt:  projExt,
    35  		WorkExt:  workExt,
    36  		PkgPaths: pkgPaths,
    37  	}
    38  	classfile[projExt] = cls
    39  	if workExt != "" {
    40  		classfile[workExt] = cls
    41  	}
    42  }
    43  
    44  func init() {
    45  	gossa.RegisterFileProcess(".gop", BuildFile)
    46  	RegisterClassFileType(".gmx", ".spx", "github.com/goplus/spx", "math")
    47  }
    48  
    49  func BuildFile(ctx *gossa.Context, filename string, src interface{}) (data []byte, err error) {
    50  	defer func() {
    51  		r := recover()
    52  		if r != nil {
    53  			err = fmt.Errorf("compile %v failed. %v", filename, r)
    54  		}
    55  	}()
    56  	c := NewContext(ctx)
    57  	fset := token.NewFileSet()
    58  	pkg, err := c.ParseFile(fset, filename, src)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	return pkg.ToSource()
    63  }
    64  
    65  func BuildFSDir(ctx *gossa.Context, fs parser.FileSystem, dir string) (data []byte, err error) {
    66  	defer func() {
    67  		r := recover()
    68  		if r != nil {
    69  			err = fmt.Errorf("compile %v failed. %v", dir, err)
    70  		}
    71  	}()
    72  	c := NewContext(ctx)
    73  	fset := token.NewFileSet()
    74  	pkg, err := c.ParseFSDir(fset, fs, dir)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return pkg.ToSource()
    79  }
    80  
    81  func BuildDir(ctx *gossa.Context, dir string) (data []byte, err error) {
    82  	defer func() {
    83  		r := recover()
    84  		if r != nil {
    85  			err = fmt.Errorf("compile %v failed. %v", dir, err)
    86  		}
    87  	}()
    88  	c := NewContext(ctx)
    89  	fset := token.NewFileSet()
    90  	pkg, err := c.ParseDir(fset, dir)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	return pkg.ToSource()
    95  }
    96  
    97  type Package struct {
    98  	Fset *token.FileSet
    99  	Pkg  *gox.Package
   100  }
   101  
   102  func (p *Package) ToSource() ([]byte, error) {
   103  	var buf bytes.Buffer
   104  	if err := gox.WriteTo(&buf, p.Pkg); err != nil {
   105  		return nil, err
   106  	}
   107  	return buf.Bytes(), nil
   108  }
   109  
   110  func (p *Package) ToAst() *goast.File {
   111  	return gox.ASTFile(p.Pkg)
   112  }
   113  
   114  type Context struct {
   115  	ctx *gossa.Context
   116  	gop gossa.Loader
   117  }
   118  
   119  func IsClass(ext string) (isProj bool, ok bool) {
   120  	if cls, ok := classfile[ext]; ok {
   121  		return ext == cls.ProjExt, true
   122  	}
   123  	return
   124  }
   125  
   126  func NewContext(ctx *gossa.Context) *Context {
   127  	return &Context{ctx: ctx, gop: gossa.NewTypesLoader(0)}
   128  }
   129  
   130  func isGopPackage(path string) bool {
   131  	if pkg, ok := gossa.LookupPackage(path); ok {
   132  		if _, ok := pkg.UntypedConsts["GopPackage"]; ok {
   133  			return true
   134  		}
   135  	}
   136  	return false
   137  }
   138  
   139  func (c *Context) Import(path string) (*types.Package, error) {
   140  	if isGopPackage(path) {
   141  		return c.gop.Import(path)
   142  	}
   143  	return c.ctx.Loader.Import(path)
   144  }
   145  
   146  func (c *Context) ParseDir(fset *token.FileSet, dir string) (*Package, error) {
   147  	pkgs, err := parser.ParseDirEx(fset, dir, parser.Config{
   148  		IsClass: IsClass,
   149  	})
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	return c.loadPackage(dir, fset, pkgs)
   154  }
   155  
   156  func (c *Context) ParseFSDir(fset *token.FileSet, fs parser.FileSystem, dir string) (*Package, error) {
   157  	pkgs, err := parser.ParseFSDir(fset, fs, dir, parser.Config{
   158  		IsClass: IsClass,
   159  	})
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	return c.loadPackage(dir, fset, pkgs)
   164  }
   165  
   166  func (c *Context) ParseFile(fset *token.FileSet, filename string, src interface{}) (*Package, error) {
   167  	srcDir, _ := filepath.Split(filename)
   168  	f, err := parser.ParseFile(fset, filename, src, 0)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	f.IsProj, f.IsClass = IsClass(filepath.Ext(filename))
   173  	name := f.Name.Name
   174  	pkgs := map[string]*ast.Package{
   175  		name: &ast.Package{
   176  			Name: name,
   177  			Files: map[string]*ast.File{
   178  				filename: f,
   179  			},
   180  		},
   181  	}
   182  	return c.loadPackage(srcDir, fset, pkgs)
   183  }
   184  
   185  func (c *Context) loadPackage(srcDir string, fset *token.FileSet, pkgs map[string]*ast.Package) (*Package, error) {
   186  	mainPkg, ok := pkgs["main"]
   187  	if !ok {
   188  		return nil, fmt.Errorf("not a main package")
   189  	}
   190  	conf := &cl.Config{
   191  		WorkingDir: srcDir, TargetDir: srcDir, Fset: fset}
   192  	conf.Importer = c
   193  	conf.LookupClass = func(ext string) (c *cl.Class, ok bool) {
   194  		c, ok = classfile[ext]
   195  		return
   196  	}
   197  	if c.ctx.IsEvalMode() {
   198  		conf.NoSkipConstant = true
   199  	}
   200  	out, err := cl.NewPackage("", mainPkg, conf)
   201  	if err != nil {
   202  		return nil, err
   203  	}
   204  	return &Package{fset, out}, nil
   205  }