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  }