maven源码


背景:有一些maven工程比较大,构建时间非常长,希望能提升这些项目的构建效率?

方法:

通过阅读maven的源码,分析maven工程没有步执行的时间长度,确定具体耗时,然后分析最长耗时。

由于maven主要执行life周期,本次分析暂时延着这个顺序吧。

具体分析过程

下载maven3.3.0版本源码到本地。其实所有版本都是可以下载的maven3各版本源码

将maven工程导入到idea后,可以看到整个项目的结构如下:
1.png

打开apache-maven/bin/mvn脚本

exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "${M2_HOME}"/boot/plexus-classworlds-*.jar \
"-Dclassworlds.conf=${M2_HOME}/bin/m2.conf" \
"-Dmaven.home=${M2_HOME}"  \
${CLASSWORLDS_LAUNCHER} "$@"

这段命令是我们运行mvn命令时候的启动命令,通过plexus-classworlds来启动maven,具体的配置在bin/m2.conf里面

main is org.apache.maven.cli.MavenCli from plexus.core

set maven.home default ${user.home}/m2

[plexus.core]
optionally ${maven.home}/lib/ext/*.jar
load       ${maven.home}/lib/*.jar
load       ${maven.home}/conf/logging

分析conf文件,org.apache.maven.cli.MavenCli里main函数是入口函数。终于可以开始愉快的阅读java代码啦。

个人阅读风格注:一般先延主线阅读,对某个问题想不通时再慢慢阅读思考。那就开始啦。

//这里是maven的execute前的一些准备事项
public int doMain( CliRequest cliRequest )
{
    PlexusContainer localContainer = null;
    try
    {
        initialize( cliRequest );
        cli( cliRequest );
        logging( cliRequest );
        version( cliRequest );
        properties( cliRequest );
        localContainer = container( cliRequest );
        commands( cliRequest );
        settings( cliRequest );
        populateRequest( cliRequest );
        encryption( cliRequest );
        repository( cliRequest );
        return execute( cliRequest );
    }

......终于到项目开始处"Scanning for projects..."

private MavenExecutionResult doExecute( MavenExecutionRequest request )
{
    request.setStartTime( new Date() );

    MavenExecutionResult result = new DefaultMavenExecutionResult();

    try
    {     
          // 校验localRepository  
        validateLocalRepository( request );
    }
    catch ( LocalRepositoryNotAccessibleException e )
    {
        return addExceptionToResult( result, e );
    }

    DefaultRepositorySystemSession repoSession = (DefaultRepositorySystemSession) newRepositorySession( request );

    MavenSession session = new MavenSession( container, repoSession, request, result );
    legacySupport.setSession( session );

    try
    {
        for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( Collections.<MavenProject> emptyList() ) )
        {
            listener.afterSessionStart( session );
        }
    }
    catch ( MavenExecutionException e )
    {
        return addExceptionToResult( result, e );
    }
     //开始scaning 所有项目罗    
    eventCatapult.fire( ExecutionEvent.Type.ProjectDiscoveryStarted, session, null );

    List<MavenProject> projects;
    try
    {
        //获取所有project
        projects = getProjectsForMavenReactor( session );
        //
        // Capture the full set of projects before any potential constraining is performed by --projects
        //
        session.setAllProjects( projects );
    }
    catch ( ProjectBuildingException e )
    {
        return addExceptionToResult( result, e );
    }

    validateProjects( projects );

    //
    // This creates the graph and trims the projects down based on the user request using something like:
    //
    // -pl project0,project2 eclipse:eclipse
    //
    ProjectDependencyGraph projectDependencyGraph = createProjectDependencyGraph( projects, request, result, true );

    if ( result.hasExceptions() )
    {
        return result;
    }

    session.setProjects( projectDependencyGraph.getSortedProjects() );

    try
    {
        session.setProjectMap( getProjectMap( session.getProjects() ) );
    }
    catch ( DuplicateProjectException e )
    {
        return addExceptionToResult( result, e );
    }

    WorkspaceReader reactorWorkspace;
    sessionScope.enter();
    sessionScope.seed( MavenSession.class, session );
    try
    {
        reactorWorkspace = container.lookup( WorkspaceReader.class, ReactorReader.HINT );
    }
    catch ( ComponentLookupException e )
    {
        return addExceptionToResult( result, e );
    }

    //
    // Desired order of precedence for local artifact repositories
    //
    // Reactor
    // Workspace
    // User Local Repository
    //        
    repoSession.setWorkspaceReader( ChainedWorkspaceReader.newInstance( reactorWorkspace,
                                                                        repoSession.getWorkspaceReader() ) );

    repoSession.setReadOnly();

    ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
    try
    {
        for ( AbstractMavenLifecycleParticipant listener : getLifecycleParticipants( projects ) )
        {
            Thread.currentThread().setContextClassLoader( listener.getClass().getClassLoader() );

            listener.afterProjectsRead( session );
        }
    }
    catch ( MavenExecutionException e )
    {
        return addExceptionToResult( result, e );
    }
    finally
    {
        Thread.currentThread().setContextClassLoader( originalClassLoader );
    }

    //
    // The projects need to be topologically after the participants have run their afterProjectsRead(session)
    // because the participant is free to change the dependencies of a project which can potentially change the
    // topological order of the projects, and therefore can potentially change the build order.
    //
    // Note that participants may affect the topological order of the projects but it is
    // not expected that a participant will add or remove projects from the session.
    //
    projectDependencyGraph = createProjectDependencyGraph( session.getProjects(), request, result, false );

    try
    {
        if ( result.hasExceptions() )
        {
            return result;
        }

        session.setProjects( projectDependencyGraph.getSortedProjects() );

        session.setProjectDependencyGraph( projectDependencyGraph );

        result.setTopologicallySortedProjects( session.getProjects() );

        result.setProject( session.getTopLevelProject() );

        lifecycleStarter.execute( session );

        validateActivatedProfiles( session.getProjects(), request.getActiveProfiles() );

        if ( session.getResult().hasExceptions() )
        {
            return addExceptionToResult( result, session.getResult().getExceptions().get( 0 ) );
        }
    }
    finally
    {
        try
        {
            afterSessionEnd( projects, session );
        }
        catch ( MavenExecutionException e )
        {
            return addExceptionToResult( result, e );
        }
        finally
        {
            sessionScope.exit();
        }
    }

    return result;
}

上面分析出了所有的project,工作目录等一堆准备工作, lifecycleStarter.execute( session );生命周期马上开始罗

 public void execute( MavenSession session )
{
    eventCatapult.fire( ExecutionEvent.Type.SessionStarted, session, null );

    ReactorContext reactorContext = null;
    ProjectBuildList projectBuilds = null;
    MavenExecutionResult result = session.getResult();

    try
    {
        if ( buildExecutionRequiresProject( session ) && projectIsNotPresent( session ) )
        {
            throw new MissingProjectException( "The goal you specified requires a project to execute"
                + " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")."
                + " Please verify you invoked Maven from the correct directory." );
        }
        //分析需要的taskSegments
        List<TaskSegment> taskSegments = lifecycleTaskSegmentCalculator.calculateTaskSegments( session );
        projectBuilds = buildListCalculator.calculateProjectBuilds( session, taskSegments );

        if ( projectBuilds.isEmpty() )
        {
            throw new NoGoalSpecifiedException( "No goals have been specified for this build."
                + " 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() + "." );
        }

        ProjectIndex projectIndex = new ProjectIndex( session.getProjects() );

        if ( logger.isDebugEnabled() )
        {
            lifecycleDebugLogger.debugReactorPlan( projectBuilds );
        }

        ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
        ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus( session.getProjectDependencyGraph() );
        reactorContext = new ReactorContext( result, projectIndex, oldContextClassLoader, reactorBuildStatus );

          这里便是获取哪一种builder,是单线程运行,还是多线程运行
        String builderId = session.getRequest().getBuilderId();
        Builder builder = builders.get( builderId );
        if ( builder == null )
        {
            throw new BuilderNotFoundException( String.format( "The builder requested using id = %s cannot be found", builderId ) );
        }

        int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency();
        if ( degreeOfConcurrency >= 2 )
        {
            logger.info( "" );
            logger.info( String.format( "Using the %s implementation with a thread count of %d", builder.getClass().getSimpleName(), degreeOfConcurrency ) );
        }
        builder.build( session, reactorContext, projectBuilds, taskSegments, reactorBuildStatus );

    }
    catch ( Exception e )
    {
        result.addException( e );
    }
    finally
    {
        eventCatapult.fire( ExecutionEvent.Type.SessionEnded, session, null );
    }
}

这里需要注意是单线程执行,还是多线程执行。你可以通过源码看到,builder有这2种实现。如果有兴趣,可以分析多线程执行,为了更清晰运行过程,我们选择分析单线程执行过程。 
.....省略一下中间过程

public void execute( MavenSession session, List<MojoExecution> mojoExecutions, ProjectIndex projectIndex )
    throws LifecycleExecutionException

{
    DependencyContext dependencyContext = newDependencyContext( session, mojoExecutions );

    PhaseRecorder phaseRecorder = new PhaseRecorder( session.getCurrentProject() );

    for ( MojoExecution mojoExecution : mojoExecutions )
    {
        logger.info("Aone content - Mojo Execution start." );
        long start = System.currentTimeMillis();
        execute( session, mojoExecution, projectIndex, dependencyContext, phaseRecorder );
        long end   = System.currentTimeMillis();
        logger.info("Aone content - Mojo Execution end . time : " + ( end - start ) );
    }
}

从这里可以看到maven其实每个project就是运行一组mojo来工作哒,顺序执行。如果你看多线程,其实最后也是调用到这里。

logger.info("Aone content - Mojo Execution end . time : " + ( end - start ) );

大致分析结论

可以打印出每个mojo的执行时间,精确的时间。 通过打印,可以看到在整个生命周期过程中:第一次分析依赖的时间比较长、编译时间很长,打war包,deploy到远端时间较长。

日志截图图如下:

123.png

想要加速构建,我们分析那部分能让我们加速?

通过对多个构建时间长的项目进行日志分析,其实他们会花近一般1/3的时间进行依赖分析。

过程如下:

private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
                      DependencyContext dependencyContext )
    throws LifecycleExecutionException
{
    MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

    try
    {
        mavenPluginManager.checkRequiredMavenVersion( mojoDescriptor.getPluginDescriptor() );
    }
    catch ( PluginIncompatibleException e )
    {
        throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
    }

    if ( mojoDescriptor.isProjectRequired() && !session.isUsingPOMsFromFilesystem() )
    {
        Throwable cause =
            new MissingProjectException( "Goal requires a project to execute"
                + " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")."
                + " Please verify you invoked Maven from the correct directory." );
        throw new LifecycleExecutionException( mojoExecution, null, cause );
    }

    if ( mojoDescriptor.isOnlineRequired() && session.isOffline() )
    {
        if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) )
        {
            Throwable cause =
                new IllegalStateException( "Goal requires online mode for execution"
                    + " but Maven is currently offline." );
            throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
        }
        else
        {
            eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution );

            return;
        }
    }

    List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex );

    ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );

    eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution );

    try
    {
        try
        {
            pluginManager.executeMojo( session, mojoExecution );
        }
        catch ( MojoFailureException e )
        {
            throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
        }
        catch ( MojoExecutionException e )
        {
            throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
        }
        catch ( PluginConfigurationException e )
        {
            throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
        }
        catch ( PluginManagerException e )
        {
            throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
        }

        eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
    }
    catch ( LifecycleExecutionException e )
    {
        eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );

        throw e;
    }
    finally
    {
        for ( MavenProject forkedProject : forkedProjects )
        {
            forkedProject.setExecutionProject( null );
        }
    }
}

这是每个mojo执行过程的步骤,都有依赖分析,第一个mojo执行后会缓存结果,分析一下依赖分析的结果吧。

MojoExecutor 208行
// 依赖分析入口
ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );

lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect, scopesToResolve, session, aggregating,Collections.<Artifact> emptySet() );

// 这里是依赖分析的过程
long start   = System.currentTimeMillis();
        Set<Artifact> artifacts =
            getDependencies( project, scopesToCollect, scopesToResolve, session, aggregating, projectArtifacts );
        long end   = System.currentTimeMillis();
        logger.info( "Aone content - get Dependencies time : " + ( end - start ) );

这里可以看到所有project在依赖分析的时间。
依赖分析过程图:
122.png

构建组件源码拉出来溜溜,对理解构建过程有一定帮助(这是我第一段阅读的代码)

// mojo执行入口
public void execute()
    throws MojoExecutionException, CompilationFailureException
{
    if ( skipMain )
    {
        getLog().info( "Not compiling main sources" );
        return;
    }

    super.execute();

    if ( outputDirectory.isDirectory() )
    {
        projectArtifact.setFile( outputDirectory );
    }
}

接下来是我看到过写的非常长的一段代码,主要思考两点:1、拼接参数,环境,2执行罗

public void execute()
    throws MojoExecutionException, CompilationFailureException
{
    // ----------------------------------------------------------------------
    // Look up the compiler. This is done before other code than can
    // cause the mojo to return before the lookup is done possibly resulting
    // in misconfigured POMs still building.
    // ----------------------------------------------------------------------

    long start = System.currentTimeMillis();

    Compiler compiler;

    getLog().debug( "Using compiler '" + compilerId + "'." );

    try
    {
        compiler = compilerManager.getCompiler( compilerId );
    }
    catch ( NoSuchCompilerException e )
    {
        throw new MojoExecutionException( "No such compiler '" + e.getCompilerId() + "'." );
    }

    //-----------toolchains start here ----------------------------------
    //use the compilerId as identifier for toolchains as well.
    Toolchain tc = getToolchain();
    if ( tc != null )
    {
        getLog().info( "Toolchain in maven-compiler-plugin: " + tc );
        if ( executable != null )
        {
            getLog().warn( "Toolchains are ignored, 'executable' parameter is set to " + executable );
        }
        else
        {
            fork = true;
            //TODO somehow shaky dependency between compilerId and tool executable.
            executable = tc.findTool( compilerId );
        }
    }
    // ----------------------------------------------------------------------
    //
    // ----------------------------------------------------------------------

    List<String> compileSourceRoots = removeEmptyCompileSourceRoots( getCompileSourceRoots() );

    if ( compileSourceRoots.isEmpty() )
    {
        getLog().info( "No sources to compile" );

        return;
    }

    // ----------------------------------------------------------------------
    // Create the compiler configuration
    // ----------------------------------------------------------------------

    CompilerConfiguration compilerConfiguration = new CompilerConfiguration();

    compilerConfiguration.setOutputLocation( getOutputDirectory().getAbsolutePath() );

    compilerConfiguration.setOptimize( optimize );

    compilerConfiguration.setDebug( debug );

    if ( debug && StringUtils.isNotEmpty( debuglevel ) )
    {
        String[] split = StringUtils.split( debuglevel, "," );
        for ( String aSplit : split )
        {
            if ( !( aSplit.equalsIgnoreCase( "none" ) || aSplit.equalsIgnoreCase( "lines" )
                || aSplit.equalsIgnoreCase( "vars" ) || aSplit.equalsIgnoreCase( "source" ) ) )
            {
                throw new IllegalArgumentException( "The specified debug level: '" + aSplit + "' is unsupported. "
                    + "Legal values are 'none', 'lines', 'vars', and 'source'." );
            }
        }
        compilerConfiguration.setDebugLevel( debuglevel );
    }

    compilerConfiguration.setParameters( parameters );

    compilerConfiguration.setVerbose( verbose );

    compilerConfiguration.setShowWarnings( showWarnings );

    compilerConfiguration.setFailOnWarning( failOnWarning );

    compilerConfiguration.setShowDeprecation( showDeprecation );

    compilerConfiguration.setSourceVersion( getSource() );

    compilerConfiguration.setTargetVersion( getTarget() );

    compilerConfiguration.setReleaseVersion( getRelease() );

    compilerConfiguration.setProc( proc );

    File generatedSourcesDirectory = getGeneratedSourcesDirectory();
    compilerConfiguration.setGeneratedSourcesDirectory( generatedSourcesDirectory != null
                    ? generatedSourcesDirectory.getAbsoluteFile() : null );

    if ( generatedSourcesDirectory != null )
    {
        String generatedSourcesPath = generatedSourcesDirectory.getAbsolutePath();

        compileSourceRoots.add( generatedSourcesPath );

        if ( isTestCompile() )
        {
            getLog().debug( "Adding " + generatedSourcesPath + " to test-compile source roots:\n  "
                                + StringUtils.join( project.getTestCompileSourceRoots()
                                                           .iterator(), "\n  " ) );

            project.addTestCompileSourceRoot( generatedSourcesPath );

            getLog().debug( "New test-compile source roots:\n  "
                                + StringUtils.join( project.getTestCompileSourceRoots()
                                                           .iterator(), "\n  " ) );
        }
        else
        {
            getLog().debug( "Adding " + generatedSourcesPath + " to compile source roots:\n  "
                                + StringUtils.join( project.getCompileSourceRoots()
                                                           .iterator(), "\n  " ) );

            project.addCompileSourceRoot( generatedSourcesPath );

            getLog().debug( "New compile source roots:\n  " + StringUtils.join( project.getCompileSourceRoots()
                                                                                       .iterator(), "\n  " ) );
        }
    }

    compilerConfiguration.setSourceLocations( compileSourceRoots );

    compilerConfiguration.setAnnotationProcessors( annotationProcessors );

    compilerConfiguration.setProcessorPathEntries( resolveProcessorPathEntries() );

    compilerConfiguration.setSourceEncoding( encoding );

    compilerConfiguration.setFork( fork );

    if ( fork )
    {
        if ( !StringUtils.isEmpty( meminitial ) )
        {
            String value = getMemoryValue( meminitial );

            if ( value != null )
            {
                compilerConfiguration.setMeminitial( value );
            }
            else
            {
                getLog().info( "Invalid value for meminitial '" + meminitial + "'. Ignoring this option." );
            }
        }

        if ( !StringUtils.isEmpty( maxmem ) )
        {
            String value = getMemoryValue( maxmem );

            if ( value != null )
            {
                compilerConfiguration.setMaxmem( value );
            }
            else
            {
                getLog().info( "Invalid value for maxmem '" + maxmem + "'. Ignoring this option." );
            }
        }
    }

    compilerConfiguration.setExecutable( executable );

    compilerConfiguration.setWorkingDirectory( basedir );

    compilerConfiguration.setCompilerVersion( compilerVersion );

    compilerConfiguration.setBuildDirectory( buildDirectory );

    compilerConfiguration.setOutputFileName( outputFileName );

    if ( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew.getStrategy().equals( this.compilerReuseStrategy ) )
    {
        compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.AlwaysNew );
    }
    else if ( CompilerConfiguration.CompilerReuseStrategy.ReuseSame.getStrategy().equals(
        this.compilerReuseStrategy ) )
    {
        if ( getRequestThreadCount() > 1 )
        {
            if ( !skipMultiThreadWarning )
            {
                getLog().warn( "You are in a multi-thread build and compilerReuseStrategy is set to reuseSame."
                                   + " This can cause issues in some environments (os/jdk)!"
                                   + " Consider using reuseCreated strategy."
                                   + System.getProperty( "line.separator" )
                                   + "If your env is fine with reuseSame, you can skip this warning with the "
                                   + "configuration field skipMultiThreadWarning "
                                   + "or -Dmaven.compiler.skipMultiThreadWarning=true" );
            }
        }
        compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseSame );
    }
    else
    {

        compilerConfiguration.setCompilerReuseStrategy( CompilerConfiguration.CompilerReuseStrategy.ReuseCreated );
    }

    getLog().debug( "CompilerReuseStrategy: " + compilerConfiguration.getCompilerReuseStrategy().getStrategy() );

    compilerConfiguration.setForceJavacCompilerUse( forceJavacCompilerUse );

    boolean canUpdateTarget;

    IncrementalBuildHelper incrementalBuildHelper = new IncrementalBuildHelper( mojoExecution, session );

    Set<File> sources;

    IncrementalBuildHelperRequest incrementalBuildHelperRequest = null;

    if ( useIncrementalCompilation )
    {
        getLog().debug( "useIncrementalCompilation enabled" );
        try
        {
            canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );

            sources = getCompileSources( compiler, compilerConfiguration );

            preparePaths( sources );

            incrementalBuildHelperRequest = new IncrementalBuildHelperRequest().inputFiles( sources );

            // CHECKSTYLE_OFF: LineLength
            if ( ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES ) && !canUpdateTarget )
                || isDependencyChanged()
                || isSourceChanged( compilerConfiguration, compiler )
                || incrementalBuildHelper.inputFileTreeChanged( incrementalBuildHelperRequest ) )
                // CHECKSTYLE_ON: LineLength
            {
                getLog().info( "Changes detected - recompiling the module!" );

                compilerConfiguration.setSourceFiles( sources );
            }
            else
            {
                getLog().info( "Nothing to compile - all classes are up to date" );

                return;
            }
        }
        catch ( CompilerException e )
        {
            throw new MojoExecutionException( "Error while computing stale sources.", e );
        }
    }
    else
    {
        getLog().debug( "useIncrementalCompilation disabled" );
        Set<File> staleSources;
        try
        {
            staleSources =
                computeStaleSources( compilerConfiguration, compiler, getSourceInclusionScanner( staleMillis ) );

            canUpdateTarget = compiler.canUpdateTarget( compilerConfiguration );

            if ( compiler.getCompilerOutputStyle().equals( CompilerOutputStyle.ONE_OUTPUT_FILE_FOR_ALL_INPUT_FILES )
                && !canUpdateTarget )
            {
                getLog().info( "RESCANNING!" );
                // TODO: This second scan for source files is sub-optimal
                String inputFileEnding = compiler.getInputFileEnding( compilerConfiguration );

                sources = computeStaleSources( compilerConfiguration, compiler,
                                                         getSourceInclusionScanner( inputFileEnding ) );

                compilerConfiguration.setSourceFiles( sources );
            }
            else
            {
                compilerConfiguration.setSourceFiles( staleSources );
            }

            preparePaths( compilerConfiguration.getSourceFiles() );
        }
        catch ( CompilerException e )
        {
            throw new MojoExecutionException( "Error while computing stale sources.", e );
        }

        if ( staleSources.isEmpty() )
        {
            getLog().info( "Nothing to compile - all classes are up to date" );

            return;
        }
    }

    // Dividing pathElements of classPath and modulePath is based on sourceFiles
    compilerConfiguration.setClasspathEntries( getClasspathElements() );

    compilerConfiguration.setModulepathEntries( getModulepathElements() );

    Map<String, String> effectiveCompilerArguments = getCompilerArguments();

    String effectiveCompilerArgument = getCompilerArgument();

    if ( ( effectiveCompilerArguments != null ) || ( effectiveCompilerArgument != null )
                    || ( compilerArgs != null ) )
    {
        if ( effectiveCompilerArguments != null )
        {
            for ( Map.Entry<String, String> me : effectiveCompilerArguments.entrySet() )
            {
                String key = me.getKey();
                String value = me.getValue();
                if ( !key.startsWith( "-" ) )
                {
                    key = "-" + key;
                }

                if ( key.startsWith( "-A" ) && StringUtils.isNotEmpty( value ) )
                {
                    compilerConfiguration.addCompilerCustomArgument( key + "=" + value, null );
                }
                else
                {
                    compilerConfiguration.addCompilerCustomArgument( key, value );
                }
            }
        }
        if ( !StringUtils.isEmpty( effectiveCompilerArgument ) )
        {
            compilerConfiguration.addCompilerCustomArgument( effectiveCompilerArgument, null );
        }
        if ( compilerArgs != null )
        {
            for ( String arg : compilerArgs )
            {
                compilerConfiguration.addCompilerCustomArgument( arg, null );
            }
        }
    }

    // ----------------------------------------------------------------------
    // Dump configuration
    // ----------------------------------------------------------------------
    if ( getLog().isDebugEnabled() )
    {
        getLog().debug( "Classpath:" );

        for ( String s : getClasspathElements() )
        {
            getLog().debug( " " + s );
        }

        if ( !getModulepathElements().isEmpty() )
        {
            getLog().debug( "Modulepath:" );
            for ( String s : getModulepathElements() )
            {
                getLog().debug( " " + s );
            }
        }

        getLog().debug( "Source roots:" );

        for ( String root : getCompileSourceRoots() )
        {
            getLog().debug( " " + root );
        }

        try
        {
            if ( fork )
            {
                if ( compilerConfiguration.getExecutable() != null )
                {
                    getLog().debug( "Excutable: " );
                    getLog().debug( " " + compilerConfiguration.getExecutable() );
                }
            }

            String[] cl = compiler.createCommandLine( compilerConfiguration );
            if ( getLog().isDebugEnabled() && cl != null && cl.length > 0 )
            {
                StringBuilder sb = new StringBuilder();
                sb.append( cl[0] );
                for ( int i = 1; i < cl.length; i++ )
                {
                    sb.append( " " );
                    sb.append( cl[i] );
                }
                getLog().debug( "Command line options:" );
                getLog().debug( sb );
            }
        }
        catch ( CompilerException ce )
        {
            getLog().debug( ce );
        }
    }


    List<String> jpmsLines = new ArrayList<String>();

    // See http://openjdk.java.net/jeps/261
    final List<String> runtimeArgs = Arrays.asList( "--upgrade-module-path", 
                                              "--add-exports",
                                              "--add-reads", 
                                              "--add-modules", 
                                              "--limit-modules" );

    // Custom arguments are all added as keys to an ordered Map
    Iterator<Map.Entry<String, String>> entryIter =
        compilerConfiguration.getCustomCompilerArgumentsEntries().iterator();
    while ( entryIter.hasNext() )
    {
        Map.Entry<String, String> entry = entryIter.next();

        if ( runtimeArgs.contains( entry.getKey() ) )
        {
            jpmsLines.add( entry.getKey() );

            String value = entry.getValue();
            if ( value == null )
            {
                entry = entryIter.next();
                value = entry.getKey();
            }
            jpmsLines.add( value );
        }
        else if ( "--patch-module".equals( entry.getKey() ) )
        {
            jpmsLines.add( "--patch-module" );

            String value = entry.getValue();
            if ( value == null )
            {
                entry = entryIter.next();
                value = entry.getKey();
            }

            String[] values = value.split( "=" );

            StringBuilder patchModule = new StringBuilder( values[0] );
            patchModule.append( '=' );

            Set<String> patchModules = new LinkedHashSet<>();
            Set<Path> sourceRoots = new HashSet<>( getCompileSourceRoots().size() );
            for ( String sourceRoot : getCompileSourceRoots() )
            {
                sourceRoots.add( Paths.get( sourceRoot ) );
            }

            String[] files = values[1].split( PS );

            for ( String file : files )
            {
                Path filePath = Paths.get( file );
                if ( getOutputDirectory().toPath().equals( filePath ) )
                {
                    patchModules.add( "_" ); // this jar
                }
                else if ( sourceRoots.contains( filePath ) )
                {
                    patchModules.add( "_" ); // this jar
                }
                else
                {
                    JavaModuleDescriptor descriptor = getPathElements().get( file );

                    if ( descriptor == null )
                    {
                        getLog().warn( "Can't locate " + file );
                    }
                    else if ( !values[0].equals( descriptor.name() ) )
                    {
                        patchModules.add( descriptor.name() );
                    }
                }
            }

            StringBuilder sb = new StringBuilder();

            for ( String mod : patchModules )
            {
                if ( sb.length() > 0 )
                {
                    sb.append( ", " );
                }
                // use 'invalid' separator to ensure values are transformed
                sb.append( mod );
            }

            jpmsLines.add( patchModule + sb.toString() );
        }
    }

    if ( !jpmsLines.isEmpty() ) 
    {
        Path jpmsArgs = Paths.get( getOutputDirectory().getAbsolutePath(), "META-INF/jpms.args" );
        try
        {
            Files.createDirectories( jpmsArgs.getParent() );

            Files.write( jpmsArgs, jpmsLines, Charset.defaultCharset() );
        }
        catch ( IOException e )
        {
            getLog().warn( e.getMessage() );
        }
    }

    long end = System.currentTimeMillis();
    getLog().debug( "aone log - compiler Configuration tiem: " + ( end - start ) );

    // ----------------------------------------------------------------------
    // Compile!
    // ----------------------------------------------------------------------

    long s1 = System.currentTimeMillis();

    if ( StringUtils.isEmpty( compilerConfiguration.getSourceEncoding() ) )
    {
        getLog().warn( "File encoding has not been set, using platform encoding " + ReaderFactory.FILE_ENCODING
                           + ", i.e. build is platform dependent!" );
    }

    CompilerResult compilerResult;


    if ( useIncrementalCompilation )
    {
        incrementalBuildHelperRequest.outputDirectory( getOutputDirectory() );

        incrementalBuildHelper.beforeRebuildExecution( incrementalBuildHelperRequest );

        getLog().debug( "incrementalBuildHelper#beforeRebuildExecution" );
    }

    try
    {
        try
        {
            compilerResult = compiler.performCompile( compilerConfiguration );
        }
        catch ( CompilerNotImplementedException cnie )
        {
            List<CompilerError> messages = compiler.compile( compilerConfiguration );
            compilerResult = convertToCompilerResult( messages );
        }
    }
    catch ( Exception e )
    {
        // TODO: don't catch Exception
        throw new MojoExecutionException( "Fatal error compiling", e );
    }

    long s2 = System.currentTimeMillis();
    getLog().debug( "aone log - compiler time: " + ( s2 - s1 ) );
    getLog().debug( "aone log - compiler common time: " + ( s2 - start ) );

    if ( useIncrementalCompilation )
    {
        if ( incrementalBuildHelperRequest.getOutputDirectory().exists() )
        {
            getLog().debug( "incrementalBuildHelper#afterRebuildExecution" );
            // now scan the same directory again and create a diff
            incrementalBuildHelper.afterRebuildExecution( incrementalBuildHelperRequest );
        }
        else
        {
            getLog().debug(
                "skip incrementalBuildHelper#afterRebuildExecution as the output directory doesn't exist" );
        }
    }

    List<CompilerMessage> warnings = new ArrayList<CompilerMessage>();
    List<CompilerMessage> errors = new ArrayList<CompilerMessage>();
    List<CompilerMessage> others = new ArrayList<CompilerMessage>();
    for ( CompilerMessage message : compilerResult.getCompilerMessages() )
    {
        if ( message.getKind() == CompilerMessage.Kind.ERROR )
        {
            errors.add( message );
        }
        else if ( message.getKind() == CompilerMessage.Kind.WARNING
            || message.getKind() == CompilerMessage.Kind.MANDATORY_WARNING )
        {
            warnings.add( message );
        }
        else
        {
            others.add( message );
        }
    }

    if ( failOnError && !compilerResult.isSuccess() )
    {
        for ( CompilerMessage message : others )
        {
            assert message.getKind() != CompilerMessage.Kind.ERROR
                && message.getKind() != CompilerMessage.Kind.WARNING
                && message.getKind() != CompilerMessage.Kind.MANDATORY_WARNING;
            getLog().info( message.toString() );
        }
        if ( !warnings.isEmpty() )
        {
            getLog().info( "-------------------------------------------------------------" );
            getLog().warn( "COMPILATION WARNING : " );
            getLog().info( "-------------------------------------------------------------" );
            for ( CompilerMessage warning : warnings )
            {
                getLog().warn( warning.toString() );
            }
            getLog().info( warnings.size() + ( ( warnings.size() > 1 ) ? " warnings " : " warning" ) );
            getLog().info( "-------------------------------------------------------------" );
        }

        if ( !errors.isEmpty() )
        {
            getLog().info( "-------------------------------------------------------------" );
            getLog().error( "COMPILATION ERROR : " );
            getLog().info( "-------------------------------------------------------------" );
            for ( CompilerMessage error : errors )
            {
                getLog().error( error.toString() );
            }
            getLog().info( errors.size() + ( ( errors.size() > 1 ) ? " errors " : " error" ) );
            getLog().info( "-------------------------------------------------------------" );
        }

        if ( !errors.isEmpty() )
        {
            throw new CompilationFailureException( errors );
        }
        else
        {
            throw new CompilationFailureException( warnings );
        }
    }
    else
    {
        for ( CompilerMessage message : compilerResult.getCompilerMessages() )
        {
            switch ( message.getKind() )
            {
                case NOTE:
                case OTHER:
                    getLog().info( message.toString() );
                    break;

                case ERROR:
                    getLog().error( message.toString() );
                    break;

                case MANDATORY_WARNING:
                case WARNING:
                default:
                    getLog().warn( message.toString() );
                    break;
            }
        }
    }
}

从这段代码你可以看出构建需要哪些东西,有兴趣可以细致分析
这里涉及到java编译器, 通过阅读源码你可以发现,并行执行的时候起了一个进程,而单线程执行的时候,直接javaCompile走起。而且直接调用java自己的编译工具,所以没有优化空间。

总结

通过阅读、分析源码,优化依赖分析部分可以得到比较高的收益,而且snapshot版本包含越多,收益越高。

本次分析基本上是纵向分析,主要是lifecycle部分比较多一点,pom模型...等许多部分没有详尽描述,后面有计划再细致分析


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM