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 }