github.com/goplus/llgo@v0.8.3/internal/llgen/llgenf.go (about)

     1  /*
     2   * Copyright (c) 2024 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 llgen
    18  
    19  import (
    20  	"go/types"
    21  	"os"
    22  	"os/exec"
    23  	"path/filepath"
    24  	"strings"
    25  
    26  	"github.com/goplus/llgo/cl"
    27  	"golang.org/x/tools/go/packages"
    28  	"golang.org/x/tools/go/ssa"
    29  	"golang.org/x/tools/go/ssa/ssautil"
    30  
    31  	llssa "github.com/goplus/llgo/ssa"
    32  )
    33  
    34  const (
    35  	loadFiles   = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles
    36  	loadImports = loadFiles | packages.NeedImports
    37  	loadTypes   = loadImports | packages.NeedTypes | packages.NeedTypesSizes
    38  	loadSyntax  = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo
    39  )
    40  
    41  func initRtAndPy(prog llssa.Program, cfg *packages.Config) {
    42  	var pkgRtAndPy []*packages.Package
    43  	load := func() []*packages.Package {
    44  		if pkgRtAndPy == nil {
    45  			var err error
    46  			pkgRtAndPy, err = packages.Load(cfg, llssa.PkgRuntime, llssa.PkgPython)
    47  			check(err)
    48  		}
    49  		return pkgRtAndPy
    50  	}
    51  
    52  	prog.SetRuntime(func() *types.Package {
    53  		rt := load()
    54  		return rt[0].Types
    55  	})
    56  	prog.SetPython(func() *types.Package {
    57  		rt := load()
    58  		return rt[1].Types
    59  	})
    60  }
    61  
    62  func GenFrom(fileOrPkg string) string {
    63  	cfg := &packages.Config{
    64  		Mode: loadSyntax | packages.NeedDeps,
    65  	}
    66  	initial, err := packages.Load(cfg, fileOrPkg)
    67  	check(err)
    68  
    69  	_, pkgs := ssautil.AllPackages(initial, ssa.SanityCheckFunctions)
    70  
    71  	pkg := initial[0]
    72  	ssaPkg := pkgs[0]
    73  	ssaPkg.Build()
    74  
    75  	prog := llssa.NewProgram(nil)
    76  	initRtAndPy(prog, cfg)
    77  
    78  	if Verbose {
    79  		ssaPkg.WriteTo(os.Stderr)
    80  	}
    81  
    82  	ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax)
    83  	check(err)
    84  
    85  	if prog.NeedPyInit { // call PyInit if needed
    86  		ret.PyInit()
    87  	}
    88  
    89  	return ret.String()
    90  }
    91  
    92  func DoFile(fileOrPkg, outFile string) {
    93  	ret := GenFrom(fileOrPkg)
    94  	err := os.WriteFile(outFile, []byte(ret), 0644)
    95  	check(err)
    96  }
    97  
    98  func SmartDoFile(inFile string, pkgPath ...string) {
    99  	const autgenFile = "llgo_autogen.ll"
   100  	dir, _ := filepath.Split(inFile)
   101  	absDir, _ := filepath.Abs(dir)
   102  	absDir = filepath.ToSlash(absDir)
   103  	fname := autgenFile
   104  	if inCompilerDir(absDir) {
   105  		fname = "out.ll"
   106  	}
   107  	outFile := dir + fname
   108  
   109  	if len(pkgPath) > 0 {
   110  		Do(pkgPath[0], inFile, outFile)
   111  	} else {
   112  		DoFile(inFile, outFile)
   113  	}
   114  	if false && fname == autgenFile {
   115  		genZip(absDir, "llgo_autogen.lla", autgenFile)
   116  	}
   117  }
   118  
   119  func genZip(dir string, outFile, inFile string) {
   120  	cmd := exec.Command("zip", outFile, inFile)
   121  	cmd.Dir = dir
   122  	cmd.Stdout = os.Stdout
   123  	cmd.Stderr = os.Stderr
   124  	cmd.Run()
   125  }
   126  
   127  func inCompilerDir(dir string) bool {
   128  	return strings.Contains(dir, "/llgo/cl/")
   129  }