github.com/comcast/canticle@v0.0.0-20161108184242-c53cface56e8/canticles/depresolver.go (about) 1 package canticles 2 3 import ( 4 "fmt" 5 "os" 6 ) 7 8 // A DependencySource represents the possible options to source a 9 // dependency from. Its possible revisions, remote sources, and other 10 // information like its on disk root, or errors resolving it. 11 type DependencySource struct { 12 // Revisions specified by canticle files 13 Revisions StringSet 14 // OnDiskRevision for this VCS 15 OnDiskRevision string 16 // Sources specified for this VCS. 17 Sources StringSet 18 // OnDiskSource for this VCS. 19 OnDiskSource string 20 // Deps contained by this VCS system. 21 Deps Dependencies 22 // Root of the pacakges import path (prefix for all dep import paths). 23 Root string 24 // Err 25 Err error 26 } 27 28 // NewDependencySource initalizes a DependencySource rooted at root on 29 // disk. 30 func NewDependencySource(root string) *DependencySource { 31 return &DependencySource{ 32 Root: root, 33 Deps: NewDependencies(), 34 Revisions: NewStringSet(), 35 Sources: NewStringSet(), 36 } 37 } 38 39 // AddCantSource adds a canticle source dep 40 func (d *DependencySource) AddCantSource(source *CanticleDependency, path string) { 41 d.Revisions.Add(source.Revision) 42 d.Sources.Add(source.SourcePath) 43 dep := NewDependency(path) 44 dep.Imports.Add(source.Root) 45 d.Deps.AddDependency(dep) 46 } 47 48 // DependencySources represents a collection of dependencysources, 49 // including functionality to lookup deps that may be rooted in other 50 // deps. 51 type DependencySources struct { 52 Sources []*DependencySource 53 } 54 55 // NewDependencySources with an iniital size for performance. 56 func NewDependencySources(size int) *DependencySources { 57 return &DependencySources{make([]*DependencySource, 0, size)} 58 } 59 60 // DepSource returns the source for a dependency if its already 61 // present. That is if the deps importpath has a prefix in 62 // this collection. 63 func (ds *DependencySources) DepSource(importPath string) *DependencySource { 64 for _, source := range ds.Sources { 65 if source.Root == importPath || PathIsChild(source.Root, importPath) { 66 return source 67 } 68 } 69 return nil 70 } 71 72 // AddSource appends this DependencySource to our collection. 73 func (ds *DependencySources) AddSource(source *DependencySource) { 74 ds.Sources = append(ds.Sources, source) 75 } 76 77 // String to pretty print this. 78 func (ds *DependencySources) String() string { 79 str := "" 80 for _, source := range ds.Sources { 81 str += fmt.Sprintf("%s \n\tRevisions:%v OnDiskRevision:%s\n\tSources:%v OnDiskSource:%s\n\tDeps:", source.Root, source.Revisions, source.OnDiskRevision, source.Sources, source.OnDiskSource) 82 for _, dep := range source.Deps { 83 str += fmt.Sprintf("\n\t\t%+v", dep) 84 } 85 str += fmt.Sprintf("\n") 86 } 87 return str 88 } 89 90 // A SourcesResolver takes a set of dependencies and returns the 91 // possible sources and revisions for it (DependencySources) for it. 92 // Sources and Branches control whether the source (e.g. github.com) 93 // will be stored and whether brnaches of precise revisions will be 94 // saved. 95 type SourcesResolver struct { 96 RootPath, Gopath string 97 Resolver RepoResolver 98 Branches, Sources bool 99 CDepReader CantDepReader 100 } 101 102 // ResolveSources for everything in deps, no dependency trees will be 103 // walked. 104 func (sr *SourcesResolver) ResolveSources(deps Dependencies) (*DependencySources, error) { 105 sources := NewDependencySources(len(deps)) 106 for _, dep := range deps { 107 LogVerbose("\tFinding source for %s", dep.ImportPath) 108 // If we already have a source 109 // for this dep just continue 110 if source := sources.DepSource(dep.ImportPath); source != nil { 111 LogVerbose("\t\tDep already added %s", dep.ImportPath) 112 source.Deps.AddDependency(dep) 113 continue 114 } 115 116 // Otherwise find the vcs root for it 117 vcs, err := sr.Resolver.ResolveRepo(dep.ImportPath, nil) 118 if err != nil { 119 LogWarn("\t\tSkipping dep %+v, %s", dep, err.Error()) 120 continue 121 } 122 123 root := vcs.GetRoot() 124 rootSrc := PackageSource(sr.Gopath, root) 125 if rootSrc == sr.RootPath || PathIsChild(rootSrc, sr.RootPath) { 126 LogVerbose("\t\tSkipping pkg %s since its vcs is at our save level", sr.RootPath) 127 continue 128 } 129 source := NewDependencySource(root) 130 131 var rev string 132 if sr.Branches { 133 rev, err = vcs.GetBranch() 134 if err != nil { 135 LogWarn("\t\tNo branch from vcs at %s %s", root, err.Error()) 136 } 137 } 138 if !sr.Branches || err != nil { 139 rev, err = vcs.GetRev() 140 if err != nil { 141 return nil, fmt.Errorf("cant get revision from vcs at %s %s", root, err.Error()) 142 } 143 } 144 source.Revisions.Add(rev) 145 source.OnDiskRevision = rev 146 147 if sr.Sources { 148 LogVerbose("\t\tGetting source for VCS: %s", root) 149 vcsSource, err := vcs.GetSource() 150 if err != nil { 151 return nil, fmt.Errorf("cant get vcs source from vcs at %s %s", root, err.Error()) 152 } 153 source.Sources.Add(vcsSource) 154 source.OnDiskSource = vcsSource 155 } 156 source.Deps.AddDependency(dep) 157 158 sources.AddSource(source) 159 } 160 161 // Resolve sources from importpaths, that is any canticle 162 // files stored in a directory imported by our vcs 163 for _, dep := range deps { 164 if err := sr.resolveCantDeps(sources, dep.ImportPath); err != nil { 165 return sources, err 166 } 167 } 168 169 // Resolve any sources from our vcs roots, that is any 170 // canticle files stored at the vcs route of a project. 171 for _, source := range sources.Sources { 172 if err := sr.resolveCantDeps(sources, source.Root); err != nil { 173 return sources, err 174 } 175 } 176 177 return sources, nil 178 } 179 180 func (sr *SourcesResolver) resolveCantDeps(sources *DependencySources, path string) error { 181 cdeps, err := sr.CDepReader.CanticleDependencies(path) 182 if err != nil { 183 if os.IsNotExist(err) { 184 return nil 185 } 186 return err 187 } 188 189 for _, cdep := range cdeps { 190 source := sources.DepSource(cdep.Root) 191 if source == nil { 192 continue 193 } 194 if !sr.Sources { 195 cdep.SourcePath = "" 196 } 197 LogVerbose("\t\tAdding canticle source %+v", cdep) 198 source.AddCantSource(cdep, path) 199 } 200 return nil 201 }