--- Goal Resolution Discussion --- The Maven Team --- 21-Sept-2004 --- Goal Resolution This document is intended to be a working memory of sorts for the goal resolution algorithm used in Maven-2. It is not intended to supplant the living bible of Maven, the lifecycle.apt document. Instead it is meant to add depth to the goal resolution step of the lifecycle and provide a place for discussing the dirty details of its implementation. *Conceptual Resolution Conceptually, goal resolution must take place in a particular order in order to preserve the encapsulation of the goal and it's implied requirements as a single operation. Obviously, since implied requirements are themselves goals, this is a recursive definition. This model is further complicated by the fact that such implied requirements can be derived from two sources: a mojo's declared pre-requisites (non-optional, these are required for correct operation of the mojo itself), and any goal decorations which may have been declared in a project-specific manner via the project POM. In general, the following should be the combined outcome of all implied requirements. +-----+ [ main-preGoal* ] [ prereq ]* main-goal [ main-postGoal* ] +-----+ Note that each of the elements in this formula is a goal in its own right, and will therefore be subject to resolution (taking the place of <<>> above) and substitution into the parent (replace <<>> with the list of goals implied by <<>> ). *Functional Resolution The fact we must merge two sources of implied-goal information in order to construct a complete execution chain complicates goal resolution beyond simple use of a directed acyclical graph (DAG). This is especially true because of the differing lifecycle and scope of the two sources. One is the plugin descriptor, which has a lifecycle arguably longer than the JVM lifecycle itself, and system-wide scope (provided plugin versioning is tracked). The other is the project's POM, which has an extremely short lifecycle (probably roughly equivalent to the maven-session lifecycle, which is the same as the JVM lifecycle in some embedded use cases), and only project scope (don't want one POM's decorations polluting the builds of other POMs). While the plugin descriptor information may be cached - even to disk - the POM goal information must be rebuilt for each maven-session. On the other hand, one very important distinction between information derived from plugin descriptors and information from POMs is that POMs lack the ability to declare new goals. This means that in theory we should be able to clone a DAG that describes all of the conventional requirements for all of the plugins referenced, and simply add inter-goal relationships to account for any extra decoration from the POM. When the session is over, we can then discard the modified DAG, retaining the plugin-derived DAG for future use in memory or on disk. There are other reasons to be very careful when caching the DAG to disk, notably the handling and updating of -SNAPSHOT plugins. *Algorithm Here is the current algorithm implemented by the GoalResolutionPhase: The separation of the plugin resolution step from the process of actually building the execution chain is new, and has not yet been implemented. However, this appears to be a required separation since the DAG cannot function properly until all plugins - and consequently the prereqs implied by them - are resolved. So, we'll take a second pass later to actually build the execution list; for now, we'll just resolve plugins. <> Can we re-separate this as a plugin-resolution phase, and provide some sort of reusable tree-visit logic (akin to the topo sorter) where we could pass in some sort of visitation command to execute per visited goal? [[1]] Initialize recursion [[a]] Instantiate Set for caching resolved goals. [[b]] Set current goal to be resolved. [] [[2]] If is contained in , Return. [[3]] Verify plugin for . [[4]] Add to resolved. [[5]] Foreach of , [[a]] Set = [[b]] Call [2] [] [[6]] Foreach of , [[a]] Set = [[b]] Call [2] [] [[7]] Foreach of , [[a]] Set = [[b]] Call [2] [] [[8]] Return. [] <> Visitation logic is eerily similar to the above recursive process. Can we create a graph visitation utility and pass in some sort of command to be executed per visited node? [[1]] Initialize recursion [[a]] Instantiate LinkedList to hold execution chain. [[b]] Instantiate Set for caching visited goals. [[c]] Set current goal to be executed. [] [[2]] If contains , Return. [[3]] Process preGoals of [[a]] Retrieve List of preGoals bound to [[b]] Foreach in [[i]] Set = [[ii]] Call [1] [] [] [[4]] Process prereqs of [[a]] Retrieve List of prereqs bound to [[b]] Foreach in [[i]] Set = [[ii]] Call [1] [] [] [[5]] Add to [[6]] Add to [[7]] Process postGoals of [[a]] Retrieve List of postGoals bound to [[b]] Foreach in [[i]] Set = [[ii]] Call [1] [] [] [[8]] Return. [] Since the user's intent most likely aligned with separate, serial execution of all goals listed on the command line , the above algorithm must be repeated for each in , with the execution chains of each being appended to a single list in order to resolve a complete, end-to-end picture of the current build session.