github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/packages/gopackages/main.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // The gopackages command is a diagnostic tool that demonstrates 6 // how to use github.com/powerman/golang-tools/go/packages to load, parse, 7 // type-check, and print one or more Go packages. 8 // Its precise output is unspecified and may change. 9 package main 10 11 import ( 12 "context" 13 "encoding/json" 14 "flag" 15 "fmt" 16 "go/types" 17 "os" 18 "sort" 19 "strings" 20 21 "github.com/powerman/golang-tools/go/packages" 22 "github.com/powerman/golang-tools/go/types/typeutil" 23 "github.com/powerman/golang-tools/internal/tool" 24 ) 25 26 func main() { 27 tool.Main(context.Background(), &application{Mode: "imports"}, os.Args[1:]) 28 } 29 30 type application struct { 31 // Embed the basic profiling flags supported by the tool package 32 tool.Profile 33 34 Deps bool `flag:"deps" help:"show dependencies too"` 35 Test bool `flag:"test" help:"include any tests implied by the patterns"` 36 Mode string `flag:"mode" help:"mode (one of files, imports, types, syntax, allsyntax)"` 37 Private bool `flag:"private" help:"show non-exported declarations too"` 38 PrintJSON bool `flag:"json" help:"print package in JSON form"` 39 BuildFlags stringListValue `flag:"buildflag" help:"pass argument to underlying build system (may be repeated)"` 40 } 41 42 // Name implements tool.Application returning the binary name. 43 func (app *application) Name() string { return "gopackages" } 44 45 // Usage implements tool.Application returning empty extra argument usage. 46 func (app *application) Usage() string { return "package..." } 47 48 // ShortHelp implements tool.Application returning the main binary help. 49 func (app *application) ShortHelp() string { 50 return "gopackages loads, parses, type-checks, and prints one or more Go packages." 51 } 52 53 // DetailedHelp implements tool.Application returning the main binary help. 54 func (app *application) DetailedHelp(f *flag.FlagSet) { 55 fmt.Fprint(f.Output(), ` 56 Packages are specified using the notation of "go list", 57 or other underlying build system. 58 59 Flags: 60 `) 61 f.PrintDefaults() 62 } 63 64 // Run takes the args after flag processing and performs the specified query. 65 func (app *application) Run(ctx context.Context, args ...string) error { 66 if len(args) == 0 { 67 return tool.CommandLineErrorf("not enough arguments") 68 } 69 70 // Load, parse, and type-check the packages named on the command line. 71 cfg := &packages.Config{ 72 Mode: packages.LoadSyntax, 73 Tests: app.Test, 74 BuildFlags: app.BuildFlags, 75 } 76 77 // -mode flag 78 switch strings.ToLower(app.Mode) { 79 case "files": 80 cfg.Mode = packages.LoadFiles 81 case "imports": 82 cfg.Mode = packages.LoadImports 83 case "types": 84 cfg.Mode = packages.LoadTypes 85 case "syntax": 86 cfg.Mode = packages.LoadSyntax 87 case "allsyntax": 88 cfg.Mode = packages.LoadAllSyntax 89 default: 90 return tool.CommandLineErrorf("invalid mode: %s", app.Mode) 91 } 92 93 lpkgs, err := packages.Load(cfg, args...) 94 if err != nil { 95 return err 96 } 97 98 // -deps: print dependencies too. 99 if app.Deps { 100 // We can't use packages.All because 101 // we need an ordered traversal. 102 var all []*packages.Package // postorder 103 seen := make(map[*packages.Package]bool) 104 var visit func(*packages.Package) 105 visit = func(lpkg *packages.Package) { 106 if !seen[lpkg] { 107 seen[lpkg] = true 108 109 // visit imports 110 var importPaths []string 111 for path := range lpkg.Imports { 112 importPaths = append(importPaths, path) 113 } 114 sort.Strings(importPaths) // for determinism 115 for _, path := range importPaths { 116 visit(lpkg.Imports[path]) 117 } 118 119 all = append(all, lpkg) 120 } 121 } 122 for _, lpkg := range lpkgs { 123 visit(lpkg) 124 } 125 lpkgs = all 126 } 127 128 for _, lpkg := range lpkgs { 129 app.print(lpkg) 130 } 131 return nil 132 } 133 134 func (app *application) print(lpkg *packages.Package) { 135 if app.PrintJSON { 136 data, _ := json.MarshalIndent(lpkg, "", "\t") 137 os.Stdout.Write(data) 138 return 139 } 140 // title 141 var kind string 142 // TODO(matloob): If IsTest is added back print "test command" or 143 // "test package" for packages with IsTest == true. 144 if lpkg.Name == "main" { 145 kind += "command" 146 } else { 147 kind += "package" 148 } 149 fmt.Printf("Go %s %q:\n", kind, lpkg.ID) // unique ID 150 fmt.Printf("\tpackage %s\n", lpkg.Name) 151 152 // characterize type info 153 if lpkg.Types == nil { 154 fmt.Printf("\thas no exported type info\n") 155 } else if !lpkg.Types.Complete() { 156 fmt.Printf("\thas incomplete exported type info\n") 157 } else if len(lpkg.Syntax) == 0 { 158 fmt.Printf("\thas complete exported type info\n") 159 } else { 160 fmt.Printf("\thas complete exported type info and typed ASTs\n") 161 } 162 if lpkg.Types != nil && lpkg.IllTyped && len(lpkg.Errors) == 0 { 163 fmt.Printf("\thas an error among its dependencies\n") 164 } 165 166 // source files 167 for _, src := range lpkg.GoFiles { 168 fmt.Printf("\tfile %s\n", src) 169 } 170 171 // imports 172 var lines []string 173 for importPath, imp := range lpkg.Imports { 174 var line string 175 if imp.ID == importPath { 176 line = fmt.Sprintf("\timport %q", importPath) 177 } else { 178 line = fmt.Sprintf("\timport %q => %q", importPath, imp.ID) 179 } 180 lines = append(lines, line) 181 } 182 sort.Strings(lines) 183 for _, line := range lines { 184 fmt.Println(line) 185 } 186 187 // errors 188 for _, err := range lpkg.Errors { 189 fmt.Printf("\t%s\n", err) 190 } 191 192 // package members (TypeCheck or WholeProgram mode) 193 if lpkg.Types != nil { 194 qual := types.RelativeTo(lpkg.Types) 195 scope := lpkg.Types.Scope() 196 for _, name := range scope.Names() { 197 obj := scope.Lookup(name) 198 if !obj.Exported() && !app.Private { 199 continue // skip unexported names 200 } 201 202 fmt.Printf("\t%s\n", types.ObjectString(obj, qual)) 203 if _, ok := obj.(*types.TypeName); ok { 204 for _, meth := range typeutil.IntuitiveMethodSet(obj.Type(), nil) { 205 if !meth.Obj().Exported() && !app.Private { 206 continue // skip unexported names 207 } 208 fmt.Printf("\t%s\n", types.SelectionString(meth, qual)) 209 } 210 } 211 } 212 } 213 214 fmt.Println() 215 } 216 217 // stringListValue is a flag.Value that accumulates strings. 218 // e.g. --flag=one --flag=two would produce []string{"one", "two"}. 219 type stringListValue []string 220 221 func newStringListValue(val []string, p *[]string) *stringListValue { 222 *p = val 223 return (*stringListValue)(p) 224 } 225 226 func (ss *stringListValue) Get() interface{} { return []string(*ss) } 227 228 func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) } 229 230 func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }