github.com/joey-fossa/fossa-cli@v0.7.34-0.20190708193710-569f1e8679f0/buildtools/debian/debian.go (about) 1 package debian 2 3 import ( 4 "path/filepath" 5 "sync" 6 7 "github.com/apex/log" 8 9 "github.com/fossas/fossa-cli/api/fossa" 10 "github.com/fossas/fossa-cli/graph" 11 "github.com/fossas/fossa-cli/pkg" 12 ) 13 14 type Cmd struct { 15 Directory string 16 Upload bool 17 DebCmd func(...string) (string, error) 18 } 19 20 // New returns a new Cmd that implements "apt-cache" to find dependency information. 21 func New() Cmd { 22 return Cmd{ 23 Directory: "/usr/share/doc/", 24 Upload: true, 25 DebCmd: aptCache, 26 } 27 } 28 29 // Dependencies returns a dependency graph by analyzing a debian package.Dependencies. 30 // Notes: 31 // (A "valid dependency" is one that has a valid path in "d.Directory+target"). Many types of 32 // packages are found when running d.DebCmd and it is important to determine which ones 33 // are used by the system. This is the reason we debug errors when a tarball upload fails. 34 // 35 // Analysis steps are as follows: 36 // 1. Find all transitive dependencies for the target package. 37 // 2. Upload all valid dependencies and retrieve locator information. 38 // 3. Loop through all valid dependencies and find their direct dependencies using d.DebCmd 39 // 4. Construct a dependency graph with the direct dependency information.Dependencies 40 // 41 func (d Cmd) Dependencies(target string) (graph.Deps, error) { 42 // Get all relevant dependencies. 43 depList, err := transitiveDeps(d.DebCmd, target) 44 if err != nil { 45 return graph.Deps{}, err 46 } 47 48 // Loop over the dep list create the locator map from valid package uploads. 49 locatorMap := uploadDeps(depList, d.Directory, d.Upload) 50 51 // Loop over the locator map, get direct deps for each and build the graph. 52 depGraph := dependencyGraph(d.DebCmd, locatorMap) 53 54 directDeps := []pkg.Import{ 55 pkg.Import{ 56 Target: target, 57 Resolved: pkg.ID{ 58 Type: pkg.Raw, 59 Name: target, 60 Revision: locatorMap[target], 61 }, 62 }, 63 } 64 65 return graph.Deps{ 66 Direct: directDeps, 67 Transitive: depGraph, 68 }, nil 69 } 70 71 func uploadDeps(dependencies map[string]string, directory string, upload bool) map[string]string { 72 var locatorMap = make(map[string]string) 73 wg := sync.WaitGroup{} 74 mapLock := sync.RWMutex{} 75 76 for d := range dependencies { 77 wg.Add(1) 78 go func(dep string) { 79 defer wg.Done() 80 81 revision, err := fossa.UploadTarballDependency(filepath.Join(directory, dep), upload, true) 82 if err != nil { 83 log.Debugf("Error uploading %v: %+v", dep, err) 84 } else { 85 mapLock.Lock() 86 locatorMap[dep] = revision.Revision 87 mapLock.Unlock() 88 } 89 }(d) 90 } 91 wg.Wait() 92 93 return locatorMap 94 } 95 96 func dependencyGraph(command func(...string) (string, error), locatorMap map[string]string) map[pkg.ID]pkg.Package { 97 wg := sync.WaitGroup{} 98 mapLock := sync.RWMutex{} 99 var depGraph = make(map[pkg.ID]pkg.Package) 100 for t, r := range locatorMap { 101 wg.Add(1) 102 go func(target, revision string) { 103 defer wg.Done() 104 if _, ok := locatorMap[target]; ok { 105 dependencies, err := directDeps(command, target) 106 if err != nil { 107 log.Debugf("Error retrieving deps for %+v: %+v", target, err) 108 } 109 110 importedDeps := []pkg.Import{} 111 for _, dep := range dependencies { 112 if _, ok := locatorMap[dep]; ok { 113 importedDeps = append(importedDeps, pkg.Import{ 114 Target: dep, 115 Resolved: pkg.ID{ 116 Type: pkg.Raw, 117 Name: dep, 118 Revision: locatorMap[dep], 119 }, 120 }) 121 } 122 } 123 124 targetID := pkg.ID{ 125 Type: pkg.Raw, 126 Name: target, 127 Revision: revision, 128 } 129 mapLock.Lock() 130 depGraph[targetID] = pkg.Package{ 131 ID: targetID, 132 Imports: importedDeps, 133 } 134 mapLock.Unlock() 135 } 136 }(t, r) 137 } 138 wg.Wait() 139 140 return depGraph 141 }