Skip to content

Instantly share code, notes, and snippets.

@freekh
Last active August 29, 2015 14:06
Show Gist options
  • Save freekh/246ea62bb4f7ea9e34f3 to your computer and use it in GitHub Desktop.
Save freekh/246ea62bb4f7ea9e34f3 to your computer and use it in GitHub Desktop.
New configuration model execution summary (according to me)

NOTE: This document was written the 16th September 2014 and are based on notes taken before that - it might be wrong, outdated or misleading.

The new configuration model introduces the concepts of rules and models. A rule may add or mutate the model, which defines data which a user can define. Rules, models and tasks all live in plugins. The model is loaded eagerly when the the plugin is applied to a project, while the rules are lazily (when needed) evaluated.

Rules

In practice, rules are annotations on methods and there are currently 3 types: immutable (@Model), mutable (@Mutate) and finalized (@Finalize). The parameters of the method (subjects) are the dependencies. For mutable rules, the first parameter indicates that it is the mutating subject (the object which we will mutate/change), while the rest are the dependencies of the rule. An immutable rule may produce the types used by other rules. An example of this is if an immutable 'rule1' maps a model to another type, a mutable (or immutable) 'rule2' might define a dependency of this type (which would be populated by 'rule1'). If the model contains N objects with the types specified in the parameters, the rules will fire N times. It is also possible to add a path annotation (@Path) to a parameter to avoid firing a rule too often (I am (REALLY) not sure but I think a Path is a view as described in the documents). As for finalized rules, they are mutating rules that are run after all mutating rules are finished (again, this is only what I think). See below for an example of a mutating rule:

@Mutate
public void createBinaries(BinaryContainer binaries, NamedDomainObjectCollection<JvmLibrarySpec> libraries, @Path("buildDir”) File buildDir)

This rule will fire for every combination of the binaries, libraries and buildDir (with the path “buildDir") present in the model. The intent indicated (it is not checked yet) is that every BinaryContainer will be mutated (in this example: we can imagine we want to add libraries to the binaries), with every combination of NamedDomainObjectCollection and File matching the path “buildDir”.

Rules and tasks

Tasks in Gradle have properties (fields/methods) defining inputs, output and an action method which defines the behaviour. As most know, tasks are what users actually end up executing. Properties are defined with a Task and sometimes we need a way to pass properties between them (I am assuming). For example: the input directory of one task is the output directory of another. The relationship (again: as I understand it) to tasks and rules is that rules are responsible for mapping the model to tasks, either by creating new tasks or by configuring existing tasks. This way, the model defining the user input can be coherently mapped to multiple different tasks. I have tried to create a diagram of the flow of plugins, models, rules and tasks here (sorry for my bad Google Apps presentation skillz): https://docs.google.com/presentation/d/13LKYNnpRJi7oD_QWYSW6L0AxfyDOIUIqhYGYjjsbdtU/edit?usp=sharing

Model

Models defines what properties a user can customize. As mentioned previously the models are loaded eagerly when a plugin is applied. This matters, because it means that we can safely execute our rules on top of the model. In practice defining a model, means to register a container in the modelRegistery (a plugin service). A simple example of model usage registery (and rules) is the LanguageBasePlugin found here: https://github.com/gradle/gradle/blob/946cdfa82264a63dfb3c09681d8d999a6456b573/subprojects/platform-base/src/main/java/org/gradle/language/base/plugins/LanguageBasePlugin.java#L74.

@bigdaz
Copy link

bigdaz commented Sep 16, 2014

A few notes:

  • Rules define the model. Saying "The model is loaded eagerly when the the plugin is applied to a project" isn't really correct, except perhaps for legacy model elements that are not yet created by rules.

  • Calling @model rules "immutable rules" isn't really right. These are "creation" rules that add a new element to the model: we could just as easily use @create for these rules.

  • Rules have 2 types of parameters: "subject" and "input" parameters. For @model rules, all parameters are inputs. For @Mutate and @finalize, the first parameter is the subject, the rest are inputs.

  • Currently each rule runs only once, and the set of parameters must be unambiguous. So for the rule example you give, there must be exactly one model element that can be viewed as type BinaryContainer and one that can be viewed as type NamedDomainObjectCollection<JvmLibrarySpec>. The @Path annotation serves to disambiguate all of the possible File elements in the model, by choosing a particular one.

  • Tasks are just model elements, just like the containers and files that serve as rule inputs. They don't live outside of the model like you have in your diagram. Tasks are created by rules, currently by mutating the TaskContainer. Each task has a unique path in the model, so you could create a @Mutate rule that had a signature like @Mutate void configureTask(@Path("tasks.myTask") Task foo)

  • The code you reference in LanguageBasePlugin is using an internal API to define a "ModelCreator" rule (the same as @model defines). Using the API is required because the BinaryContainer can present different "views", so the rule actually adds an adapter to the model rather than the raw type. But it's still defining a rule, which is only executed on demand if another rule requires the BinaryContainer as input.

  • One of the "views" that a BinaryContainer can present is CollectionBuilder<? extends Binary>, allowing rules like this:

     @Mutate
     void createSampleBinaries(CollectionBuilder<SampleBinary> binaries) {
          binaries.create("sampleBinary")
     }
    

@ldaley
Copy link

ldaley commented Sep 17, 2014

I don't have anything to add over and above Daz's comments.

@freekh
Copy link
Author

freekh commented Sep 17, 2014

Ok, this makes sense.

PS: the error messages that you get on rules (say if a rule cannot be applied) are what inspired me to name them "immutable"/"mutable".

@radimk
Copy link

radimk commented Sep 17, 2014

My takeover is that according to Daz @model means CreateModel and @Mutate is UpdateModel (+ there is @finalize to finish/lock the model). I like that way of explaining how it works.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment