github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/analyzers/golang/analyze.go (about) 1 package golang 2 3 import ( 4 "github.com/apex/log" 5 6 "github.com/fossas/fossa-cli/analyzers/golang/resolver" 7 "github.com/fossas/fossa-cli/buildtools/dep" 8 "github.com/fossas/fossa-cli/buildtools/gdm" 9 "github.com/fossas/fossa-cli/buildtools/glide" 10 "github.com/fossas/fossa-cli/buildtools/gocmd" 11 "github.com/fossas/fossa-cli/buildtools/godep" 12 "github.com/fossas/fossa-cli/buildtools/gomodules" 13 "github.com/fossas/fossa-cli/buildtools/govendor" 14 "github.com/fossas/fossa-cli/buildtools/vndr" 15 "github.com/fossas/fossa-cli/errors" 16 "github.com/fossas/fossa-cli/graph" 17 "github.com/fossas/fossa-cli/pkg" 18 ) 19 20 // Analyze builds a dependency graph using go list and then looks up revisions 21 // using tool-specific lockfiles. 22 func (a *Analyzer) Analyze() (graph.Deps, error) { 23 m := a.Module 24 log.Debugf("%#v", m) 25 26 // Get Go project. 27 project, err := a.Project(m.BuildTarget) 28 if err != nil { 29 return graph.Deps{}, err 30 } 31 log.Debugf("Go project: %#v", project) 32 33 // Read lockfiles to get revisions. 34 var r resolver.Resolver 35 switch a.Options.Strategy { 36 case "manifest:gomodules": 37 if a.Options.LockfilePath == "" { 38 return graph.Deps{}, errors.New("manifest strategy specified without lockfile path") 39 } 40 r, err = gomodules.New(a.Options.LockfilePath) 41 if err != nil { 42 return graph.Deps{}, err 43 } 44 case "manifest:dep": 45 if a.Options.LockfilePath == "" { 46 return graph.Deps{}, errors.New("manifest strategy specified without lockfile path") 47 } 48 r, err = dep.New(a.Options.LockfilePath, a.Options.ManifestPath) 49 if err != nil { 50 return graph.Deps{}, err 51 } 52 case "manifest:gdm": 53 if a.Options.LockfilePath == "" { 54 return graph.Deps{}, errors.New("manifest strategy specified without lockfile path") 55 } 56 r, err = gdm.FromFile(a.Options.LockfilePath) 57 if err != nil { 58 return graph.Deps{}, err 59 } 60 case "manifest:glide": 61 if a.Options.LockfilePath == "" { 62 return graph.Deps{}, errors.New("manifest strategy specified without lockfile path") 63 } 64 r, err = glide.FromFile(a.Options.LockfilePath) 65 if err != nil { 66 return graph.Deps{}, err 67 } 68 case "manifest:godep": 69 if a.Options.LockfilePath == "" { 70 return graph.Deps{}, errors.New("manifest strategy specified without lockfile path") 71 } 72 r, err = godep.FromFile(a.Options.LockfilePath) 73 if err != nil { 74 return graph.Deps{}, err 75 } 76 case "manifest:govendor": 77 if a.Options.LockfilePath == "" { 78 return graph.Deps{}, errors.New("manifest strategy specified without lockfile path") 79 } 80 r, err = govendor.FromFile(a.Options.LockfilePath) 81 if err != nil { 82 return graph.Deps{}, err 83 } 84 case "manifest:vndr": 85 if a.Options.LockfilePath == "" { 86 return graph.Deps{}, errors.New("manifest strategy specified without lockfile path") 87 } 88 r, err = vndr.FromFile(a.Options.LockfilePath) 89 if err != nil { 90 return graph.Deps{}, err 91 } 92 93 // Resolve revisions by traversing the local $GOPATH and calling the package's 94 // VCS. 95 case "gopath-vcs": 96 return graph.Deps{}, errors.ErrNotImplemented 97 98 // Read revisions from an auto-detected tool manifest. 99 default: 100 r, err = a.ResolverFromLockfile(project.Tool, project.Manifest) 101 if err != nil { 102 return graph.Deps{}, err 103 } 104 } 105 106 log.Debugf("Resolver: %#v", r) 107 108 var allImports []pkg.Import 109 importMap := make(map[pkg.Import]bool) 110 transitiveDeps := make(map[pkg.ID]pkg.Package) 111 112 for _, buildTag := range a.BuildTags { 113 // Use `go list` to get imports and deps of module. 114 flags := []string{"-tags", buildTag} 115 main, err := a.Go.ListOne(m.BuildTarget, flags) 116 if err != nil { 117 return graph.Deps{}, err 118 } 119 120 if len(main.Deps) == 0 { 121 log.Warnf("No imports found for buid target %+v", m.BuildTarget) 122 return graph.Deps{}, nil 123 } 124 125 log.Debugf("Go main package: %#v", main) 126 deps, err := a.Go.List(main.Deps, flags) 127 if err != nil { 128 return graph.Deps{}, err 129 } 130 131 // Construct map of import path to package. 132 gopkgs := append(deps, main) 133 gopkgMap := make(map[string]gocmd.Package) 134 for _, p := range gopkgs { 135 gopkgMap[p.ImportPath] = p 136 } 137 // cgo imports don't have revisions. 138 gopkgMap["C"] = gocmd.Package{ 139 Name: "C", 140 IsStdLib: true, // This is so we don't try to lookup a revision. Maybe there should be a NoRevision bool field? 141 } 142 log.Debugf("gopkgMap: %#v", gopkgMap) 143 144 // Construct transitive dependency graph. 145 for _, gopkg := range deps { 146 log.Debugf("Getting revision for: %#v", gopkg) 147 148 // Resolve dependency. 149 revision, err := a.Revision(project, r, gopkg) 150 if err != nil { 151 return graph.Deps{}, err 152 } 153 id := revision.Resolved 154 155 // Check if the revision has already been scanned. 156 if _, ok := transitiveDeps[id]; ok { 157 continue 158 } 159 160 // Resolve dependency imports. 161 var imports []pkg.Import 162 for _, i := range gopkg.Imports { 163 _, ok := gopkgMap[i] 164 if !ok { 165 log.Fatalf("Could not find Go package for %#v, your build may have errors. Try `go list -json <MODULE>`.", i) 166 } 167 log.Debugf("Resolving import of: %#v", gopkg) 168 log.Debugf("Resolving dependency import: %#v", i) 169 revision, err := a.Revision(project, r, gopkgMap[i]) 170 if err != nil { 171 return graph.Deps{}, errors.Wrapf(err, "could not resolve %s", i) 172 } 173 imports = append(imports, revision) 174 } 175 176 transitiveDeps[id] = pkg.Package{ 177 ID: id, 178 Imports: imports, 179 } 180 } 181 182 // Construct direct imports list. 183 for _, i := range main.Imports { 184 revision, err := a.Revision(project, r, gopkgMap[i]) 185 if err != nil { 186 return graph.Deps{}, err 187 } 188 189 // Check if revision was added by a previous build tag. 190 if _, exists := importMap[revision]; !exists { 191 allImports = append(allImports, revision) 192 importMap[revision] = true 193 } 194 } 195 } 196 197 m.Deps = transitiveDeps 198 m.Imports = allImports 199 200 return graph.Deps{ 201 Direct: allImports, 202 Transitive: transitiveDeps, 203 }, nil 204 }