[Go deep into Maven source code] maven binds command line parameters to specific plug-ins and maven command line parameters
Maven plugin
We know that the specific building actions of Maven are implemented by the plug-in. maven itself only provides a framework, which provides highly customizable functions, when we use maven commands to execute commands such as mvn clean package, maven will bind the package phase (phase) to the corresponding lifecycle, and then look for a project) and execute the specific plugin to complete continuous build.
Maven plugin)
After reading the command line, maven parses the command line parameters into task parameters based on whether the command line parameters are the system's default phase or other custom plug-ins (goal, the task parameters are used to generate a list of plug-in executions.
for ( Object task : tasks ) { if ( task instanceof GoalTask ) { String pluginGoal = ( (GoalTask) task ).pluginGoal; MojoDescriptor mojoDescriptor = mojoDescriptorCreator.getMojoDescriptor( pluginGoal, session, project ); MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, "default-cli", MojoExecution.Source.CLI ); mojoExecutions.add( mojoExecution ); } else if ( task instanceof LifecycleTask ) { String lifecyclePhase = ( (LifecycleTask) task ).getLifecyclePhase(); Map<String, List<MojoExecution>> phaseToMojoMapping = calculateLifecycleMappings( session, project, lifecyclePhase ); for ( List<MojoExecution> mojoExecutionsFromLifecycle : phaseToMojoMapping.values() ) { mojoExecutions.addAll( mojoExecutionsFromLifecycle ); } } else { throw new IllegalStateException( "unexpected task " + task ); } }
In the code, first determine the type of the task. If it is a GoalTask, it indicates that the parameter is clean: clean, a format with goal, this situation may be caused by maven built-in plug-ins or plug-ins developed by developers themselves. In this case, maven will first find the specific plug-ins Based on the plug-in's goal, the method is to first find the plug-in defined by the project. If you cannot find the plug-in, you can find it in the repository. Here, paste the core code:
PluginDescriptor pluginDescriptor = pluginManager.loadPlugin( plugin, request.getRepositories(), request.getRepositorySession() ); if ( request.getPrefix().equals( pluginDescriptor.getGoalPrefix() ) ) { return new DefaultPluginPrefixResult( plugin ); }
This part is relatively simple, that is, to traverse the plug-ins in the project and load them one by one, and then compare whether the goal prefix of this plug-in is the same as that of the command line request. If it is the same, encapsulate the plug-in to return. Other command line parsing methods in the groupId: artifactId: version: goal format are similar. The response plugin plug-in is loaded based on the information. Next, we will focus on the complicated situation, that is, the built-in phase like clean package. The following code will explain one by one: Determine which lifecycle is based on lifecyclephase, for example, the phase package is defined in the default lifecycle. maven has three built-in lifecycles: clean, default, and site. maven uses plexus as the IOC container, the defaultLifeCycles dependency is in the component of maven-core. the phase in each lifecycle is defined in xml. If you are interested, you can check the corresponding code. Back to the code here, maven finds the default lifecycle Based on phase by default. Here, the default lifecycle is found through package.
/* * Determine the lifecycle that corresponds to the given phase. */ Lifecycle lifecycle = defaultLifeCycles.get( lifecyclePhase ); if ( lifecycle == null ) { throw new LifecyclePhaseNotFoundException( "Unknown lifecycle phase \"" + lifecyclePhase + "\". You must specify a valid lifecycle phase" + " or a goal in the format <plugin-prefix>:<goal> or" + " <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>. Available lifecycle phases are: " + defaultLifeCycles.getLifecyclePhaseList() + ".", lifecyclePhase ); }
Next, traverse all the phase under the default lifecycle until the phase package exits the traversal cycle. This shows a feature of maven, that is, if you specify a phase in a lifecycle, the phase before this phase will be executed. Here, we mainly initialize the phase contained in mappings. The reason why the list of plug-ins for each phase is TreeMap is because it is followed by priority, that is, sort keys to traverse and determine the execution sequence of the plug-in. The plug-in to be executed by each phase is written to the next code segment.
/* * Initialize mapping from lifecycle phase to bound mojos. The key set of this map denotes the phases the caller * is interested in, i.e. all phases up to and including the specified phase. */ Map<String, Map<Integer, List<MojoExecution>>> mappings = new LinkedHashMap<String, Map<Integer, List<MojoExecution>>>(); for ( String phase : lifecycle.getPhases() ) { Map<Integer, List<MojoExecution>> phaseBindings = new TreeMap<Integer, List<MojoExecution>>(); mappings.put( phase, phaseBindings ); if ( phase.equals( lifecyclePhase ) ) { break; } }
Next, we traverse all the plug-ins in this project. Each plug-in traverses all the execution configurations. If phase is specified in the execution configuration, add all the Mojo corresponding to the goal under the execution to the execution map of the corresponding phase. If phase is not specified in the execution configuration, traverse all the goal under the execution, obtain the Mojo description of the goal in sequence, and add the Mojo to the execution map of the corresponding phase according to the phase bound to each Mojo.
/* * Grab plugin executions that are bound to the selected lifecycle phases from project. The effective model of * the project already contains the plugin executions induced by the project's packaging type. Remember, all * phases of interest and only those are in the lifecyle mapping, if a phase has no value in the map, we are not * interested in any of the executions bound to it. */ for ( Plugin plugin : project.getBuild().getPlugins() ) { for ( PluginExecution execution : plugin.getExecutions() ) { // if the phase is specified then I don't have to go fetch the plugin yet and pull it down // to examine the phase it is associated to. if ( execution.getPhase() != null ) { Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( execution.getPhase() ); if ( phaseBindings != null ) { for ( String goal : execution.getGoals() ) { MojoExecution mojoExecution = new MojoExecution( plugin, goal, execution.getId() ); mojoExecution.setLifecyclePhase( execution.getPhase() ); addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() ); } } } // if not then i need to grab the mojo descriptor and look at the phase that is specified else { for ( String goal : execution.getGoals() ) { MojoDescriptor mojoDescriptor = pluginManager.getMojoDescriptor( plugin, goal, project.getRemotePluginRepositories(), session.getRepositorySession() ); Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( mojoDescriptor.getPhase() ); if ( phaseBindings != null ) { MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, execution.getId() ); mojoExecution.setLifecyclePhase( mojoDescriptor.getPhase() ); addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() ); } } } } }
After the previous steps, the Mojo execution list corresponding to all phase has been obtained. Next, we need to concatenate all phase Mojo into a general list. Here, mappings is a LinkedHashMap, therefore, there is a sequence during traversal, And the execution map of each phase is TreeMap, Which is sorted by priority. In this way, the overall sequence is first followed by the overall phase sequence, then, sort by the priority in phase.
Map<String, List<MojoExecution>> lifecycleMappings = new LinkedHashMap<String, List<MojoExecution>>(); for ( Map.Entry<String, Map<Integer, List<MojoExecution>>> entry : mappings.entrySet() ) { List<MojoExecution> mojoExecutions = new ArrayList<MojoExecution>(); for ( List<MojoExecution> executions : entry.getValue().values() ) { mojoExecutions.addAll( executions ); } lifecycleMappings.put( entry.getKey(), mojoExecutions ); }