github.com/goplus/igop@v0.17.0/gopbuild/build.go (about) 1 /* 2 * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package gopbuild 18 19 //go:generate go run ../cmd/qexp -outdir ../pkg github.com/goplus/gop/builtin 20 //go:generate go run ../cmd/qexp -outdir ../pkg github.com/goplus/gop/builtin/ng 21 //go:generate go run ../cmd/qexp -outdir ../pkg github.com/goplus/gop/builtin/iox 22 //go:generate go run ../cmd/qexp -outdir ../pkg github.com/qiniu/x/errors 23 24 import ( 25 "bytes" 26 "fmt" 27 goast "go/ast" 28 "go/types" 29 "path/filepath" 30 31 "github.com/goplus/gop/ast" 32 "github.com/goplus/gop/cl" 33 "github.com/goplus/gop/parser" 34 "github.com/goplus/gop/token" 35 "github.com/goplus/gox" 36 "github.com/goplus/igop" 37 "github.com/goplus/mod/gopmod" 38 39 _ "github.com/goplus/igop/pkg/bufio" 40 _ "github.com/goplus/igop/pkg/fmt" 41 _ "github.com/goplus/igop/pkg/github.com/goplus/gop/builtin" 42 _ "github.com/goplus/igop/pkg/github.com/goplus/gop/builtin/iox" 43 _ "github.com/goplus/igop/pkg/github.com/goplus/gop/builtin/ng" 44 _ "github.com/goplus/igop/pkg/github.com/qiniu/x/errors" 45 _ "github.com/goplus/igop/pkg/io" 46 _ "github.com/goplus/igop/pkg/log" 47 _ "github.com/goplus/igop/pkg/math/big" 48 _ "github.com/goplus/igop/pkg/math/bits" 49 _ "github.com/goplus/igop/pkg/os" 50 _ "github.com/goplus/igop/pkg/strconv" 51 _ "github.com/goplus/igop/pkg/strings" 52 ) 53 54 type Class = gopmod.Class 55 type Project = gopmod.Project 56 57 var ( 58 classfile = make(map[string]*cl.Project) 59 ) 60 61 func RegisterClassFileType(ext string, class string, works []*Class, pkgPaths ...string) { 62 cls := &cl.Project{ 63 Ext: ext, 64 Class: class, 65 Works: works, 66 PkgPaths: pkgPaths, 67 } 68 if ext != "" { 69 classfile[ext] = cls 70 } 71 for _, w := range works { 72 classfile[w.Ext] = cls 73 } 74 } 75 76 func init() { 77 igop.RegisterFileProcess(".gop", BuildFile) 78 igop.RegisterFileProcess(".gopx", BuildFile) 79 RegisterClassFileType(".gmx", "Game", []*Class{{Ext: ".spx", Class: "Sprite"}}, "github.com/goplus/spx", "math") 80 } 81 82 func BuildFile(ctx *igop.Context, filename string, src interface{}) (data []byte, err error) { 83 defer func() { 84 r := recover() 85 if r != nil { 86 err = fmt.Errorf("compile %v failed. %v", filename, r) 87 } 88 }() 89 c := NewContext(ctx) 90 pkg, err := c.ParseFile(filename, src) 91 if err != nil { 92 return nil, err 93 } 94 return pkg.ToSource() 95 } 96 97 func BuildFSDir(ctx *igop.Context, fs parser.FileSystem, dir string) (data []byte, err error) { 98 defer func() { 99 r := recover() 100 if r != nil { 101 err = fmt.Errorf("compile %v failed. %v", dir, err) 102 } 103 }() 104 c := NewContext(ctx) 105 pkg, err := c.ParseFSDir(fs, dir) 106 if err != nil { 107 return nil, err 108 } 109 return pkg.ToSource() 110 } 111 112 func BuildDir(ctx *igop.Context, dir string) (data []byte, err error) { 113 defer func() { 114 r := recover() 115 if r != nil { 116 err = fmt.Errorf("compile %v failed. %v", dir, err) 117 } 118 }() 119 c := NewContext(ctx) 120 pkg, err := c.ParseDir(dir) 121 if err != nil { 122 return nil, err 123 } 124 return pkg.ToSource() 125 } 126 127 type Package struct { 128 Fset *token.FileSet 129 Pkg *gox.Package 130 } 131 132 func (p *Package) ToSource() ([]byte, error) { 133 var buf bytes.Buffer 134 if err := gox.WriteTo(&buf, p.Pkg); err != nil { 135 return nil, err 136 } 137 return buf.Bytes(), nil 138 } 139 140 func (p *Package) ToAst() *goast.File { 141 return gox.ASTFile(p.Pkg) 142 } 143 144 type Context struct { 145 ctx *igop.Context 146 fset *token.FileSet 147 gop igop.Loader 148 } 149 150 func IsClass(ext string) (isProj bool, ok bool) { 151 if cls, ok := classfile[ext]; ok { 152 return ext == cls.Ext, true 153 } 154 if ext == ".gopx" { 155 ok = true 156 } 157 return 158 } 159 160 func NewContext(ctx *igop.Context) *Context { 161 if ctx.IsEvalMode() { 162 ctx = igop.NewContext(0) 163 } 164 return &Context{ctx: ctx, fset: token.NewFileSet(), gop: igop.NewTypesLoader(ctx, 0)} 165 } 166 167 func isGopPackage(path string) bool { 168 if pkg, ok := igop.LookupPackage(path); ok { 169 if _, ok := pkg.UntypedConsts["GopPackage"]; ok { 170 return true 171 } 172 } 173 return false 174 } 175 176 func (c *Context) Import(path string) (*types.Package, error) { 177 if isGopPackage(path) { 178 return c.gop.Import(path) 179 } 180 return c.ctx.Loader.Import(path) 181 } 182 183 func (c *Context) ParseDir(dir string) (*Package, error) { 184 pkgs, err := parser.ParseDirEx(c.fset, dir, parser.Config{ 185 IsClass: IsClass, 186 }) 187 if err != nil { 188 return nil, err 189 } 190 return c.loadPackage(dir, pkgs) 191 } 192 193 func (c *Context) ParseFSDir(fs parser.FileSystem, dir string) (*Package, error) { 194 pkgs, err := parser.ParseFSDir(c.fset, fs, dir, parser.Config{ 195 IsClass: IsClass, 196 }) 197 if err != nil { 198 return nil, err 199 } 200 return c.loadPackage(dir, pkgs) 201 } 202 203 func (c *Context) ParseFile(filename string, src interface{}) (*Package, error) { 204 srcDir, _ := filepath.Split(filename) 205 isProj, isClass := IsClass(filepath.Ext(filename)) 206 mode := parser.ParseComments 207 if isClass { 208 mode |= parser.ParseGoPlusClass 209 } 210 f, err := parser.ParseFile(c.fset, filename, src, mode) 211 if err != nil { 212 return nil, err 213 } 214 f.IsProj, f.IsClass = isProj, isClass 215 name := f.Name.Name 216 pkgs := map[string]*ast.Package{ 217 name: &ast.Package{ 218 Name: name, 219 Files: map[string]*ast.File{ 220 filename: f, 221 }, 222 }, 223 } 224 return c.loadPackage(srcDir, pkgs) 225 } 226 227 func (c *Context) loadPackage(srcDir string, pkgs map[string]*ast.Package) (*Package, error) { 228 mainPkg, ok := pkgs["main"] 229 if !ok { 230 for _, v := range pkgs { 231 mainPkg = v 232 break 233 } 234 } 235 if c.ctx.Mode&igop.DisableCustomBuiltin == 0 { 236 if f, err := igop.ParseBuiltin(c.fset, mainPkg.Name); err == nil { 237 mainPkg.GoFiles = map[string]*goast.File{"_igop_builtin.go": f} 238 } 239 } 240 conf := &cl.Config{ 241 WorkingDir: srcDir, TargetDir: srcDir, Fset: c.fset} 242 conf.Importer = c 243 conf.LookupClass = func(ext string) (c *cl.Project, ok bool) { 244 c, ok = classfile[ext] 245 return 246 } 247 if c.ctx.IsEvalMode() { 248 conf.NoSkipConstant = true 249 } 250 out, err := cl.NewPackage("", mainPkg, conf) 251 if err != nil { 252 return nil, err 253 } 254 return &Package{c.fset, out}, nil 255 }