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