def deploy_with_domains(builder, target_dir, name, role_domains): """ Register a file deployment. 'role_domains' is a sequence of (role, domain) pairs. The deployment will take the roles and domains specified, and build them into a deployment at deploy/[name]. More specifically, a rule will be created for label: "deployment:<name>/deployed" which depends on "package:(<domain>)*{<role}/postinstalled" for each (<role>, <domain>) pair in 'role_domains'. In other words, the deployment called 'name' will depend on the given roles in the appropriate domains having been "finished" (postinstalled). An "instructions applied" label "deployment:<name>/instructionsapplied" will also be created. The deployment should eventually be located at 'target_dir'. """ the_action = FileDeploymentBuilder(role_domains, target_dir) dep_label = depend.Label(utils.LabelType.Deployment, name, None, utils.LabelTag.Deployed) iapp_label = depend.Label(utils.LabelType.Deployment, name, None, utils.LabelTag.InstructionsApplied, transient=True) # We depend on every postinstall for every package in the roles deployment_rule = depend.Rule(dep_label, the_action) for role, domain in role_domains: role_label = depend.Label(utils.LabelType.Package, "*", role, utils.LabelTag.PostInstalled, domain=domain) deployment_rule.add(role_label) # The instructionsapplied label is standalone .. app_rule = depend.Rule(iapp_label, the_action) # Now add 'em .. builder.ruleset.add(deployment_rule) builder.ruleset.add(app_rule) # .. and deal with cleanup, which is entirely generic deployment.register_cleanup(builder, name) # .. and set the environment the_action.attach_env(builder)
def register_cleanup(builder, deployment): """ Register the rule you need to clean a deployment. Cleaning a deployment basically means we remove the directory and its deployed tag. """ target_lbl = depend.Label(utils.LabelType.Deployment, deployment, None, utils.LabelTag.Clean) rule = depend.Rule(target_lbl, CleanDeploymentBuilder()) builder.ruleset.add(rule)
def _inside_of_deploy(builder, name, the_action): """This implements the common code from the public 'deploy()' function. """ dep_label = Label(utils.LabelType.Deployment, name, None, utils.LabelTag.Deployed) deployment_rule = depend.Rule(dep_label, the_action) # We need to clean it as well, annoyingly .. deployment.register_cleanup(builder, name) builder.ruleset.add(deployment_rule) # InstructionsApplied is a standalone rule, invoked by the deployment iapp_label = Label(utils.LabelType.Deployment, name, None, utils.LabelTag.InstructionsApplied, transient=True) iapp_rule = depend.Rule(iapp_label, the_action) builder.ruleset.add(iapp_rule)
def role_depends_on_deployment(builder, role, deployment, domain=None): """ Make every package in the given role depend on the given deployment """ tgt = depend.Label(utils.LabelType.Package, "*", role, utils.LabelTag.PreConfig) the_rule = depend.Rule(tgt, None) the_rule.add( depend.Label(utils.LabelType.Deployment, deployment, None, utils.LabelTag.Deployed, domain=domain)) builder.ruleset.add(the_rule)
def package_depends_on_checkout(ruleset, pkg_name, role_name, co_name, action=None): """ Make the given package depend on the given checkout * ruleset - The ruleset to use - builder.ruleset, for example. * pkg_name - The package which depends. * role_name - The role which depends. Can be '*' for a wildcard. * co_name - The checkout which this package and role depends on. * action - If non-None, specifies an Action to be invoked to get from the checkout to the package preconfig. If you are a normal (outside muddle itself) caller, then you will normally leave this None unless you are doing something deeply weird. """ checkout = depend.Label(utils.LabelType.Checkout, co_name, None, utils.LabelTag.CheckedOut) preconfig = depend.Label(utils.LabelType.Package, pkg_name, role_name, utils.LabelTag.PreConfig) new_rule = depend.Rule(preconfig, action) new_rule.add(checkout) ruleset.add(new_rule) # We can't clean or distclean a package until we've checked out its checkout # Both are transient, as we don't need to remember we've done them, and # indeed they should be doable more than once clean = depend.Label(utils.LabelType.Package, pkg_name, role_name, utils.LabelTag.Clean, transient=True) ruleset.add(depend.depend_one(action, clean, checkout)) distclean = depend.Label(utils.LabelType.Package, pkg_name, role_name, utils.LabelTag.DistClean, transient=True) ruleset.add(depend.depend_one(action, distclean, checkout))
def deploy(builder, name, rolesThatUseThis=[], rolesNeededForThis=[]): """ Register a tools deployment. This is used to: 1. Set the environment for each role in 'rolesThatUseThis' so that PATH, LD_LIBRARY_PATH and PKG_CONFIG_PATH include the 'name' deployment 2. Make deployment:<name>/deployed depend upon the 'rolesNeededForThis' 3. Register cleanup for this deployment The intent is that we have a "tools" deployment, which provides useful host tools (for instance, something to mangle a file in a particular manner). Those roles which need to use such tools in their builds (normally in a Makefile.muddle) then need to have the environment set appropriately to allow them to find the tools (and ideally, not system provided tools which mighth have the same name). """ tgt = depend.Label(utils.LabelType.Deployment, name, None, utils.LabelTag.Deployed) for role in rolesThatUseThis: for tag in (utils.LabelTag.PreConfig, utils.LabelTag.Configured, utils.LabelTag.Built, utils.LabelTag.Installed, utils.LabelTag.PostInstalled): lbl = depend.Label(utils.LabelType.Package, "*", role, tag) env = builder.get_environment_for(lbl) attach_env(builder, role, env, name) deployment.role_depends_on_deployment(builder, role, name) the_rule = depend.Rule(tgt, ToolsDeploymentBuilder(rolesNeededForThis)) builder.ruleset.add(the_rule) deployment.deployment_depends_on_roles(builder, name, rolesNeededForThis) deployment.register_cleanup(builder, name)
def pkg_depends_on_deployment(builder, pkg, roles, deployment, domain=None): """ Make this package depend on the given deployment Specifically, given each role 'r' in 'roles', make the label "package:<pkg>{<r>}/preconfig" depend on the label "deployment:<deployment>/deployed". If 'domain' is given, this is (currently) just used for the deploymet label. """ deployment_label = depend.Label(utils.LabelType.Deployment, deployment, None, utils.LabelTag.Deployed, domain=domain) for i in roles: tgt = depend.Label(utils.LabelType.Package, pkg, i, utils.LabelTag.PreConfig) the_rule = depend.Rule(tgt, None) the_rule.add(deployment_label) builder.ruleset.add(the_rule)
def add_checkout_rules(builder, co_label, action): """ Add the standard checkout rules to a ruleset for a checkout with name co_label. 'action' should be an instance of VcsCheckoutBuilder, which knows how to build a checkout: label, depending on its tag. """ ruleset = builder.ruleset # All of the VCS tags are transient (well, with the obvious exception # of "checked_out" itself). So we need to be a little bit careful. # Make sure we have the correct basic tag if co_label.tag != utils.LabelTag.CheckedOut: co_label = co_label.copy_with_tag(utils.LabelTag.CheckedOut) # And we simply use the VcsCheckoutBuilder (as we assume it to be) # to build us co_rule = depend.Rule(co_label, action) ruleset.add(co_rule) # Pulled is a transient label. pulled_label = co_label.copy_with_tag(utils.LabelTag.Pulled, transient=True) # Since 'checked_out' is not transient, and since it seems reasonable # enough that "muddle pull" should check the checkout out if it has not # already been done, then we can make it depend upon the checked_out label... # Tell its rule that it depends on the checkout being checked out (!) rule = depend.Rule(pulled_label, action) rule.add(co_label) ruleset.add(rule) # Merged is very similar, and also depends on the checkout existing merged_label = co_label.copy_with_tag(utils.LabelTag.Merged, transient=True) rule = depend.Rule(merged_label, action) rule.add(co_label) ruleset.add(rule) ## We used to say that UpToDate depended on Pulled. ## Our nearest equivalent would be Merged depending on Pulled. ## But that's plainly not a useful dependency, so we shall ignore it. #depend.depend_chain(action, # uptodate_label, # [ utils.LabelTag.Pulled ], ruleset) # We don't really want 'push' to do a 'checkout', so instead we rely on # the action only doing something if the corresponding checkout has # been checked out. Which leaves the rule with no apparent dependencies pushed_label = co_label.copy_with_tag(utils.LabelTag.ChangesPushed, transient=True) rule = depend.Rule(pushed_label, action) ruleset.add(rule) # The same also applies to commit... committed_label = co_label.copy_with_tag(utils.LabelTag.ChangesCommitted, transient=True) rule = depend.Rule(committed_label, action) ruleset.add(rule) # Centralised VCSs, in general, want us to do a 'pull' (update) before # doing a 'commit', so we should try to honour that, if necessary if (action.must_pull_before_commit(builder, co_label)): rule.add(pulled_label)
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 create(builder, target_file, name, compressionMethod = None, pruneFunc = None): """ Create a CPIO deployment and return it. * 'builder' is the muddle builder that is driving us * 'target_file' is the name of the CPIO file we want to create. Note that this may include a sub-path (for instance, "fred/file.cpio" or even "/fred/file.cpio"). * 'name' is either: 1. The name of the deployment that will contain this CPIO file (in the builder's default domain), or 2. A deployment or package label, ditto * 'comporessionMethod' is the compression method to use: * None means no compression * 'gzip' means gzip * 'bzip2' means bzip2 * if 'pruneFunc' is not None, it is a function to be called like pruneFunc(Hierarchy) to prune the hierarchy prior to packing. Usually something like deb.deb_prune, it's intended to remove spurious stuff like manpages from initrds and the like. Normal usage is thus something like:: fw = cpio.create(builder, 'firmware.cpio', deployment) fw.copy_from_role(role1, '', '/') fw.copy_from_role(role2, 'bin', '/bin') fw.done() or:: fw = cpio.create(builder, 'firmware.cpio', package('firmware', role)) fw.copy_from_role(role, '', '/') fw.done() """ if isinstance(name, basestring): label = depend.Label(LabelType.Deployment, name, None, LabelTag.Deployed, domain = builder.default_domain) elif isinstance(name, depend.Label): label = name if label.type not in (LabelType.Deployment, LabelType.Package): raise GiveUp("Third argument to muddled.deployments.cpio.create()" " should be a string or a deployment/package label," " not a %s label"%label.type) if label.type == LabelType.Deployment and label.tag != LabelTag.Deployed: label = label.copy_with_tag(LabelTag.Deployed) elif label.type == LabelType.Package and label.tag != LabelTag.PostInstalled: label = label.copy_with_tag(LabelTag.PostInstalled) else: raise GiveUp("Third argument to muddled.deployments.cpio.create()" " should be a string or a package/deployment label," " not %s"%type(name)) the_action = CpioDeploymentBuilder(target_file, [], compressionMethod, pruneFunc) the_rule = depend.Rule(label, the_action) builder.ruleset.add(the_rule) if label.type == LabelType.Deployment: deployment.register_cleanup(builder, name) return CpioWrapper(builder, the_action, label)