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 }