def get_implicit_gpl_checkouts(builder): """Find all the checkouts to which GPL-ness propagates. Returns a tuple, (result, because), where: * 'result' is a set of the checkout labels that are implicitly made "GPL" by propagation, and * 'because' is a dictionary linking each such label to a set of strings explaining the reason for the labels inclusion """ # There are clearly two ways we can do this: # # 1. For each checkout, follow its dependencies until we find something # that is non-system GPL, or we don't (obviously, finding one such # is enough). # # 2. For each non-system GPL checkout, find everything that depends upon # it and mark it as propagated-to # # In either case, it is definitely worth checking to see if there are # *any* non-system GPL checkouts. # # If we do (1) then we may need to traverse the entire dependency tree # for each and every checkout in it (e.g., if there are no non-system # GPL licensed checkouts). # # If we do (2), then we do nothing if there are no non-system GPL # checkouts. For each that there is, we do need to traverse the entire # dependency tree, but we can hope that this is for significantly fewer # cases than in (1). # # Also, it is possible that we may have "blockers" inserted into the tree, # which truncate such a traversal (I'm not 100% sure about this yet). # # Regardless, approach (2) seems the more sensible. all_gpl_checkouts = get_gpl_checkouts(builder) # Localise for our loop get_checkout_license = builder.db.get_checkout_license get_license_not_affected_by = builder.db.get_license_not_affected_by get_nothing_builds_against = builder.db.get_nothing_builds_against ruleset = builder.ruleset DEBUG = False def add_if_not_us_or_gpl(our_co, this_co, result, because, reason): """Add 'this_co' to 'result' if it is not 'our_co' and not GPL itself. In which case, also add 'this_co':'reason' to 'because' Relies on 'our_co' having a wildcarded label tag. """ if our_co.just_match(this_co): # OK, that's just some variant on ourselves if DEBUG: print 'BUT %s is our_co' % this_co return this_license = get_checkout_license(this_co, absent_is_None=True) if this_license and this_license.is_gpl(): if DEBUG: print 'BUT %s is already GPL' % this_co return lbl = this_co.copy_with_tag('*') result.add(lbl) if lbl in because: because[lbl].add(reason) else: because[lbl] = set([reason]) if DEBUG: print 'ADD %s' % lbl result = set() # Checkouts implicitly affected because = {} # checkout -> what it depended on that did so if DEBUG: print print 'Finding implicit GPL checkouts' for co_label in all_gpl_checkouts: if DEBUG: print '.. %s' % co_label license = get_checkout_license(co_label) if not license.propagates(): if DEBUG: print ' has a link-exception of some sort - ignoring it' continue if get_nothing_builds_against(co_label): if DEBUG: print ' nothing builds against this - ignoring it' continue depend_on_this = required_by(ruleset, co_label) for this_label in depend_on_this: # We should have a bunch of package labels (possibly the same # package present with different tags), plus quite likely some # variants on our own checkout label, and sometimes other stuff if DEBUG: print ' %s' % this_label, if this_label.type == LabelType.Package: not_affected_by = get_license_not_affected_by(this_label) if co_label in not_affected_by: if DEBUG: print 'NOT against %s' % co_label continue # OK, what checkouts does that imply? pkg_checkouts = builder.checkouts_for_package(this_label) if DEBUG: print 'EXPANDS to %s' % ( label_list_to_string(pkg_checkouts)) for this_co in pkg_checkouts: if DEBUG: print ' %s' % this_label, not_affected_by = get_license_not_affected_by(this_co) if co_label in not_affected_by: if DEBUG: print 'NOT against %s' % co_label continue # We know that our original 'co_label' has type '/*` add_if_not_us_or_gpl( co_label, this_co, result, because, '%s depends on %s' % (this_label.copy_with_tag('*'), co_label)) elif this_label.type == LabelType.Checkout: # We know that our original 'co_label' has type '/*` add_if_not_us_or_gpl( co_label, this_label, result, because, '%s depends on %s' % (this_label.copy_with_tag('*'), co_label)) else: # Deployments don't build stuff, so we can ignore them if DEBUG: print 'IGNORE' continue return result, because
def get_implicit_gpl_checkouts(builder): """Find all the checkouts to which GPL-ness propagates. Returns a tuple, (result, because), where: * 'result' is a set of the checkout labels that are implicitly made "GPL" by propagation, and * 'because' is a dictionary linking each such label to a set of strings explaining the reason for the labels inclusion """ # There are clearly two ways we can do this: # # 1. For each checkout, follow its dependencies until we find something # that is non-system GPL, or we don't (obviously, finding one such # is enough). # # 2. For each non-system GPL checkout, find everything that depends upon # it and mark it as propagated-to # # In either case, it is definitely worth checking to see if there are # *any* non-system GPL checkouts. # # If we do (1) then we may need to traverse the entire dependency tree # for each and every checkout in it (e.g., if there are no non-system # GPL licensed checkouts). # # If we do (2), then we do nothing if there are no non-system GPL # checkouts. For each that there is, we do need to traverse the entire # dependency tree, but we can hope that this is for significantly fewer # cases than in (1). # # Also, it is possible that we may have "blockers" inserted into the tree, # which truncate such a traversal (I'm not 100% sure about this yet). # # Regardless, approach (2) seems the more sensible. all_gpl_checkouts = get_gpl_checkouts(builder) # Localise for our loop get_checkout_license = builder.db.get_checkout_license get_license_not_affected_by = builder.db.get_license_not_affected_by get_nothing_builds_against = builder.db.get_nothing_builds_against ruleset = builder.ruleset DEBUG = False def add_if_not_us_or_gpl(our_co, this_co, result, because, reason): """Add 'this_co' to 'result' if it is not 'our_co' and not GPL itself. In which case, also add 'this_co':'reason' to 'because' Relies on 'our_co' having a wildcarded label tag. """ if our_co.just_match(this_co): # OK, that's just some variant on ourselves if DEBUG: print 'BUT %s is our_co'%this_co return this_license = get_checkout_license(this_co, absent_is_None=True) if this_license and this_license.is_gpl(): if DEBUG: print 'BUT %s is already GPL'%this_co return lbl = this_co.copy_with_tag('*') result.add(lbl) if lbl in because: because[lbl].add(reason) else: because[lbl] = set([reason]) if DEBUG: print 'ADD %s'%lbl result = set() # Checkouts implicitly affected because = {} # checkout -> what it depended on that did so if DEBUG: print print 'Finding implicit GPL checkouts' for co_label in all_gpl_checkouts: if DEBUG: print '.. %s'%co_label license = get_checkout_license(co_label) if not license.propagates(): if DEBUG: print ' has a link-exception of some sort - ignoring it' continue if get_nothing_builds_against(co_label): if DEBUG: print ' nothing builds against this - ignoring it' continue depend_on_this = required_by(ruleset, co_label) for this_label in depend_on_this: # We should have a bunch of package labels (possibly the same # package present with different tags), plus quite likely some # variants on our own checkout label, and sometimes other stuff if DEBUG: print ' %s'%this_label, if this_label.type == LabelType.Package: not_affected_by = get_license_not_affected_by(this_label) if co_label in not_affected_by: if DEBUG: print 'NOT against %s'%co_label continue # OK, what checkouts does that imply? pkg_checkouts = builder.checkouts_for_package(this_label) if DEBUG: print 'EXPANDS to %s'%(label_list_to_string(pkg_checkouts)) for this_co in pkg_checkouts: if DEBUG: print ' %s'%this_label, not_affected_by = get_license_not_affected_by(this_co) if co_label in not_affected_by: if DEBUG: print 'NOT against %s'%co_label continue # We know that our original 'co_label' has type '/*` add_if_not_us_or_gpl(co_label, this_co, result, because, '%s depends on %s'%(this_label.copy_with_tag('*'), co_label)) elif this_label.type == LabelType.Checkout: # We know that our original 'co_label' has type '/*` add_if_not_us_or_gpl(co_label, this_label, result, because, '%s depends on %s'%(this_label.copy_with_tag('*'), co_label)) else: # Deployments don't build stuff, so we can ignore them if DEBUG: print 'IGNORE' continue return result, because
def depend_unit_test(): """ Some fairly simple tests for the dependency solver. """ l1 = Label(utils.LabelType.Checkout, "co_1", "role_1", utils.LabelTag.CheckedOut) l2 = Label(utils.LabelType.Checkout, "co_1", "role_1", utils.LabelTag.Pulled) l3 = Label(utils.LabelType.Package, "pkg_1", "role_1", utils.LabelTag.PreConfig) l4 = Label(utils.LabelType.Deployment, "dep_1", "role_2", utils.LabelTag.Built) # Check label_from_string .. lx = Label.from_string("foo:bar{baz}/wombat[T]") lx_a = Label("foo", "bar", "baz", "wombat") assert lx == lx_a assert lx.transient assert not lx.system lx = Label.from_string("foo:bar/wombat[T]") lx_a = Label("foo", "bar", None, "wombat") assert lx == lx_a assert lx.transient assert not lx.system lx = Label.from_string("*:bar/wombat") assert (lx is not None) lx_a = Label("*", "bar", None, "wombat") assert lx == lx_a assert not lx.transient assert not lx.system lx = Label.from_string("*:wombat/*") assert lx is not None lx_a = Label("*", "wombat", None, "*") assert lx == lx_a assert not lx.transient assert not lx.system lx = Label.from_string(l1.__str__()) assert lx is not None assert lx == l1 lx = Label.from_string(l2.__str__()) assert lx is not None assert lx == l2 lx = Label.from_string(l3.__str__()) assert lx is not None assert lx == l3 lx = Label.from_string(l4.__str__()) assert lx is not None assert lx == l4 # Let's check that label matching works the way we think it does .. la1 = Label(type='*', name=l1.name, domain=l1.domain, role=l1.role, tag=l1.tag) la2 = Label(type=l1.type, name='*', domain=l1.domain, role=l1.role, tag=l1.tag) la3 = Label(type=l1.type, name='*', domain=l1.domain, role='*', tag=l1.tag) la4 = l1.copy_with_tag('*') assert l1.match(l1) == 0 assert l2.match(l1) is None assert la1.match(l1) == -1 assert l1.match(la1) == -1 assert (l2.match(la4)) == -1 assert l1.match(la3) == -2 r1 = depend.Rule(l1, pkg.NoAction()) r2 = depend.Rule(l2, pkg.NoAction()) r2.add(l1) r3 = depend.Rule(l3, pkg.NoAction()) r4 = depend.Rule(l4, pkg.NoAction()) r3.add(l2) r4.add(l3); r4.add(l2) rs = depend.RuleSet() rs.add(r1) rs.add(r2) rs.add(r3) rs.add(r4) assert str(rs).strip() == """\ ----- checkout:co_1{role_1}/checked_out <-NoAction-- [ ] checkout:co_1{role_1}/pulled <-NoAction-- [ checkout:co_1{role_1}/checked_out ] deployment:dep_1{role_2}/built <-NoAction-- [ checkout:co_1{role_1}/pulled, package:pkg_1{role_1}/preconfig ] package:pkg_1{role_1}/preconfig <-NoAction-- [ checkout:co_1{role_1}/pulled ] -----""" r3_required_for = depend.needed_to_build(rs, l3) assert depend.rule_list_to_string(r3_required_for) == "[ checkout:co_1{role_1}/checked_out <-NoAction-- [ ], checkout:co_1{role_1}/pulled <-NoAction-- [ checkout:co_1{role_1}/checked_out ], package:pkg_1{role_1}/preconfig <-NoAction-- [ checkout:co_1{role_1}/pulled ], ]" r2_required_by = depend.required_by(rs, l2) assert depend.rule_list_to_string(r2_required_by) == "[ checkout:co_1{role_1}/pulled, deployment:dep_1{role_2}/built, package:pkg_1{role_1}/preconfig, ]"
def depend_unit_test(): """ Some fairly simple tests for the dependency solver. """ l1 = Label(utils.LabelType.Checkout, "co_1", "role_1", utils.LabelTag.CheckedOut) l2 = Label(utils.LabelType.Checkout, "co_1", "role_1", utils.LabelTag.Pulled) l3 = Label(utils.LabelType.Package, "pkg_1", "role_1", utils.LabelTag.PreConfig) l4 = Label(utils.LabelType.Deployment, "dep_1", "role_2", utils.LabelTag.Built) # Check label_from_string .. lx = Label.from_string("foo:bar{baz}/wombat[T]") lx_a = Label("foo", "bar", "baz", "wombat") assert lx == lx_a assert lx.transient assert not lx.system lx = Label.from_string("foo:bar/wombat[T]") lx_a = Label("foo", "bar", None, "wombat") assert lx == lx_a assert lx.transient assert not lx.system lx = Label.from_string("*:bar/wombat") assert (lx is not None) lx_a = Label("*", "bar", None, "wombat") assert lx == lx_a assert not lx.transient assert not lx.system lx = Label.from_string("*:wombat/*") assert lx is not None lx_a = Label("*", "wombat", None, "*") assert lx == lx_a assert not lx.transient assert not lx.system lx = Label.from_string(l1.__str__()) assert lx is not None assert lx == l1 lx = Label.from_string(l2.__str__()) assert lx is not None assert lx == l2 lx = Label.from_string(l3.__str__()) assert lx is not None assert lx == l3 lx = Label.from_string(l4.__str__()) assert lx is not None assert lx == l4 # Let's check that label matching works the way we think it does .. la1 = Label(type='*', name=l1.name, domain=l1.domain, role=l1.role, tag=l1.tag) la2 = Label(type=l1.type, name='*', domain=l1.domain, role=l1.role, tag=l1.tag) la3 = Label(type=l1.type, name='*', domain=l1.domain, role='*', tag=l1.tag) la4 = l1.copy_with_tag('*') assert l1.match(l1) == 0 assert l2.match(l1) is None assert la1.match(l1) == -1 assert l1.match(la1) == -1 assert (l2.match(la4)) == -1 assert l1.match(la3) == -2 r1 = depend.Rule(l1, pkg.NoAction()) r2 = depend.Rule(l2, pkg.NoAction()) r2.add(l1) r3 = depend.Rule(l3, pkg.NoAction()) r4 = depend.Rule(l4, pkg.NoAction()) r3.add(l2) r4.add(l3) r4.add(l2) rs = depend.RuleSet() rs.add(r1) rs.add(r2) rs.add(r3) rs.add(r4) assert str(rs).strip() == """\ ----- checkout:co_1{role_1}/checked_out <-NoAction-- [ ] checkout:co_1{role_1}/pulled <-NoAction-- [ checkout:co_1{role_1}/checked_out ] deployment:dep_1{role_2}/built <-NoAction-- [ checkout:co_1{role_1}/pulled, package:pkg_1{role_1}/preconfig ] package:pkg_1{role_1}/preconfig <-NoAction-- [ checkout:co_1{role_1}/pulled ] -----""" r3_required_for = depend.needed_to_build(rs, l3) assert depend.rule_list_to_string( r3_required_for ) == "[ checkout:co_1{role_1}/checked_out <-NoAction-- [ ], checkout:co_1{role_1}/pulled <-NoAction-- [ checkout:co_1{role_1}/checked_out ], package:pkg_1{role_1}/preconfig <-NoAction-- [ checkout:co_1{role_1}/pulled ], ]" r2_required_by = depend.required_by(rs, l2) assert depend.rule_list_to_string( r2_required_by ) == "[ checkout:co_1{role_1}/pulled, deployment:dep_1{role_2}/built, package:pkg_1{role_1}/preconfig, ]"