github.com/tiagovtristao/plz@v13.4.0+incompatible/src/core/graph.go (about)

     1  // Representation of the build graph.
     2  // The graph of build targets forms a DAG which we discover from the top
     3  // down and then build bottom-up.
     4  
     5  package core
     6  
     7  import (
     8  	"sort"
     9  	"sync"
    10  )
    11  
    12  // A BuildGraph contains all the loaded targets and packages and maintains their
    13  // relationships, especially reverse dependencies which are calculated here.
    14  // It also arbitrates access to a lot of things via its builtin mutex which
    15  // is probably our most overused lock :(
    16  type BuildGraph struct {
    17  	// Map of all currently known targets by their label.
    18  	targets map[BuildLabel]*BuildTarget
    19  	// Map of all currently known packages.
    20  	packages map[packageKey]*Package
    21  	// Reverse dependencies that are pending on targets actually being added to the graph.
    22  	pendingRevDeps map[BuildLabel]map[BuildLabel]*BuildTarget
    23  	// Actual reverse dependencies
    24  	revDeps map[BuildLabel][]*BuildTarget
    25  	// Registered subrepos, as a map of their name to their root.
    26  	subrepos map[string]*Subrepo
    27  	// Used to arbitrate access to the graph. We parallelise most build operations
    28  	// and Go maps aren't natively threadsafe so this is needed.
    29  	mutex sync.RWMutex
    30  }
    31  
    32  // AddTarget adds a new target to the graph.
    33  func (graph *BuildGraph) AddTarget(target *BuildTarget) *BuildTarget {
    34  	graph.mutex.Lock()
    35  	defer graph.mutex.Unlock()
    36  	if _, present := graph.targets[target.Label]; present {
    37  		panic("Attempted to re-add existing target to build graph: " + target.Label.String())
    38  	}
    39  	graph.targets[target.Label] = target
    40  	// Check these reverse deps which may have already been added against this target.
    41  	revdeps, present := graph.pendingRevDeps[target.Label]
    42  	if present {
    43  		for revdep, originalTarget := range revdeps {
    44  			if originalTarget != nil {
    45  				graph.linkDependencies(graph.targets[revdep], originalTarget)
    46  			} else {
    47  				graph.linkDependencies(graph.targets[revdep], target)
    48  			}
    49  		}
    50  		delete(graph.pendingRevDeps, target.Label) // Don't need any more
    51  	}
    52  	return target
    53  }
    54  
    55  // AddPackage adds a new package to the graph with given name.
    56  func (graph *BuildGraph) AddPackage(pkg *Package) {
    57  	key := packageKey{Name: pkg.Name, Subrepo: pkg.SubrepoName}
    58  	graph.mutex.Lock()
    59  	defer graph.mutex.Unlock()
    60  	if _, present := graph.packages[key]; present {
    61  		panic("Attempt to readd existing package: " + key.String())
    62  	}
    63  	graph.packages[key] = pkg
    64  }
    65  
    66  // Target retrieves a target from the graph by label
    67  func (graph *BuildGraph) Target(label BuildLabel) *BuildTarget {
    68  	graph.mutex.RLock()
    69  	defer graph.mutex.RUnlock()
    70  	return graph.targets[label]
    71  }
    72  
    73  // TargetOrDie retrieves a target from the graph by label. Dies if the target doesn't exist.
    74  func (graph *BuildGraph) TargetOrDie(label BuildLabel) *BuildTarget {
    75  	target := graph.Target(label)
    76  	if target == nil {
    77  		log.Fatalf("Target %s not found in build graph\n", label)
    78  	}
    79  	return target
    80  }
    81  
    82  // PackageByLabel retrieves a package from the graph using the appropriate parts of the given label.
    83  // The Name entry is ignored.
    84  func (graph *BuildGraph) PackageByLabel(label BuildLabel) *Package {
    85  	return graph.Package(label.PackageName, label.Subrepo)
    86  }
    87  
    88  // Package retrieves a package from the graph by name & subrepo, or nil if it can't be found.
    89  func (graph *BuildGraph) Package(name, subrepo string) *Package {
    90  	graph.mutex.RLock()
    91  	defer graph.mutex.RUnlock()
    92  	return graph.packages[packageKey{Name: name, Subrepo: subrepo}]
    93  }
    94  
    95  // PackageOrDie retrieves a package by label, and dies if it can't be found.
    96  func (graph *BuildGraph) PackageOrDie(label BuildLabel) *Package {
    97  	pkg := graph.PackageByLabel(label)
    98  	if pkg == nil {
    99  		log.Fatalf("Package %s doesn't exist in graph", packageKey{Name: label.PackageName, Subrepo: label.Subrepo})
   100  	}
   101  	return pkg
   102  }
   103  
   104  // AddSubrepo adds a new subrepo to the graph. It dies if one is already registered by this name.
   105  func (graph *BuildGraph) AddSubrepo(subrepo *Subrepo) {
   106  	graph.mutex.Lock()
   107  	defer graph.mutex.Unlock()
   108  	if _, present := graph.subrepos[subrepo.Name]; present {
   109  		log.Fatalf("Subrepo %s is already registered", subrepo.Name)
   110  	}
   111  	graph.subrepos[subrepo.Name] = subrepo
   112  }
   113  
   114  // MaybeAddSubrepo adds the given subrepo to the graph, or returns the existing one if one is already registered.
   115  func (graph *BuildGraph) MaybeAddSubrepo(subrepo *Subrepo) *Subrepo {
   116  	graph.mutex.Lock()
   117  	defer graph.mutex.Unlock()
   118  	if s, present := graph.subrepos[subrepo.Name]; present {
   119  		return s
   120  	}
   121  	graph.subrepos[subrepo.Name] = subrepo
   122  	return subrepo
   123  }
   124  
   125  // Subrepo returns the subrepo with this name. It returns nil if one isn't registered.
   126  func (graph *BuildGraph) Subrepo(name string) *Subrepo {
   127  	graph.mutex.RLock()
   128  	defer graph.mutex.RUnlock()
   129  	return graph.subrepos[name]
   130  }
   131  
   132  // SubrepoOrDie returns the subrepo with this name, dying if it doesn't exist.
   133  func (graph *BuildGraph) SubrepoOrDie(name string) *Subrepo {
   134  	subrepo := graph.Subrepo(name)
   135  	if subrepo == nil {
   136  		log.Fatalf("No registered subrepo by the name %s", name)
   137  	}
   138  	return subrepo
   139  }
   140  
   141  // Len returns the number of targets currently in the graph.
   142  func (graph *BuildGraph) Len() int {
   143  	graph.mutex.RLock()
   144  	defer graph.mutex.RUnlock()
   145  	return len(graph.targets)
   146  }
   147  
   148  // AllTargets returns a consistently ordered slice of all the targets in the graph.
   149  func (graph *BuildGraph) AllTargets() BuildTargets {
   150  	graph.mutex.RLock()
   151  	defer graph.mutex.RUnlock()
   152  	targets := make(BuildTargets, 0, len(graph.targets))
   153  	for _, target := range graph.targets {
   154  		targets = append(targets, target)
   155  	}
   156  	sort.Sort(targets)
   157  	return targets
   158  }
   159  
   160  // PackageMap returns a copy of the graph's internal map of name to package.
   161  func (graph *BuildGraph) PackageMap() map[string]*Package {
   162  	graph.mutex.RLock()
   163  	defer graph.mutex.RUnlock()
   164  	packages := make(map[string]*Package, len(graph.packages))
   165  	for k, v := range graph.packages {
   166  		packages[k.String()] = v
   167  	}
   168  	return packages
   169  }
   170  
   171  // AddDependency adds a dependency between two build targets.
   172  // The 'to' target doesn't necessarily have to exist in the graph yet (but 'from' must).
   173  func (graph *BuildGraph) AddDependency(from BuildLabel, to BuildLabel) {
   174  	graph.mutex.Lock()
   175  	defer graph.mutex.Unlock()
   176  	fromTarget := graph.targets[from]
   177  	// We might have done this already; do a quick check here first.
   178  	if fromTarget.hasResolvedDependency(to) {
   179  		return
   180  	}
   181  	toTarget, present := graph.targets[to]
   182  	// The dependency may not exist yet if we haven't parsed its package.
   183  	// In that case we stash it away for later.
   184  	if !present {
   185  		graph.addPendingRevDep(from, to, nil)
   186  	} else {
   187  		graph.linkDependencies(fromTarget, toTarget)
   188  	}
   189  }
   190  
   191  // NewGraph constructs and returns a new BuildGraph.
   192  // Users should not attempt to construct one themselves.
   193  func NewGraph() *BuildGraph {
   194  	return &BuildGraph{
   195  		targets:        map[BuildLabel]*BuildTarget{},
   196  		packages:       map[packageKey]*Package{},
   197  		pendingRevDeps: map[BuildLabel]map[BuildLabel]*BuildTarget{},
   198  		revDeps:        map[BuildLabel][]*BuildTarget{},
   199  		subrepos:       map[string]*Subrepo{},
   200  	}
   201  }
   202  
   203  // ReverseDependencies returns the set of revdeps on the given target.
   204  func (graph *BuildGraph) ReverseDependencies(target *BuildTarget) []*BuildTarget {
   205  	graph.mutex.RLock()
   206  	defer graph.mutex.RUnlock()
   207  	if revdeps, present := graph.revDeps[target.Label]; present {
   208  		return revdeps[:]
   209  	}
   210  	return []*BuildTarget{}
   211  }
   212  
   213  // AllDepsBuilt returns true if all the dependencies of a target are built.
   214  func (graph *BuildGraph) AllDepsBuilt(target *BuildTarget) bool {
   215  	graph.mutex.RLock()
   216  	defer graph.mutex.RUnlock()
   217  	return target.allDepsBuilt()
   218  }
   219  
   220  // AllDependenciesResolved returns true once all the dependencies of a target have been
   221  // parsed and resolved to real targets.
   222  func (graph *BuildGraph) AllDependenciesResolved(target *BuildTarget) bool {
   223  	graph.mutex.RLock()
   224  	defer graph.mutex.RUnlock()
   225  	return target.allDependenciesResolved()
   226  }
   227  
   228  // linkDependencies adds the dependency of fromTarget on toTarget and the corresponding
   229  // reverse dependency in the other direction.
   230  // This is complicated somewhat by the require/provide mechanism which is resolved at this
   231  // point, but some of the dependencies may not yet exist.
   232  func (graph *BuildGraph) linkDependencies(fromTarget, toTarget *BuildTarget) {
   233  	for _, label := range toTarget.ProvideFor(fromTarget) {
   234  		target, present := graph.targets[label]
   235  		if present {
   236  			fromTarget.resolveDependency(toTarget.Label, target)
   237  			graph.revDeps[label] = append(graph.revDeps[label], fromTarget)
   238  		} else {
   239  			graph.addPendingRevDep(fromTarget.Label, label, toTarget)
   240  		}
   241  	}
   242  }
   243  
   244  func (graph *BuildGraph) addPendingRevDep(from, to BuildLabel, orig *BuildTarget) {
   245  	if deps, present := graph.pendingRevDeps[to]; present {
   246  		deps[from] = orig
   247  	} else {
   248  		graph.pendingRevDeps[to] = map[BuildLabel]*BuildTarget{from: orig}
   249  	}
   250  }
   251  
   252  // DependentTargets returns the labels that 'from' should actually depend on when it declared a dependency on 'to'.
   253  // This is normally just 'to' but could be otherwise given require/provide shenanigans.
   254  func (graph *BuildGraph) DependentTargets(from, to BuildLabel) []BuildLabel {
   255  	fromTarget := graph.Target(from)
   256  	if toTarget := graph.Target(to); fromTarget != nil && toTarget != nil {
   257  		graph.mutex.Lock()
   258  		defer graph.mutex.Unlock()
   259  		return toTarget.ProvideFor(fromTarget)
   260  	}
   261  	return []BuildLabel{to}
   262  }