Skip to content

Commit

Permalink
x/vgo: add deplist command to get build information about (test) deps
Browse files Browse the repository at this point in the history
This CL is a work in progress for golang/go#24661. Following the pattern laid
down by the vet command, we want to pass the build details of the
transitive (test) deps of a list of packages onto vetters/linters etc
that need to load imports of said packages for go/types (and similar)
analysis.

Fixes golang/go#24661

Change-Id: If1496dd6a3ed501ad6f124226a05f5d57284c57d
  • Loading branch information
myitcv committed May 20, 2018
1 parent b39cea3 commit c77fb7d
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 0 deletions.
100 changes: 100 additions & 0 deletions vendor/cmd/go/internal/deplist/deplist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package deplist implements the ``go deplist'' command.
package deplist

import (
"cmd/go/internal/base"
"cmd/go/internal/load"
"cmd/go/internal/work"
)

var CmdDeplist = &base.Command{
Run: runDeplist,
CustomFlags: true,
UsageLine: "deplist [-n] [-x] [build flags] [deplist flags] [packages]",
Short: "provide JSON package build information for dependencies of packages",
Long: `
Deplist provide JSON package build information for dependencies of packages
`,
}

func runDeplist(cmd *base.Command, args []string) {
deplistFlags, pkgArgs := deplistFlags(args)

work.BuildInit()

test := false
build := false

for _, v := range deplistFlags {
switch v {
case "-test":
test = true
case "-build":
build = true
}
}

if !build {
base.Fatalf("don't yet know what to do without -build flag")
}

deps := make(map[string]bool)
var testDeps []string

pkgs := load.PackagesAndErrors(pkgArgs)
if len(pkgs) == 0 {
base.Fatalf("no packages to deplist")
}

for _, p := range pkgs {
for _, d := range p.Deps {
deps[d] = true
}

if test {
for _, d := range p.TestImports {
testDeps = append(testDeps, d)
}
for _, d := range p.XTestImports {
testDeps = append(testDeps, d)
}
}
}

var testPkgArgs []string

for _, d := range testDeps {
if !deps[d] {
testPkgArgs = append(testPkgArgs, d)
}
}

if len(testPkgArgs) > 0 {
for _, p := range load.PackagesAndErrors(testPkgArgs) {
deps[p.ImportPath] = true
for _, d := range p.Deps {
deps[d] = true
}
}
}

var uniqDeps []string
for p := range deps {
uniqDeps = append(uniqDeps, p)
}

depPkgs := load.PackagesForBuild(uniqDeps)

var b work.Builder
b.Init()

root := &work.Action{Mode: "go deplist"}
for _, p := range depPkgs {
root.Deps = append(root.Deps, b.DeplistAction(work.ModeBuild, work.ModeBuild, p))
}
b.Do(root)
}
64 changes: 64 additions & 0 deletions vendor/cmd/go/internal/deplist/deplistFlag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package deplist

import (
"flag"
"fmt"
"os"
"strings"

"cmd/go/internal/base"
"cmd/go/internal/cmdflag"
"cmd/go/internal/work"
)

const cmd = "deplist"

// deplistFlagDefn is the set of flags we process.
var deplistFlagDefn = []*cmdflag.Defn{
{Name: "test", BoolVar: new(bool)},
{Name: "build", BoolVar: new(bool)},
}

var deplistTool string

// add build flags to deplistFlagDefn.
func init() {
var cmd base.Command
work.AddBuildFlags(&cmd)
cmd.Flag.VisitAll(func(f *flag.Flag) {
deplistFlagDefn = append(deplistFlagDefn, &cmdflag.Defn{
Name: f.Name,
Value: f.Value,
})
})
}

// deplistFlags processes the command line, splitting it at the first non-flag
// into the list of flags and list of packages.
func deplistFlags(args []string) (passToDeplist, packageNames []string) {
for i := 0; i < len(args); i++ {
if !strings.HasPrefix(args[i], "-") {
return args[:i], args[i:]
}

f, value, extraWord := cmdflag.Parse(cmd, deplistFlagDefn, args, i)
if f == nil {
fmt.Fprintf(os.Stderr, "deplist: flag %q not defined\n", args[i])
fmt.Fprintf(os.Stderr, "Run \"go help deplist\" for more information\n")
os.Exit(2)
}
if f.Value != nil {
if err := f.Value.Set(value); err != nil {
base.Fatalf("invalid flag argument for -%s: %v", f.Name, err)
}
}
if extraWord {
i++
}
}
return args, nil
}
26 changes: 26 additions & 0 deletions vendor/cmd/go/internal/work/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,32 @@ func (b *Builder) VetAction(mode, depMode BuildMode, p *load.Package) *Action {
return a
}

// DeplistAction returns the action for running go deplist on package p.
// It depends on the action for compiling p.
// If the caller may be causing p to be installed, it is up to the caller
// to make sure that the install depends on (runs after) deplist.
func (b *Builder) DeplistAction(mode, depMode BuildMode, p *load.Package) *Action {
// Construct deplist action.
a := b.cacheAction("deplist", p, func() *Action {
a1 := b.CompileAction(mode, depMode, p)

a := &Action{
Mode: "deplist",
Package: p,
Deps: []*Action{a1},
Objdir: a1.Objdir,
}
if a1.Func == nil {
// Built-in packages like unsafe.
return a
}
a.Func = (*Builder).deplist

return a
})
return a
}

// LinkAction returns the action for linking p into an executable
// and possibly installing the result (according to mode).
// depMode is the action (build or install) to use when compiling dependencies.
Expand Down
37 changes: 37 additions & 0 deletions vendor/cmd/go/internal/work/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,43 @@ func (b *Builder) vet(a *Action) error {
return b.run(a, p.Dir, p.ImportPath, env, cfg.BuildToolexec, tool, VetFlags, a.Objdir+"vet.cfg")
}

func (b *Builder) deplist(a *Action) error {
// a.Deps[0] is the build of the package being deplistted.

p := a.Deps[0].Package

enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", "\t")

failed := false

c := cache.Default()
ca, err := c.Get(a.Deps[0].actionID)
if err != nil {
failed = true
}

details := struct {
ImportPath string
PackageFile string
Incomplete bool
}{
ImportPath: p.ImportPath,
}

if !failed {
details.PackageFile = c.OutputFile(ca.OutputID)
} else {
details.Incomplete = true
}

if err := enc.Encode(details); err != nil {
return fmt.Errorf("failed to JSON encode package: %v", err)
}

return nil
}

// linkActionID computes the action ID for a link action.
func (b *Builder) linkActionID(a *Action) cache.ActionID {
p := a.Package
Expand Down
2 changes: 2 additions & 0 deletions vendor/cmd/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"cmd/go/internal/bug"
"cmd/go/internal/cfg"
"cmd/go/internal/clean"
"cmd/go/internal/deplist"
"cmd/go/internal/doc"
"cmd/go/internal/envcmd"
"cmd/go/internal/fix"
Expand Down Expand Up @@ -56,6 +57,7 @@ func init() {
vgo.CmdVerify,
version.CmdVersion,
vet.CmdVet,
deplist.CmdDeplist,

help.HelpBuildmode,
help.HelpC,
Expand Down

0 comments on commit c77fb7d

Please sign in to comment.