From 2cbcc53c540ee1d8f7373c6e5644d1ca1c8b49c0 Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 24 Apr 2024 07:55:51 +0800 Subject: [PATCH] llgo build/install --- build_install_run.go | 45 ---------- cmd/internal/build/build.go | 76 +---------------- load.go => cmd/internal/install/install.go | 24 +++--- cmd/llgo/llgo.go | 2 + gen.go | 98 ---------------------- internal/build/build_install.go | 95 +++++++++++++++++++++ 6 files changed, 115 insertions(+), 225 deletions(-) delete mode 100644 build_install_run.go rename load.go => cmd/internal/install/install.go (54%) delete mode 100644 gen.go create mode 100644 internal/build/build_install.go diff --git a/build_install_run.go b/build_install_run.go deleted file mode 100644 index 70c5dbbd..00000000 --- a/build_install_run.go +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package llgo - -import ( - "github.com/goplus/llgo/x/gocmd" - "github.com/goplus/mod/gopmod" -) - -// ----------------------------------------------------------------------------- - -// NotFound returns if cause err is ErrNotFound or not -func NotFound(err error) bool { - return gopmod.IsNotFound(err) -} - -// ----------------------------------------------------------------------------- - -func BuildDir(dir string, conf *Config, build *gocmd.BuildConfig) (err error) { - panic("todo") -} - -func BuildPkgPath(workDir, pkgPath string, conf *Config, build *gocmd.BuildConfig) (err error) { - panic("todo") -} - -func BuildFiles(files []string, conf *Config, build *gocmd.BuildConfig) (err error) { - panic("todo") -} - -// ----------------------------------------------------------------------------- diff --git a/cmd/internal/build/build.go b/cmd/internal/build/build.go index fddf7ce2..ebe5e4e2 100644 --- a/cmd/internal/build/build.go +++ b/cmd/internal/build/build.go @@ -18,88 +18,20 @@ package build import ( - "fmt" - "log" - "os" - "path/filepath" - "reflect" - - "github.com/goplus/llgo" "github.com/goplus/llgo/cmd/internal/base" - "github.com/goplus/llgo/internal/projs" - "github.com/goplus/llgo/x/gocmd" + "github.com/goplus/llgo/internal/build" ) // llgo build var Cmd = &base.Command{ - UsageLine: "llgo build [flags] [packages]", - Short: "Build Go files", + UsageLine: "llgo build [-o output] [build flags] [packages]", + Short: "Compile packages and dependencies", } -var ( - flagOutput = flag.String("o", "", "build output file") - _ = flag.Bool("v", false, "print verbose information") - flag = &Cmd.Flag -) - func init() { Cmd.Run = runCmd } func runCmd(cmd *base.Command, args []string) { - err := flag.Parse(args) - if err != nil { - log.Panicln("parse input arguments failed:", err) - } - - args = flag.Args() - if len(args) == 0 { - args = []string{"."} - } - - proj, args, err := projs.ParseOne(args...) - if err != nil { - log.Panicln(err) - } - if len(args) != 0 { - log.Panicln("too many arguments:", args) - } - - conf := &llgo.Config{} - confCmd := &gocmd.BuildConfig{} - if *flagOutput != "" { - output, err := filepath.Abs(*flagOutput) - if err != nil { - log.Panicln(err) - } - confCmd.Output = output - } - build(proj, conf, confCmd) + build.Do(args, build.ModeBuild) } - -func build(proj projs.Proj, conf *llgo.Config, build *gocmd.BuildConfig) { - var obj string - var err error - switch v := proj.(type) { - case *projs.DirProj: - obj = v.Dir - err = llgo.BuildDir(obj, conf, build) - case *projs.PkgPathProj: - obj = v.Path - err = llgo.BuildPkgPath("", obj, conf, build) - case *projs.FilesProj: - err = llgo.BuildFiles(v.Files, conf, build) - default: - log.Panicln("`llgo build` doesn't support", reflect.TypeOf(v)) - } - if llgo.NotFound(err) { - fmt.Fprintf(os.Stderr, "llgo build %v: not found\n", obj) - } else if err != nil { - fmt.Fprintln(os.Stderr, err) - } else { - return - } - os.Exit(1) -} - -// ----------------------------------------------------------------------------- diff --git a/load.go b/cmd/internal/install/install.go similarity index 54% rename from load.go rename to cmd/internal/install/install.go index 7966059e..1ae49dbf 100644 --- a/load.go +++ b/cmd/internal/install/install.go @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 The GoPlus Authors (goplus.org). All rights reserved. + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,20 +14,24 @@ * limitations under the License. */ -package llgo +// Package install implements the “llgo install command. +package install import ( - "github.com/goplus/llgo/ssa" + "github.com/goplus/llgo/cmd/internal/base" + "github.com/goplus/llgo/internal/build" ) -// ----------------------------------------------------------------------------- - -type Config struct { +// llgo install +var Cmd = &base.Command{ + UsageLine: "llgo install [build flags] [packages]", + Short: "Compile and install packages and dependencies", } -// LoadDir loads Go packages from a specified directory. -func LoadDir(dir string, conf *Config, genTestPkg, promptGen bool) (out, test *ssa.Package, err error) { - panic("todo") +func init() { + Cmd.Run = runCmd } -// ----------------------------------------------------------------------------- +func runCmd(cmd *base.Command, args []string) { + build.Do(args, build.ModeInstall) +} diff --git a/cmd/llgo/llgo.go b/cmd/llgo/llgo.go index 7e22a168..9f30a120 100644 --- a/cmd/llgo/llgo.go +++ b/cmd/llgo/llgo.go @@ -27,6 +27,7 @@ import ( "github.com/goplus/llgo/cmd/internal/base" "github.com/goplus/llgo/cmd/internal/build" "github.com/goplus/llgo/cmd/internal/help" + "github.com/goplus/llgo/cmd/internal/install" ) func mainUsage() { @@ -38,6 +39,7 @@ func init() { flag.Usage = mainUsage base.Llgo.Commands = []*base.Command{ build.Cmd, + install.Cmd, } } diff --git a/gen.go b/gen.go deleted file mode 100644 index 262f1298..00000000 --- a/gen.go +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package llgo - -/* -import ( - "fmt" - "io/fs" - "os" - "path/filepath" - "strings" - - "github.com/qiniu/x/errors" -) - -type GenFlags int - -const ( - GenFlagCheckOnly GenFlags = 1 << iota - GenFlagPrintError - GenFlagPrompt -) - -// Gen generates llgo_autogen.ll for a Go package directory. -func Gen(dir string, conf *Config, genTestPkg bool, flags GenFlags) (string, bool, error) { - recursively := strings.HasSuffix(dir, "/...") - if recursively { - dir = dir[:len(dir)-4] - } - return dir, recursively, genDir(dir, conf, genTestPkg, recursively, flags) -} - -func genDir(dir string, conf *Config, genTestPkg, recursively bool, flags GenFlags) (err error) { - if conf == nil { - conf = new(Config) - } - if recursively { - var ( - list errors.List - ) - fn := func(path string, d fs.DirEntry, err error) error { - if err == nil && d.IsDir() { - if strings.HasPrefix(d.Name(), "_") || (path != dir && hasMod(path)) { // skip _ - return filepath.SkipDir - } - if e := genGoIn(path, conf, genTestPkg, flags); e != nil && notIgnNotated(e, conf) { - if flags&GenFlagPrintError != 0 { - fmt.Fprintln(os.Stderr, e) - } - list.Add(e) - } - } - return err - } - err = filepath.WalkDir(dir, fn) - if err != nil { - return errors.NewWith(err, `filepath.WalkDir(dir, fn)`, -2, "filepath.WalkDir", dir, fn) - } - return list.ToError() - } - if e := genGoIn(dir, conf, genTestPkg, flags); e != nil && notIgnNotated(e, conf) { - if (flags & GenFlagPrintError) != 0 { - fmt.Fprintln(os.Stderr, e) - } - err = e - } - return -} - -func hasMod(dir string) bool { - _, err := os.Lstat(dir + "/go.mod") - return err == nil -} - -// GenPkgPath generates llgo_autogen.ll for a Go package. -func GenPkgPath(workDir, pkgPath string, conf *Config, allowExtern bool, flags GenFlags) (localDir string, recursively bool, err error) { - panic("todo") -} - -// GenFiles generates llgo_autogen.ll for specified Go files. -func GenFiles(autogen string, files []string, conf *Config) (outFiles []string, err error) { - panic("todo") -} -*/ diff --git a/internal/build/build_install.go b/internal/build/build_install.go new file mode 100644 index 00000000..252539ed --- /dev/null +++ b/internal/build/build_install.go @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package build + +import ( + "log" + "os" + "strings" + + "golang.org/x/tools/go/packages" + "golang.org/x/tools/go/ssa" + "golang.org/x/tools/go/ssa/ssautil" + + "github.com/goplus/llgo/cl" + llssa "github.com/goplus/llgo/ssa" +) + +type Mode int + +const ( + ModeBuild Mode = iota + ModeInstall +) + +// ----------------------------------------------------------------------------- + +const ( + loadFiles = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles + loadImports = loadFiles | packages.NeedImports + loadTypes = loadImports | packages.NeedTypes | packages.NeedTypesSizes + loadSyntax = loadTypes | packages.NeedSyntax | packages.NeedTypesInfo +) + +func Do(args []string, mode Mode) { + flags, patterns := parseArgs(args) + cfg := &packages.Config{ + Mode: loadSyntax | packages.NeedExportFile, + BuildFlags: flags, + } + + if patterns == nil { + patterns = []string{"."} + } + initial, err := packages.Load(cfg, patterns...) + check(err) + + // Create SSA-form program representation. + _, ssaPkgs := ssautil.AllPackages(initial, ssa.SanityCheckFunctions) + + llssa.Initialize(llssa.InitAll) + prog := llssa.NewProgram(nil) + for i, ssaPkg := range ssaPkgs { + pkg := initial[i] + if ssaPkg == nil { // TODO(xsw): error handling + log.Panicf("cannot build SSA for package %s", pkg) + } + ssaPkg.Build() + ret, err := cl.NewPackage(prog, ssaPkg, pkg.Syntax) + check(err) + if mode == ModeInstall { + os.WriteFile(pkg.ExportFile+".ll", []byte(ret.String()), 0644) + } + } +} + +func parseArgs(args []string) (flags, patterns []string) { + for i, arg := range args { + if !strings.HasPrefix(arg, "-") { + return args[:i], args[i:] + } + } + return args, nil +} + +func check(err error) { + if err != nil { + panic(err) + } +} + +// -----------------------------------------------------------------------------