github.com/goplus/igop@v0.25.0/cmd/internal/export/export.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 export
    18  
    19  import (
    20  	"bytes"
    21  	"errors"
    22  	"fmt"
    23  	"go/build"
    24  	"log"
    25  	"os"
    26  	"os/exec"
    27  	"path/filepath"
    28  	"strings"
    29  
    30  	"github.com/goplus/igop/cmd/internal/base"
    31  )
    32  
    33  var (
    34  	flagExportDir      string
    35  	flagBuildContext   string
    36  	flagCustomTags     string
    37  	flagBuildTags      string
    38  	flagExportFileName string
    39  	flagExportSource   bool
    40  )
    41  
    42  func init() {
    43  	flag.StringVar(&flagExportDir, "outdir", "", "set export pkg path")
    44  	flag.StringVar(&flagBuildContext, "contexts", "", "set customer build contexts goos_goarch list. eg \"drawin_amd64 darwin_arm64\"")
    45  	flag.StringVar(&flagCustomTags, "addtags", "", "add custom tags, split by ;")
    46  	flag.StringVar(&flagBuildTags, "tags", "", "a comma-separated list of build tags")
    47  	flag.StringVar(&flagExportFileName, "filename", "export", "set export file name")
    48  	flag.BoolVar(&flagExportSource, "src", false, "export source mode")
    49  }
    50  
    51  // Cmd - igop build
    52  var Cmd = &base.Command{
    53  	UsageLine: "igop export [flags] [package]",
    54  	Short:     "export Go package to igop builtin package",
    55  }
    56  
    57  var (
    58  	flag = &Cmd.Flag
    59  )
    60  
    61  func init() {
    62  	Cmd.Run = exportCmd
    63  }
    64  
    65  func exportCmd(cmd *base.Command, args []string) {
    66  	err := flag.Parse(args)
    67  	if err != nil {
    68  		os.Exit(2)
    69  	}
    70  	args = flag.Args()
    71  	if len(args) == 0 {
    72  		cmd.Usage(os.Stderr)
    73  	}
    74  	if len(args) == 1 {
    75  		pkgs, err := parserPkgs(args[0])
    76  		if err != nil {
    77  			log.Panicln(err)
    78  		}
    79  		if len(pkgs) == 0 {
    80  			log.Panicln("not found packages")
    81  		}
    82  		args = pkgs
    83  	}
    84  	if flagExportFileName == "" {
    85  		flagExportFileName = "export"
    86  	}
    87  	ctxList := parserContextList(flagBuildContext)
    88  	Export(args, ctxList)
    89  }
    90  
    91  // pkg/...
    92  func parserPkgs(expr string) ([]string, error) {
    93  	cmds := []string{"list", "-f={{.ImportPath}}={{.Name}}", expr}
    94  	if flagBuildTags != "" {
    95  		cmds = []string{"list", "-tags", flagBuildTags, "-f={{.ImportPath}}={{.Name}}", expr}
    96  	}
    97  	data, err := runGoCommand(cmds...)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	var pkgs []string
   102  	for _, line := range strings.Split(string(data), "\n") {
   103  		pos := strings.Index(line, "=")
   104  		if pos != -1 {
   105  			pkg, name := line[:pos], line[pos+1:]
   106  			if name == "main" || isSkipPkg(pkg) {
   107  				continue
   108  			}
   109  			pkgs = append(pkgs, pkg)
   110  		}
   111  	}
   112  	return pkgs, nil
   113  }
   114  
   115  func runGoCommand(args ...string) (ret []byte, err error) {
   116  	var stdout, stderr bytes.Buffer
   117  	cmd := exec.Command("go", args...)
   118  	cmd.Stdout = &stdout
   119  	cmd.Stderr = &stderr
   120  	err = cmd.Run()
   121  	if err == nil {
   122  		ret = stdout.Bytes()
   123  	} else if stderr.Len() > 0 {
   124  		err = errors.New(stderr.String())
   125  	}
   126  	return
   127  }
   128  
   129  func isSkipPkg(pkg string) bool {
   130  	switch pkg {
   131  	case "unsafe":
   132  		return true
   133  	default:
   134  		if strings.HasPrefix(pkg, "vendor/") {
   135  			return true
   136  		}
   137  		for _, v := range strings.Split(pkg, "/") {
   138  			if v == "internal" {
   139  				return true
   140  			}
   141  		}
   142  	}
   143  	return false
   144  }
   145  
   146  func Export(pkgs []string, ctxList []*build.Context) {
   147  	log.Println("process", pkgs)
   148  	if len(ctxList) == 0 {
   149  		ExportPkgs(pkgs, nil)
   150  	} else {
   151  		for _, ctx := range ctxList {
   152  			ExportPkgs(pkgs, ctx)
   153  		}
   154  	}
   155  }
   156  
   157  func ExportPkgs(pkgs []string, ctx *build.Context) {
   158  	prog := NewProgram(ctx)
   159  	err := prog.Load(pkgs)
   160  	if err != nil {
   161  		log.Panicln(err)
   162  	}
   163  	for _, pkg := range pkgs {
   164  		if pkg == "unsafe" {
   165  			continue
   166  		}
   167  		fpath, err := ExportPkg(prog, pkg, ctx)
   168  		if err != nil {
   169  			log.Printf("export %v failed: %v\n", pkg, err)
   170  		} else {
   171  			log.Printf("export %v: %v\n", pkg, fpath)
   172  		}
   173  	}
   174  }
   175  
   176  func ExportPkg(prog *Program, pkg string, ctx *build.Context) (string, error) {
   177  	e, err := prog.ExportPkg(pkg, "q")
   178  	if err != nil {
   179  		return "", err
   180  	}
   181  	var tags []string
   182  	if flagCustomTags != "" {
   183  		tags = strings.Split(flagCustomTags, ";")
   184  	}
   185  	data, err := exportPkg(e, "q", "", tags)
   186  	if err != nil {
   187  		return "", err
   188  	}
   189  	if flagExportDir == "" {
   190  		fmt.Println(string(data))
   191  		return "stdout", nil
   192  	}
   193  	fpath := filepath.Join(flagExportDir, pkg)
   194  	var fname string
   195  	if ctx != nil {
   196  		fname = flagExportFileName + "_" + ctx.GOOS + "_" + ctx.GOARCH + ".go"
   197  	} else {
   198  		fname = flagExportFileName + ".go"
   199  	}
   200  	err = writeFile(fpath, fname, data)
   201  	return filepath.Join(fpath, fname), err
   202  }
   203  
   204  func parserContextList(list string) (ctxs []*build.Context) {
   205  	for _, info := range strings.Split(list, " ") {
   206  		info = strings.TrimSpace(info)
   207  		ar := strings.Split(info, "_")
   208  		if len(ar) != 2 {
   209  			continue
   210  		}
   211  		ctx := build.Default
   212  		ctx.GOOS = ar[0]
   213  		ctx.GOARCH = ar[1]
   214  		ctx.BuildTags = strings.Split(flagBuildTags, ",")
   215  		ctxs = append(ctxs, &ctx)
   216  	}
   217  	return
   218  }