3.1 Declare dependencies

In the Overwiew, we explained load and remove dependencies for a group. More precisely, a load dependency group is a group that is loaded whenever the dependant is loaded, whereas a remove dependency is removed if the dependant is loaded.

May happen that the same group is declared both as a load and a remove dependency (usually by different groups). In that case, the remove dependency tooks precedence and the group is not loaded. Therefore, declaring a group as a load dependency is not a strong gaurantee that the group will at the end contribute to the generation of the systemd unit. On the other hand, declaring a group as a remove dependency definitively prevents it from contributing.

Groups declare their load and remove dependencies as child nodes inside pull and replace nodes respectively. Each node representing a dependency must have the respective group name as node name and may optionally have group as node type. For example:


def-group Group3 {}
def-group "Group 4" {
    pull {
        (group) Group3
    }
}
def-group Group5 {
    pull {
        (group) Group3
        "Group 4"
    }
}
def-group Group6 {
    pull {
        "Group 4"
    }
    replace {
        Group3
    }
}

A group listed as load dependency can still be prevented to be loaded if another group lists the same group as a remove dependency. On the contrary, groups listed as remove dependencies cannot be included by any mean because it is not possible to revert a remove dependency. The only way to load a group declared as remove dependency is to prevent the group that specifies it as a remove dependency to be loaded.

We have explained that even if a group is declared as a load dependency (for example, by putting it inside the pull block) may still happen that it is not loaded in the final systemd unit. However, Tomloader gives you some control on what happens when a loaded dependency is blacklisted.

Dependencies declared inside the pull node may be declared needed or mandatory. A needed dependency is fundamental and the group cannot behave correctly without it. Marking a load dependency as needed does not prevent that dependency from being removed, because that would violate the load-remove relationship stated before. Instead, if a load needed dependency is blacklisted then Tomloader would either issue an error and block the unit generation, or just dropping the dependant group. Both these approaches do not violate the load-remove assumption and at the same time do not leave loaded any group with missing needed load dependencies.

Needed dependencies inside pull are declared through the needed child node:


def-node Group {
    pull {
        (group) NeededDep {
            needed #true
        }
    }
}

The needed node accepts either a boolean (a KDL boolean #true, #false or the special strings "true", "false" since double quotes " are mandatory for "true", "false" as specified by KDL specifications) or the value discard. If not specified, needed is set to false.

Setting needed to true will automatically block the generation of a systemd unit if the needed dependency has been blacklisted somewhere else during the generation. This approach is useful mostly when the unit is manually generated and the needed dependency must be present in the final systemd unit. In this way, tomloader will notice you if by mistake you have removed that dependency (usually by loading another group that removed it).

The special value discard for needed will instead unload the entire group if the dependency is unloaded. In the following example


def-group GroupA {}
def-group GroupB {
    pull {
        GroupA {
            needed discard
        }
    }
}

trying to load GroupB and at the same time blacklisting GroupA will still generate a unit but will automatically remove GroupB too. Several discard options could unload several groups at once, for example in


def-group GroupA {}
def-group GroupB {
    pull {
        GroupA {
            needed discard
        }
    }
}
def-group GroupC {
    pull {
        GroupC {
            needed discard
        }
    }
}

blacklisting GroupA will unload both GroupB and GroupC.

A group may appear in both pull and replace. In this case, the group itself is excluded while its dependencies remain included. Indeed, dependencies are transitive by default:

  1. dependencies declared inside pull propagate both their load and remove dependencies;
  2. dependencies declared inside replace propagate only their remove dependencies.

Transitive behaviour can be controlled through the child node inherit. For example, inherit #false will prevent all transitive dependencies from being loaded:


def-group Group7 {
    pull {
        // Group4 is loaded as a pull dependency
        // Group3 is loaded as a replace
        // dependency
        Group6
    }
}
def-group Group8 {
    pull {
        // only Group6 is loaded as load
        // dependency, no further groups
        // will be loaded here.
        Group6 {
            inherit #false
        }
    }
}

Warning: using inherit #false for a group dependency with needed dependencies will prevent those transitive dependencies to be loaded, which will usually result in an error or in cascading gropus unloading.

In general, inherit accepts any boolean as explained before, with inherit #true reverting to the default behaviour. For remove dependencies, inherit also accepts the string pulls as value. With this property, all the transitive load dependencies are instead loaded as remove dependencies.


def-group Group9 {
    replace {
        // Group3 and Group4 are loaded as
        // replace dependencies
        Group6 {
            inherit pulls
        }
    }
}

Groups can declare dependencies also inside a merge node the same way you declare them in pull or replace node. Groups specified inside a merge are loaded as remove dependencies but with the following differences from usual remove dependencies:

Warning: transitive needed dependencies of a group listed in merge will still be imported even if inherit #false has been specified. Therefore, needed dependencies should be loaded elsewhere in order to prevent a generation failure or unloading the entire group.

The inherit child of a dependency specified inside a merge node accepts the special value deep other than #true and #false. For each dependency inside merge with inherit deep:

Loading multiple groups inside merge node may generate conflits when one or more fields are modified by different groups. Section Conflicts explains how to manage and resolve conflicts.