[the most detailed explanation of Maven source code in history]

Posted by kslagdive on Tue, 23 Nov 2021 03:12:12 +0100

Maven source code reading

Where is the Main function?

In line 109 of apache-maven-3.8.3 \ Apache maven \ SRC \ bin \ MVN

108 CLASSWORLDS_JAR=`echo "${MAVEN_HOME}"/boot/plexus-classworlds-*.jar`
109 CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher

Org. Codehaus. Plexus. Classworlds. Launcher. Launcher (this is where our dream starts: the main function is here)

org.codehaus.plexus.classworlds.launcher.Launcher#main
    public static void main( String[] args )
    {
        try
        {
            int exitCode = mainWithExitCode( args ); // TODO core method

            System.exit( exitCode );
        }
        catch ( Exception e )
        {
            e.printStackTrace();

            System.exit( 100 );
        }
    }

How to start?

Description: idea development tool

JVM parameters

Note: refer to idea startup parameters for specific parameters

-Dclassworlds.conf=E:\#learning\apache-maven-3.8.3\apache-maven\src\bin\m2.conf -Dmaven.home=D:\apache-maven-3.8.3 -Dmaven.conf=D:\apache-maven-3.8.3\conf -Dclassworlds.conf=D:\apache-maven-3.5.4\bin\m2.conf -Dmaven.multiModuleProjectDirectory=E:\#adt\audit-trail-ms -Duser.dir=E:\#adt\audit-trail-ms

Own doubts:

What is the difference between maven.multiModuleProjectDirectory and user.dir? The default user.dir is our source code project path. maven.multiModuleProjectDirectory has no default value and must be filled in. The execution path of our mvn command is configured by user.dir. Why two must be configured? This place still needs in-depth understanding. If you know, your understanding is given in the partner comment area. Thank you!

Example of program parameters

clean package

Principle of script startup

maven essentially executes commands, because the maven construction framework is fixed and plug-ins are changed. Therefore, the first level commands (mvn -v, which we usually see to view the maven version, is this mvn) are consistent:

The first level command depends on the name of the script file. There is no doubt that the logical meaning of the script file is one of the cores of maven. Next, take an in-depth look at what these scripts do. Take mvn.cmd as an example (compared with MVN, the file has no difference except syntax).

Once you can change the mvn name and change the command: example mvn - > mvn - copy

D:\apache-maven-3.8.3\bin>mvn - copy -v
Apache Maven 3.8.3 (ff8e977a158738155dc465c6a97ffaf31982d739)
Maven home: D:\apache-maven-3.8.3
Java version: 1.8.0_151, vendor: Oracle Corporation, runtime: D:\Java\jdk1.8.0_151\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"

Start the cmd command from the command line

for %%i in ("%MAVEN_HOME%"\boot\plexus-classworlds-*) do set CLASSWORLDS_JAR="%%i"
@REM Specifies the to execute the command jar
set CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher

"%JAVACMD%" ^
  %JVM_CONFIG_MAVEN_PROPS% ^
  %MAVEN_OPTS% ^
  %MAVEN_DEBUG_OPTS% ^
  -classpath %CLASSWORLDS_JAR% ^
@REM classworlds Profile address
  "-Dclassworlds.conf=%MAVEN_HOME%\bin\m2.conf" ^
@rem maven home
  "-Dmaven.home=%MAVEN_HOME%" ^
@rem jansi Location of
  "-Dlibrary.jansi.path=%MAVEN_HOME%\lib\jansi-native" ^
@rem Location of command execution, The default is maven Project catalogue
  "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
@rem Automatic call classworlds.launcher.Launcher of main method, And pass the command line parameters to main of args
  %CLASSWORLDS_LAUNCHER% %MAVEN_CMD_LINE_ARGS%

The script mainly does four things

  1. Associated configuration m2.conf;
  2. Associate the entry plexus-classworlds-2.6.0.jar (de version association) and the entry method;
  3. Write configuration parameters;
  4. Intercepting command input as parameter to execute method 2;

Here we debug the code by directly starting the java code through the source code

org.codehaus.plexus.classworlds.launcher.Launcher#main

The first line of code to start, where the dream began

mainWithExitCode

launcher.configure( is ); load configuration

launcher.launch( args ); Dream to set sail

    public void launch( String[] args )
        throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
        NoSuchRealmException
    {
        try
        {
            launchEnhanced( args ); // Enhanced startup mainly depends on this

            return;
        }
        catch ( NoSuchMethodException e )
        {
            // ignore
        }

        launchStandard( args ); // Use standard startup after exception
    }

launchEnhanced: enhanced startup

Object ret = mainMethod.invoke( mainClass, new Object[]{args, getWorld()} ); Reflection call

MavenCli#main(String[], ClassWorld): Maven command line

doMain: core process

public int doMain(CliRequest cliRequest) {
        PlexusContainer localContainer = null; // Cluster container
        try {
            // The following comments are from: https://www.codenong.com/cs106269680/ In case of infringement, please contact us for deletion
            // Both core paths have default values
  			// Improve the working path workingDirectory. If it is empty, it defaults to the current user path and the multi module project path is multiModuleProjectDirectory
			// If it is empty, take maven.multiModuleProjectDirectory environment variable
            // These two parameters can only be set through command parameters, because there is no set method and other assignment places
            // maven.home resolve absolute path
            initialize(cliRequest); // Initialization Directory: TODO focus on the following
            // Improve commandLine, parse commands, and match the built-in commands - v, - h
            // Judge whether the commandLine is - v or - h, print the corresponding information on the console and exit
            cli(cliRequest); // Parse command
			//Write the system parameters and user parameters into the request, and set the systemProperties and userProperties properties properties
            //1. Set the system parameter System.getenv(), some system level parameter variables
            //2. Set the run related parameter System.getProperties(), mainly java related properties
            //3. Set maven build version information, mainly from / org/apache/maven/messages/build.properties
            //  Get the build information in: get it under the messages package of Maven core, that is, some version and publisher information
            properties(cliRequest); // Merge properties
			//Improve the log attribute configuration in the execution request, - X,-q,-e set the maven executionrequest log level, which is actually the log level of the execution plug-in
            //Set whether the message font color of the print log is enabled. The bottom layer is to use org.fusesource.jansi.Ansi to control multi-color output;
            //If the command contains - b and - l parts, the console output font color is the default color
            //For the - l file command, the redirect log is output to file, and the System log is written through PrintStream, including setOut and setErr;
            logging(cliRequest); // Set log
            informativeCommands(cliRequest); // Exception prompt
            //In debug mode, print version information after parsing to - V
            version(cliRequest); // Print version
            //Build Maven.class, which is responsible for executing the build behavior specified by maven, and assemble some parameters
            // maven = container.lookup(Maven.class); DefaultMaven will be used for subsequent processing pom
            localContainer = container(cliRequest); // Build container
            //Print whether some setting information is enabled, such as error log and checksum_ POLICY_ Whether fail is enabled
            commands(cliRequest);
            //EventSpyDispatcher initialization and setting, - s,-gs command parsing, setting user profile and global profile
            // Set parameters, parse settings.xml, TODO core logic
            configure(cliRequest);
            //-t. - GT command parsing, setting toolchains
            toolchains(cliRequest);
			//1. Verify the obsolete maven commands, {"up", "npu", "cpu", "npr"}, and print warning information
            //2. Parse - B (batch), - nsu (Progress snapshot update), - N (not recursive into subprojects), - ff (stop building when the build response heap fails for the first time)
            //-FAE (only the currently failed builds will be set as failed, and other unaffected builds will continue), - fn (all build failures will be ignored, regardless of whether the build task fails or not) [ff,-fae,-fn is the first in sequential detection]
            //-O (set offline operation ID), - U (identify whether to update snapshot)
            //-C and - C (mutually exclusive). When the checksums do not match, what processing strategy is used? The default is - C (warning) - C will make the checksums do not match and cause the build to fail and exit
            //-P xxx,xxxx (used to introduce the configuration file list, separated by commas. The format and parsing process of the configuration file are not involved here)
            //-ntp (the progress information is not displayed during upload or download, and the priority is the highest in the progress display), and then if there are log files such as -l xxxx.log,
            //Use Slf4jMavenTransferListener to listen and write to the log file. If not and the log level is debug, use consolemaven transferlistener to listen to the progress
            //-f xxxx/pom.xml, load the pom.xml file into the execution parameters. If it is not set, it defaults to the POM file under the basic path. If there is a parent file, load and reference it
            //-rf xxxxx (when the build fails, execute again from a specific module, and the option can be followed by [groupId]:artifactId)
            //-pl xxxxxx (project list, the relative path of multiple modules separated by commas, or the module is expressed in the form of [groupId]:artifactId), and each parameter item followed will be expressed in +, -,!, Or other symbols. If + or other symbols are the header, they will be exclude d, and the rest will be include d
            //-am xxx, - amd xxx, XXX indicates the module. Set the compilation behavior or compilation scope. The former means to compile the module (upstream make) that the selected module depends on at the same time, and the latter means to compile the part (downstream make) that depends on the selected module. Select it according to the actual demand. If it is not specified, both upstream and downstream modules will be loaded by default when making
            //Load maven.repo.local in user configuration and system configuration (user configuration is not obtained), and load the local warehouse path into the operation parameters
            //-T xxx (set the number of concurrent parallel threads for construction, which can be followed by C, indicating that the number of threads needs to be multiplied by the number of processors available to the jvm at the current runtime, that is, so many threads are distributed for each kernel and obtained through Runtime.getRuntime().availableProcessors())
            //-b xxxxxx (specify the id of the builder, and the default is multithreaded builder)
            populateRequest(cliRequest); // Populates a series of command parameters
            //-emp xxx (a separate tool that encrypts and prints the master password) does not participate in the construction process
            //-ep xxx (a separate tool that encrypts and prints the server password) does not participate in the construction process
            encryption(cliRequest); // Encryption parameter processing
			//There are two ways to determine whether to use the local library of the old version of Maven 2 instead of the remote warehouse:
            //-llr (i.e. legacy local repository, setting the old version parameter)
            //If the system configuration maven.legacyLocalRepo is true, the old version Maven library is used
            repository(cliRequest); // Handle legacy local warehouses
            //Execute command input parameters
            return execute(cliRequest); // implement
        } catch (ExitException e) {
            return e.exitCode; // exit, the system exits
        } catch (UnrecognizedOptionException e) {
            // Pure user error, suppress stack trace: pure user error, suppress stack trace
            return 1;
        } catch (BuildAbort e) {
            CLIReportingUtils.showError(slf4jLogger, "ABORTED", e, cliRequest.showErrors);
            return 2;
        } catch (Exception e) {
            CLIReportingUtils.showError(slf4jLogger, "Error executing Maven.", e, cliRequest.showErrors);
            return 1;
        } finally {
            if (localContainer != null) {
                localContainer.dispose();
            }
        }
    }

Mavencli#execute: core entry for Maven execution

  • MavenCli#execute: maven
private int execute(CliRequest cliRequest) throws MavenExecutionRequestPopulationException { // Get defaultmaveneexecutionrequestpopulator defaultmaveneexecutionrequest populator for local warehouse
    MavenExecutionRequest request = executionRequestPopulator.populateDefaults(cliRequest.request);

    eventSpyDispatcher.onEvent(request); // Request event notification
    // TODO core execution logic
    MavenExecutionResult result = maven.execute(request); // maven command execution, the core of the core

    eventSpyDispatcher.onEvent(result); // Result time notification

    eventSpyDispatcher.close(); // Monitor event scheduler shutdown
}
  • org.apache.maven.DefaultMaven#execute

​ org.apache.maven.DefaultMaven#doExecute

​ MavenExecutionResult result = new DefaultMavenExecutionResult();

​ org.apache.maven.DefaultMaven#doExecute

private MavenExecutionResult doExecute(MavenExecutionRequest request, MavenSession session,
            MavenExecutionResult result, DefaultRepositorySystemSession repoSession) {
        eventCatapult.fire(ExecutionEvent.Type.ProjectDiscoveryStarted, session, null); // Started processing the project. TODO is mainly a notification event
        // Build pom
        Result<? extends ProjectDependencyGraph> graphResult = buildGraph(session, result); // All POMS
        try {
            session.setProjectMap(getProjectMap(session.getProjects())); // Set project map: pom location of the project
        } catch (DuplicateProjectException e) {
            return addExceptionToResult(result, e);
        }

        WorkspaceReader reactorWorkspace;
        try { // Reactor, get reactor
            reactorWorkspace = container.lookup(WorkspaceReader.class, ReactorReader.HINT);
        } catch (ComponentLookupException e) {
            return addExceptionToResult(result, e);
        }
        // Set workspace reader
        repoSession.setWorkspaceReader(
                ChainedWorkspaceReader.newInstance(reactorWorkspace, repoSession.getWorkspaceReader()));

        repoSession.setReadOnly();

        graphResult = buildGraph(session, result); // todo: core project pom information

        try {
            result.setProject(session.getTopLevelProject());

            validatePrerequisitesForNonMavenPluginProjects(session.getProjects());

            lifecycleStarter.execute(session); // todo: core, life cycle construction

            validateActivatedProfiles(session.getProjects(), request.getActiveProfiles());
        } finally {
        }
        return result;
    }
  • org.apache.maven.lifecycle.internal.LifecycleStarter#execute

    builder.build( session, reactorContext, projectBuilds, taskSegments, reactorBuildStatus );
    
  • org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder#build

    There is also a multithreaded, similar to

    public void build(MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
            List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus) {
        for (TaskSegment taskSegment : taskSegments) {
            for (ProjectSegment projectBuild : projectBuilds.getByTaskSegment(taskSegment)) {
                try { // todo: core construction project
                    lifecycleModuleBuilder.buildProject(session, reactorContext, projectBuild.getProject(),
                            taskSegment); // Life cycle construction
                    if (reactorBuildStatus.isHalted()) {
                        break;
                    }
                } catch (Exception e) {
                    break; // Why are we just ignoring this exception? Are exceptions are being used for flow control
                }
            }
        }
    }
    
  • org.apache.maven.lifecycle.internal.LifecycleModuleBuilder#buildProject

    Core in core
    Note reference: https://www.codenong.com/cs106269680/ In case of infringement, please contact us for deletion

  1. Set the initialization properties of the build
  2. Verify accessibility of local dependent Libraries
  3. Create a repository systemsession
  4. Create MavenSession
  5. Execute abstractlifecycle participant.aftersessionstart (session)
  6. Gets the object that verifies pom object error
  7. Create a ProjectDependencyGraph to adjust the – projects and reactor modes (ensure that all projects passed to the ReactorReader are only the specified projects)
  8. Create a ReactorReader to obtain the object map (getprojectmap (projects)). When obtaining, the uniqueness of the objects will be verified. These objects are the collection of objects obtained in step 6
  9. Execute abstractlifecycle participant.afterprojectsread (session) post processing
  10. Create a ProjectDependencyGraph without further adjustment. This work has been done in step 7. Here, the topological sorting of abstractlifecycle participants will be completed, which may change the dependency and affect the construction order
  11. Start executing LifecycleStarter.start()
    //=============================================================================
    //The above steps are the preparation steps for the execution of the build, followed by the detailed steps of the build, which will involve obtaining the configuration of the maven plug-in and the execution of the plug-in goal until each plug-in executes its own build logic:
    1. The goal execution of each plug-in is based on a certain life cycle. Here is the entry to execute a goal of the plug-in: org.apache.maven.lifecycle.internal.LifecycleStarter#execute(MavenSession)
    2. Execute DefaultLifecycleTaskSegmentCalculator#calculateTaskSegments to obtain the goal set corresponding to the configuration plug-in. If it cannot be found, it will use MavenSession.getTopLevelProject().getDefaultGoal() by default to build maven (the obtained construction tasks are placed in TaskSegment)
    3. According to the construction tasks obtained in 2, the set of construction objects is calculated and encapsulated in the ProjectBuildList. In fact, a mapping is made. All projects in the current MavenSession must execute the tasks in the TaskSegment set
    4. Get the builderId in the execution parameters, which can be divided into two types: single instance construction or multi-threaded construction. The default is multi-threaded. You can set the number of threads through the - T command, and then start the construction by specifying the builder
    5. Construction logic: single case construction traverses the task segment and the construction object set ProjectBuildList one by one, and multithreading builds concurrently according to the specified number of threads as the upper limit
    6. A project construction strategy, calculate the Maven execution plan of the current project, unify the PluginManagement and BuildPlugin (build module version) in the project, and obtain MojoExecution,
public void buildProject(MavenSession session, MavenSession rootSession, ReactorContext reactorContext,
        MavenProject currentProject, TaskSegment taskSegment) {
    session.setCurrentProject(currentProject);
    sessionScope.enter(reactorContext.getSessionScopeMemento());
    sessionScope.seed(MavenSession.class, session);
    try {
        eventCatapult.fire(ExecutionEvent.Type.ProjectStarted, session, null);
        //Get build execution plan
        MavenExecutionPlan executionPlan = builderCommon.resolveBuildPlan(session, currentProject, taskSegment,
                new HashSet<Artifact>());
        //Get the actuator through the execution plan
        List<MojoExecution> mojoExecutions = executionPlan.getMojoExecutions();

        projectExecutionListener.beforeProjectLifecycleExecution(
                new ProjectExecutionEvent(session, currentProject, mojoExecutions));
        //Execute the plug-in construction task, and the internal part is the actuator corresponding to the circular execution of mojoExecutions,
        //The specific logic is to get the Mojo interface instance corresponding to the plug-in through MavenPluginManager
        //Then execute the Mojo instance to execute the extension interface logic to obtain the powerful extension capability provided by the plug-in
        mojoExecutor.execute(session, mojoExecutions, reactorContext.getProjectIndex()); // todo core: resolving dependencies
    } catch (Throwable t) {
    } finally {
    }
}
  • org.apache.maven.lifecycle.internal.MojoExecutor#execute

    • org.apache.maven.lifecycle.internal.MojoExecutor#execute
    • org.apache.maven.lifecycle.internal.MojoExecutor#execute

    mojo actuator

Topics: Maven