14. Gradle編譯其他應用代碼流程(三) - Load過程

繼續上一篇   13. Gradle編譯其他應用代碼流程(二)。 

這篇從InProcessBuildActionExecuter.execute開始這一篇的內容。


在正式內容前,首先要講個gradle構建的流程,這個也是接下來內容的流程。

Load->Configure->Build

大家可以思考下,這3步都主要做了什麼事。


另外,從執行流程的事件通知來看,它還分成下面5個通知事件:

buildStarted         //開始編譯
settingsEvaluated    //settings處理完畢
projectsLoaded       //項目loaded
projectsEvaluated    //項目處理完畢
buildFinished        //編譯結束




一. InProcessBuildActionExecuter.execute

文件路徑:
subprojects\launcher\src\main\java\org\gradle\launcher\exec\InProcessBuildActionExecuter.java
方法:
public Object execute(BuildAction action, BuildRequestContext buildRequestContext, BuildActionParameters actionParameters, ServiceRegistry contextServices) {
    GradleLauncher gradleLauncher = gradleLauncherFactory.newInstance(action.getStartParameter(), buildRequestContext, contextServices);
    try {
        gradleLauncher.addStandardOutputListener(buildRequestContext.getOutputListener());
        gradleLauncher.addStandardErrorListener(buildRequestContext.getErrorListener());
        GradleBuildController buildController = new GradleBuildController(gradleLauncher);
        buildActionRunner.run(action, buildController);
            
        System.out.println("Sandy buildController.getResult(): " + buildController.getResult());
        return buildController.getResult();
    } finally {
        gradleLauncher.stop();
    }
}


文件路徑:
subprojects\launcher\src\main\java\org\gradle\launcher\exec\InProcessBuildActionExecuter.java
方法:
public GradleInternal run() {
    try {
      	System.out.println("Sandy GradleInternal run state: " + state);
        return (GradleInternal) getLauncher().run().getGradle();
    } finally {
        state = State.Completed;
    }
}

文件路徑:
subprojects\launcher\src\main\java\org\gradle\launcher\exec\ChainingBuildActionRunner.java
方法:
@Override
public void run(BuildAction action, BuildController buildController) {
  	System.out.println("Sandy ChainingBuildActionRunner run runners.size: " + runners.size());
    for (BuildActionRunner runner : runners) {
       	System.out.println("runner: " + runner);
        runner.run(action, buildController);
		if (buildController.hasResult()) {
			return;
        }
    }
    throw new UnsupportedOperationException(String.format("Don't know how to run a build action of type %s.", action.getClass().getSimpleName()));
}

文件路徑:
subprojects\launcher\src\main\java\org\gradle\tooling\internal\provider\ExecuteBuildActionRunner.java
方法:
public void run(BuildAction action, BuildController buildController) {
   	System.out.println("ExecuteBuildActionRunner action: " + action
  			+ " buildController: " + buildController);
    if (action instanceof ExecuteBuildAction) {
      	System.out.println("ExecuteBuildActionRunner run..");
        buildController.run();
        buildController.setResult(null);
        System.out.println("ExecuteBuildActionRunner result: " + "null");
    }
}

文件路徑:
subprojects\launcher\src\main\java\org\gradle\launcher\exec\GradleBuildController.java
方法:
public GradleInternal run() {
    try {
        System.out.println("Sandy GradleInternal run state: " + state);
        return (GradleInternal) getLauncher().run().getGradle();
    } finally {
        state = State.Completed;
    }
}


上面的步驟比較清晰明瞭,需要注意的是ChainingBuildActionRunner和ExecuteBuildActionRunner,其實他們都是同一個接口BuildActionRunner,所以這裏又是喜聞樂見的裝飾者模式。

public class ChainingBuildActionRunner implements BuildActionRunner {
	private final List<? extends BuildActionRunner> runners;

    public ChainingBuildActionRunner(List<? extends BuildActionRunner> runners) {
        this.runners = runners;      
    }
}

public class ExecuteBuildActionRunner implements BuildActionRunner {}


最後來到ExecuteBuildActionRunner.run方法,它會調用GradleBuildController.run(),顧名思義,它是GradleBuild的控制器,所以馬上就要接觸到Gradle build步驟了;接下來會調用DefaultGradleLauncher.run()方法。


二. DefaultGradleLauncher 

@Override
public BuildResult run() {
   return doBuild(Stage.Build);
}

private BuildResult doBuild(final Stage upTo) {
    return buildOperationExecutor.run("Run build", new Factory<BuildResult>() {
        @Override
        public BuildResult create() {
            Throwable failure = null;
            try {
                	
              	System.out.println("buildListener: " + buildListener.getClass() + " gradle: " + gradle.getClass());
                buildListener.buildStarted(gradle);
                doBuildStages(upTo);
            } catch (Throwable t) {
                failure = exceptionAnalyser.transform(t);
            }
            BuildResult buildResult = new BuildResult(upTo.name(), gradle, failure);
            buildListener.buildFinished(buildResult);
            if (failure != null) {
                throw new ReportedException(failure);
            }

            return buildResult;
        }
    });
}


首先來看buildListener.buildStarted(gradle); 這行代碼的作用是通知gradle構建已經開始,那既然是通知,接收者是誰呢?


看buildListener賦值的地方:
文件路徑:

subprojects\core\src\main\java\org\gradle\initialization\DefaultGradleLauncher.java

方法

public DefaultGradleLauncher(GradleInternal gradle, InitScriptHandler initScriptHandler, SettingsLoader settingsLoader,
                                 BuildConfigurer buildConfigurer, ExceptionAnalyser exceptionAnalyser,
                                 LoggingManagerInternal loggingManager, BuildListener buildListener,
                                 ModelConfigurationListener modelConfigurationListener,
                                 BuildCompletionListener buildCompletionListener, BuildOperationExecutor operationExecutor,
                                 BuildConfigurationActionExecuter buildConfigurationActionExecuter, BuildExecuter buildExecuter, BuildScopeServices buildServices) {
     ...
    this.buildListener = buildListener;
     ...
}


繼續看調動的地方

文件路徑:

subprojects\core\src\main\java\org\gradle\initialization\DefaultGradleLauncherFactory.java

方法:

return new DefaultGradleLauncher(
            ...
            gradle.getBuildListenerBroadcaster(),
            ...
        );


//文件路徑:

subprojects\core\src\main\java\org\gradle\invocation\DefaultGradle.java

    
public BuildListener getBuildListenerBroadcaster() {
        return buildListenerBroadcast.getSource();
}        

    
public DefaultGradle(Gradle parent, StartParameter startParameter, ServiceRegistryFactory parentRegistry) {
        ...
        buildListenerBroadcast = getListenerManager().createAnonymousBroadcaster(BuildListener.class);
        projectEvaluationListenerBroadcast = getListenerManager().createAnonymousBroadcaster(ProjectEvaluationListener.class);
        buildListenerBroadcast.add(new BuildAdapter() {
        	
            @Override
			public void buildStarted(Gradle gradle) {
				super.buildStarted(gradle);
				Exception ex = new Exception("Sandy buildStarted");
				ex.printStackTrace();
				System.out.println("buildListenerBroadcast sandy buildStarted");
			}

			@Override
			public void settingsEvaluated(Settings settings) {
				super.settingsEvaluated(settings);
				Exception ex = new Exception("Sandy settingsEvaluated");
				ex.printStackTrace();
				System.out.println("buildListenerBroadcast sandy settingsEvaluated");
			}

			@Override
			public void projectsEvaluated(Gradle gradle) {
				super.projectsEvaluated(gradle);
				System.out.println("buildListenerBroadcast sandy projectsEvaluated");
			}

			@Override
			public void buildFinished(BuildResult result) {
				super.buildFinished(result);
				System.out.println("buildListenerBroadcast sandy buildFinished");
			}

			@Override
            public void projectsLoaded(Gradle gradle) {
            	System.out.println("buildListenerBroadcast sandy projectsLoaded");
                rootProjectActions.execute(rootProject);
                rootProjectActions = null;
            }
        });
    }


嗯,也就是接受者是在DefaultGradle裏面,也就是說通知gradle build已經開始,接下來看doBuildStages(Stage.Build)方法,這個方法很重要,gradle build的主要流程都在這裏面。


private void doBuildStages(Stage upTo) {
        if (stage == Stage.Build) {
            throw new IllegalStateException("Cannot build with GradleLauncher multiple times");
        }

        if (stage == null) {
            // Evaluate init scripts
            initScriptHandler.executeScripts(gradle);

            // Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
            settings = settingsLoader.findAndLoadSettings(gradle);

            stage = Stage.Load;
        }

        if (upTo == Stage.Load) {
            return;
        }

        if (stage == Stage.Load) {
            // Configure build
            buildOperationExecutor.run("Configure build", new Runnable() {
                @Override
                public void run() {
                    buildConfigurer.configure(gradle);

                    if (!gradle.getStartParameter().isConfigureOnDemand()) {
                        buildListener.projectsEvaluated(gradle);
                    }

                    modelConfigurationListener.onConfigure(gradle);
                }
            });

            stage = Stage.Configure;
        }

        if (upTo == Stage.Configure) {
            return;
        }

        // After this point, the GradleLauncher cannot be reused
        stage = Stage.Build;

        // Populate task graph
        buildOperationExecutor.run("Calculate task graph", new Runnable() {
            @Override
            public void run() {
                buildConfigurationActionExecuter.select(gradle);
                if (gradle.getStartParameter().isConfigureOnDemand()) {
                    buildListener.projectsEvaluated(gradle);
                }
            }
        });

        // Execute build
        buildOperationExecutor.run("Run tasks", new Runnable() {
            @Override
            public void run() {
                buildExecuter.execute(gradle);
            }
        });
    }


還記得最開始說過的gradle構建分成3個步驟嗎?Load-> Configure->Build


那這個方法doBuildStages可以很清楚的看到這個流程,大家看下上面的代碼即可明白,那接下來我們一步步分析這個過程。

三. Load.executeScripts 初始化腳本

if (stage == null) {
     // Evaluate init scripts
     initScriptHandler.executeScripts(gradle);

     // Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
     settings = settingsLoader.findAndLoadSettings(gradle);

     stage = Stage.Load;
}

1. 首先來看第一行

initScriptHandler.executeScripts(gradle);

// Evaluate init scripts  看解釋是統計初始化腳本,那具體是幹什麼呢?


文件路徑:

subprojects\core\src\main\java\org\gradle\initialization\InitScriptHandler.java

/**
 * Finds and executes all init scripts for a given build.
 */
public class InitScriptHandler {
    ...
    
    public void executeScripts(final GradleInternal gradle) {
    	System.out.println("executeScripts");
        final List<File> initScripts = gradle.getStartParameter().getAllInitScripts();
        if (initScripts.isEmpty()) {
            return;
        }

        BuildOperationDetails operationDetails = BuildOperationDetails.displayName("Run init scripts").progressDisplayName("init scripts").build();
        buildOperationExecutor.run(operationDetails, new Runnable() {
            @Override
            public void run() {
                for (File script : initScripts) {
                	System.out.println("InitScriptHandler run processor" + processor);
                    processor.process(new UriScriptSource("initialization script", script), gradle);
                }
            }
        });
    }
}

從類InitScriptHandler的註釋就知道,這個類的作用是找到並且執行初始化腳本,那我們接下來看看:

a. 它說的初始化腳本指的是什麼?

b. 怎麼執行初始化腳本的?


2. 首先來看getAllInitScripts是在幹什麼:


subprojects\core\src\main\java\org\gradle\StartParameter.java

public List<File> getAllInitScripts() {
        CompositeInitScriptFinder initScriptFinder = new CompositeInitScriptFinder(
            new UserHomeInitScriptFinder(getGradleUserHomeDir()), new DistributionInitScriptFinder(gradleHomeDir)
        );
        

        List<File> scripts = new ArrayList<File>(getInitScripts());
        initScriptFinder.findScripts(scripts);
        return Collections.unmodifiableList(scripts);
    }


subprojects\core\src\main\java\org\gradle\initialization\UserHomeInitScriptFinder.java

public void findScripts(Collection<File> scripts) {
        File userInitScript = new File(userHomeDir, "init.gradle");
        if (userInitScript.isFile()) {
            scripts.add(userInitScript);
        }
        findScriptsInDir(new File(userHomeDir, "init.d"), scripts);
    }


subprojects\core\src\main\java\org\gradle\initialization\DistributionInitScriptFinder.java

public void findScripts(Collection<File> scripts) {
        if (gradleHome == null) {
            return;
        }
        findScriptsInDir(new File(gradleHome, "init.d"), scripts);
    }


3個作用:

a. 嘗試找到環境變量'GRADLE_USER_HOME'目錄下的init.gradle文件,並加入列表。

如果沒有配置環境變量'GRADLE_USER_HOME',那麼目錄就是'C:\Users\xxx(你的用戶名)\.gradle'

b. 找到環境變量'GRADLE_USER_HOME'下面init.d目錄下的 *.gradle配置文件,並加入列表。

c. 找到gradle home目錄下init.d目錄的 *.gradle配置文件,並加入列表。


我的gradle_user_home和gradle_home分別是:

gradleUserHomeDir=D:\gradle_jar_cache
gradleHome=E:\work_space\gradle-source-from-csdn\gradle-3.1\build\distributions\gradle-3.1-snapshot-1

其實如果大家去看下gradle home目錄的init.d目錄,你會發現裏面有個readme.txt文件,裏面有這樣的描述:

You can add .gradle init scripts to this directory. Each one is executed at the start of the build.

那我們的Gradle 源代碼流程分析也驗證了這句話。


3. 然後來看看processor.process在幹什麼

for (File script : initScripts) {
     processor.process(new UriScriptSource("initialization script", script), gradle);
}


首先initScripts是指上面我們統計3個目錄下得到的腳本文件集合,那這裏就是挨個執行它們。

那UriScriptSource是用來解析傳入的script文件,把它的內容讀出來。


那繼續看process方法

文件路徑:

subprojects\core\src\main\java\org\gradle\configuration\DefaultInitScriptProcessor.java


/**
 * Processes (and runs) an init script for a specified build.  Handles defining
 * the classpath based on the initscript {} configuration closure.
 */
public class DefaultInitScriptProcessor implements InitScriptProcessor {
    ...

    public void process(final ScriptSource initScript, GradleInternal gradle) {
        ....
        configurer.apply(gradle);
    }
}


文件路徑:

subprojects\core\src\main\java\org\gradle\configuration\DefaultScriptPluginFactory.java

public void apply(final Object target) {
        	System.out.println("ScriptPluginImpl apply 1");
            ...
            
            // Pass 1, extract plugin requests and plugin repositories and execute buildscript {}, ignoring (i.e. not even compiling) anything else

            Class<? extends BasicScript> scriptType = initialPassScriptTarget.getScriptClass();
            InitialPassStatementTransformer initialPassStatementTransformer = new InitialPassStatementTransformer(scriptSource, initialPassScriptTarget, documentationRegistry);
            SubsetScriptTransformer initialTransformer = new SubsetScriptTransformer(initialPassStatementTransformer);
            String id = INTERNER.intern("cp_" + initialPassScriptTarget.getId());
            CompileOperation<PluginRequests> initialOperation = new FactoryBackedCompileOperation<PluginRequests>(id, initialTransformer, initialPassStatementTransformer, pluginRequestsSerializer);

            ScriptRunner<? extends BasicScript, PluginRequests> initialRunner = compiler.compile(scriptType, initialOperation, baseScope.getExportClassLoader(), Actions.doNothing());
            initialRunner.run(target, services);

            PluginRequests pluginRequests = initialRunner.getData();
            PluginManagerInternal pluginManager = initialPassScriptTarget.getPluginManager();
            System.out.println("ScriptPluginImpl apply is empty: " + pluginRequests.isEmpty());
            pluginRequestApplicator.applyPlugins(pluginRequests, scriptHandler, pluginManager, targetScope);

            System.out.println("ScriptPluginImpl apply 4");
            // Pass 2, compile everything except buildscript {}, pluginRepositories{}, and plugin requests, then run
            final ScriptTarget scriptTarget = secondPassTarget(target);
            scriptType = scriptTarget.getScriptClass();

            BuildScriptTransformer buildScriptTransformer = new BuildScriptTransformer(scriptSource, scriptTarget);
            String operationId = scriptTarget.getId();
            CompileOperation<BuildScriptData> operation = new FactoryBackedCompileOperation<BuildScriptData>(operationId, buildScriptTransformer, buildScriptTransformer, buildScriptDataSerializer);

            final ScriptRunner<? extends BasicScript, BuildScriptData> runner = compiler.compile(scriptType, operation, targetScope.getLocalClassLoader(), ClosureCreationInterceptingVerifier.INSTANCE);
            if (scriptTarget.getSupportsMethodInheritance() && runner.getHasMethods()) {
            	System.out.println("ScriptPluginImpl apply 5");
                scriptTarget.attachScript(runner.getScript());
            }
            System.out.println("ScriptPluginImpl apply 6");
            if (!runner.getRunDoesSomething()) {
            	System.out.println("ScriptPluginImpl apply 7");
                return;
            }

            Runnable buildScriptRunner = new Runnable() {
                public void run() {
                	System.out.println("ScriptPluginImpl apply 8");
                    runner.run(target, services);
                }
            };

            System.out.println("ScriptPluginImpl apply 9");
            boolean hasImperativeStatements = runner.getData().getHasImperativeStatements();
            scriptTarget.addConfiguration(buildScriptRunner, !hasImperativeStatements);
        }


執行流程: 

a. 在DefaultScriptPluginFactory的apply裏面主要有兩步 pass1/ pass2

在pass1的時候,處理的文件是

'D:\gradle_jar_cache\caches\3.1-snapshot-1\scripts-remapped\settings_brbxf8awmquo5r6nxrgcmg7fq\iaw3k0vmpedkxsp2gv9xvnom\cp_settingsd7eae713beda1bd9e69f8461da734880\metadata\metadata.bin'


b. 在pass2的時候,處理初始化腳本,如果腳本文件裏面有配置,則會到走到'ScriptPluginImpl apply 8' 加載這個腳本的屬性;

如果腳本里面有配置,只是一個空文件,則從'ScriptPluginImpl apply 7'返回。


總結下這個步驟的內容:

a. 加載gradle_user_home下面init.gradle和init.d/目錄下 *.gradle以及gradle home 目錄下的init.d/目錄下的 *.gradle文件

b. 如果有這些文件,而且內容不爲空,那麼將會加載它們配置的屬性。


比較重要也很令人費解是這行代碼:

scriptCompilerFactory.createCompiler(scriptSource);


這行代碼粗看沒有什麼,但是仔細分析後其實大有乾坤!它是在爲下一行

initialRunner.run(target, services);

生成可以加載的Java類,最終調用DefaultScriptCompilationHandler.compileToDir來生成類。 生成的類路徑如下:


D:\gradle_jar_cache\caches\3.1-snapshot-1\scripts\df5frxe005h8vnbjcuzkr2g0s\cp_settings\cp_settingsd7eae713beda1bd9e69f8461da734880


執行堆棧如下:

java.lang.Exception: Sandy compile to dir
	at org.gradle.groovy.scripts.internal.DefaultScriptCompilationHandler.compileToDir(DefaultScriptCompilationHandler.java:97)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$CompileToCrossBuildCacheAction.execute(FileCacheBackedScriptClassCompiler.java:160)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$CompileToCrossBuildCacheAction.execute(FileCacheBackedScriptClassCompiler.java:141)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$Progre***eportingInitializer.execute(FileCacheBackedScriptClassCompiler.java:185)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$Progre***eportingInitializer.execute(FileCacheBackedScriptClassCompiler.java:164)
	at org.gradle.cache.internal.DefaultPersistentDirectoryCache$Initializer.initialize(DefaultPersistentDirectoryCache.java:100)
	at org.gradle.cache.internal.DefaultCacheAccess$2.run(DefaultCacheAccess.java:116)
	at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.doWriteAction(DefaultFileLockManager.java:179)
	at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.writeFile(DefaultFileLockManager.java:169)
	at org.gradle.cache.internal.DefaultCacheAccess.open(DefaultCacheAccess.java:113)
	at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:47)
	at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:28)
	at org.gradle.cache.internal.DefaultCacheFactory.doOpen(DefaultCacheFactory.java:83)
	at org.gradle.cache.internal.DefaultCacheFactory.open(DefaultCacheFactory.java:51)
	at org.gradle.cache.internal.DefaultCacheRepository$PersistentCacheBuilder.doOpen(DefaultCacheRepository.java:147)
	at org.gradle.cache.internal.DefaultCacheRepository$AbstractCacheBuilder.open(DefaultCacheRepository.java:121)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$RemapBuildScriptsAction.execute(FileCacheBackedScriptClassCompiler.java:365)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$RemapBuildScriptsAction.execute(FileCacheBackedScriptClassCompiler.java:333)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$Progre***eportingInitializer.execute(FileCacheBackedScriptClassCompiler.java:185)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler$Progre***eportingInitializer.execute(FileCacheBackedScriptClassCompiler.java:164)
	at org.gradle.cache.internal.DefaultPersistentDirectoryCache$Initializer.initialize(DefaultPersistentDirectoryCache.java:100)
	at org.gradle.cache.internal.DefaultCacheAccess$2.run(DefaultCacheAccess.java:116)
	at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.doWriteAction(DefaultFileLockManager.java:179)
	at org.gradle.cache.internal.DefaultFileLockManager$DefaultFileLock.writeFile(DefaultFileLockManager.java:169)
	at org.gradle.cache.internal.DefaultCacheAccess.open(DefaultCacheAccess.java:113)
	at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:47)
	at org.gradle.cache.internal.DefaultPersistentDirectoryStore.open(DefaultPersistentDirectoryStore.java:28)
	at org.gradle.cache.internal.DefaultCacheFactory.doOpen(DefaultCacheFactory.java:83)
	at org.gradle.cache.internal.DefaultCacheFactory.open(DefaultCacheFactory.java:51)
	at org.gradle.cache.internal.DefaultCacheRepository$PersistentCacheBuilder.doOpen(DefaultCacheRepository.java:147)
	at org.gradle.cache.internal.DefaultCacheRepository$AbstractCacheBuilder.open(DefaultCacheRepository.java:121)
	at org.gradle.groovy.scripts.internal.FileCacheBackedScriptClassCompiler.compile(FileCacheBackedScriptClassCompiler.java:111)
	at org.gradle.groovy.scripts.internal.CrossBuildInMemoryCachingScriptClassCache.getOrCompile(CrossBuildInMemoryCachingScriptClassCache.java:46)
	at org.gradle.groovy.scripts.internal.BuildScopeInMemoryCachingScriptClassCompiler.compile(BuildScopeInMemoryCachingScriptClassCompiler.java:48)
	at org.gradle.groovy.scripts.DefaultScriptCompilerFactory$ScriptCompilerImpl.compile(DefaultScriptCompilerFactory.java:50)
	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:154)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:40)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:25)
	at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:34)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:55)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:573)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:125)
	at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:42)
	at org.gradle.configuration.DefaultBuildConfigurer.configure(DefaultBuildConfigurer.java:38)
	at org.gradle.initialization.DefaultGradleLauncher$2.run(DefaultGradleLauncher.java:154)
	at org.gradle.internal.Factories$1.create(Factories.java:22)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:53)
	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:151)
	at org.gradle.initialization.DefaultGradleLauncher.access$200(DefaultGradleLauncher.java:33)
	at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:114)
	at org.gradle.initialization.DefaultGradleLauncher$1.create(DefaultGradleLauncher.java:106)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:63)
	at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:106)
	at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:92)
	at org.gradle.launcher.exec.GradleBuildController.run(GradleBuildController.java:67)
	at org.gradle.tooling.internal.provider.ExecuteBuildActionRunner.run(ExecuteBuildActionRunner.java:31)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:43)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:42)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:79)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:51)
	at org.gradle.launcher.cli.RunBuildAction.run(RunBuildAction.java:54)
	at org.gradle.internal.Actions$RunnableActionAdapter.execute(Actions.java:173)
	at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:250)
	at org.gradle.launcher.cli.CommandLineActionFactory$ParseAndBuildAction.execute(CommandLineActionFactory.java:217)
	at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:33)
	at org.gradle.launcher.cli.JavaRuntimeValidationAction.execute(JavaRuntimeValidationAction.java:24)
	at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:33)
	at org.gradle.launcher.cli.ExceptionReportingAction.execute(ExceptionReportingAction.java:22)
	at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:210)
	at org.gradle.launcher.cli.CommandLineActionFactory$WithLogging.execute(CommandLineActionFactory.java:174)
	at org.gradle.launcher.Main.doAction(Main.java:33)
	at org.gradle.launcher.bootstrap.EntryPoint.run(EntryPoint.java:45)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:483)
	at org.gradle.launcher.bootstrap.ProcessBootstrap.runNoExit(ProcessBootstrap.java:60)
	at org.gradle.launcher.bootstrap.ProcessBootstrap.run(ProcessBootstrap.java:37)
	at org.gradle.launcher.GradleMain.main(GradleMain.java:24)



接下來,繼續往下面看Load步驟的第二個步驟findAndLoadSettings.

// Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
settings = settingsLoader.findAndLoadSettings(gradle);



四. Load.findAndLoadSettings 

settings = settingsLoader.findAndLoadSettings(gradle);


首先settingsLoader使用了裝飾者模式,從外到內包裝順序是:

public class NotifyingSettingsLoader implements SettingsLoader {
    private final SettingsLoader settingsLoader;
    ...

    @Override
    public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
        SettingsInternal settings = settingsLoader.findAndLoadSettings(gradle);
        
        gradle.getBuildListenerBroadcaster().settingsEvaluated(settings);
        buildLoader.load(settings.getRootProject(), settings.getDefaultProject(), gradle, settings.getRootClassLoaderScope());
        gradle.getBuildListenerBroadcaster().projectsLoaded(gradle);
        return settings;
    }
}


public class CompositeBuildSettingsLoader implements SettingsLoader {
    private final SettingsLoader delegate;
    ...

    @Override
    public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
        SettingsInternal settings = delegate.findAndLoadSettings(gradle);
        ...
    }
}


/**
 * Handles locating and processing setting.gradle files.  Also deals with the buildSrc module, since that modules is
 * found after settings is located, but needs to be built before settings is processed.
 */
public class DefaultSettingsLoader implements SettingsLoader {
    ...

    @Override
    public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
        ...
        
        SettingsInternal settings = findSettingsAndLoadIfAppropriate(gradle, startParameter);
        
        ...

        ProjectSpec spec = ProjectSpecs.forStartParameter(startParameter, settings);
        System.out.println(" spec: " + spec);
        

        if (spec.containsProject(settings.getProjectRegistry())) {
        	System.out.println("spec.containsProject");
            setDefaultProject(spec, settings);
            return settings;
        }

        // Try again with empty settings
        StartParameter noSearchParameter = startParameter.newInstance();
        noSearchParameter.useEmptySettings();
        settings = findSettingsAndLoadIfAppropriate(gradle, noSearchParameter);

        // Set explicit build file, if required
        if (noSearchParameter.getBuildFile() != null) {
            ProjectDescriptor rootProject = settings.getRootProject();
            rootProject.setBuildFileName(noSearchParameter.getBuildFile().getName());
        }
        setDefaultProject(spec, settings);

        return settings;
}


NotifyingSettingsLoader首先會根據裝飾者模式逐個的調用進去,所以DefaultSettingsLoader.findAndLoadSettings方法,然後在回頭看NotifyingSettingsLoader.findAndLoadSettings的其他代碼。


DefaultSettingsLoader根據註釋,它是要處理項目目錄下的setting.gradle文件,同時處理buildSrc模塊,這個好像一般項目沒有。

所以,可以先看DefaultSettingsLoader.findAndLoadSettings方法。


1. DefaultSettingsLoader

文件路徑:

subprojects\core\src\main\java\org\gradle\initialization\DefaultSettingsLoader.java

@Override
public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
    StartParameter startParameter = gradle.getStartParameter();        
    System.out.println("DefaultSettingsLoader.findAndLoadSettings startParameter: " + startParameter
        		+ " gradle: " + gradle);        
    SettingsInternal settings = findSettingsAndLoadIfAppropriate(gradle, startParameter);
    System.out.println(" settings: " + settings);

    ProjectSpec spec = ProjectSpecs.forStartParameter(startParameter, settings);
    System.out.println(" spec: " + spec);
        

    if (spec.containsProject(settings.getProjectRegistry())) {
     	System.out.println("spec.containsProject");
         setDefaultProject(spec, settings);
         return settings;
    }

    // Try again with empty settings
    StartParameter noSearchParameter = startParameter.newInstance();
    noSearchParameter.useEmptySettings();
    settings = findSettingsAndLoadIfAppropriate(gradle, noSearchParameter);

    // Set explicit build file, if required
    if (noSearchParameter.getBuildFile() != null) {
       ProjectDescriptor rootProject = settings.getRootProject();
       rootProject.setBuildFileName(noSearchParameter.getBuildFile().getName());
    }
    setDefaultProject(spec, settings);

    return settings;
}
    

private SettingsInternal findSettingsAndLoadIfAppropriate(GradleInternal gradle,
    ...
    return settingsProcessor.process(gradle, settingsLocation, buildSourceClassLoader, startParameter);
}  


2. 首先看findSettingsAndLoadIfAppropriate 這個方法。看名字應該是尋找Settings並嘗試加載它。
它調用的是settingsProcessor.process 這個settingsProcessor又使用了裝飾者模式,共有3個類。

public class NotifyingSettingsProcessor implements SettingsProcessor {}

public class PropertiesLoadingSettingsProcessor implements SettingsProcessor {
    public SettingsInternal process(GradleInternal gradle,
                                    SettingsLocation settingsLocation,
                                    ClassLoaderScope baseClassLoaderScope,
                                    StartParameter startParameter) {
    	System.out.println("PropertiesLoadingSettingsProcessor run");
        propertiesLoader.loadProperties(settingsLocation.getSettingsDir());
        return processor.process(gradle, settingsLocation, baseClassLoaderScope, startParameter);
    }
}

public class ScriptEvaluatingSettingsProcessor implements SettingsProcessor {
    public SettingsInternal process(GradleInternal gradle,
                                    SettingsLocation settingsLocation,
                                    ClassLoaderScope baseClassLoaderScope,
                                    StartParameter startParameter) {
    	System.out.println("Script Process process");
        Clock settingsProcessingClock = new Clock();
        Map<String, String> properties = propertiesLoader.mergeProperties(Collections.<String, String>emptyMap());
        SettingsInternal settings = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(),
                settingsLocation.getSettingsScriptSource(), properties, startParameter, baseClassLoaderScope);
        applySettingsScript(settingsLocation, settings);
        LOGGER.debug("Timing: Processing settings took: {}", settingsProcessingClock.getTime());
        return settings;
    }
}


真的,gradle源代碼使用了很多裝飾者模式。


3. 首先看看PropertiesLoadingSettingsProcessor.process方法

文件路徑:subprojects\core\src\main\java\org\gradle\initialization\PropertiesLoadingSettingsProcessor.java

public class PropertiesLoadingSettingsProcessor implements SettingsProcessor {
    ...
    public SettingsInternal process(GradleInternal gradle,
                                    SettingsLocation settingsLocation,
                                    ClassLoaderScope baseClassLoaderScope,
                                    StartParameter startParameter) {
    	System.out.println("PropertiesLoadingSettingsProcessor run");
        propertiesLoader.loadProperties(settingsLocation.getSettingsDir());
        return processor.process(gradle, settingsLocation, baseClassLoaderScope, startParameter);
    }
}


文件路徑:

subprojects\core\src\main\java\org\gradle\initialization\DefaultGradlePropertiesLoader.java

public static final String SYSTEM_PROP_PREFIX = "systemProp";
String ENV_PROJECT_PROPERTIES_PREFIX = "ORG_GRADLE_PROJECT_";
String SYSTEM_PROJECT_PROPERTIES_PREFIX = "org.gradle.project.";

public void loadProperties(File settingsDir) {
        loadProperties(settingsDir, startParameter, getAllSystemProperties(), getAllEnvProperties());
    }

    void loadProperties(File settingsDir, StartParameter startParameter, Map<String, String> systemProperties, Map<String, String> envProperties) {
        defaultProperties.clear();
        overrideProperties.clear();
        addGradleProperties(defaultProperties, new File(settingsDir, Project.GRADLE_PROPERTIES));
        addGradleProperties(overrideProperties, new File(startParameter.getGradleUserHomeDir(), Project.GRADLE_PROPERTIES));
        setSystemProperties(startParameter.getSystemPropertiesArgs());
        overrideProperties.putAll(getEnvProjectProperties(envProperties));
        overrideProperties.putAll(getSystemProjectProperties(systemProperties));
        
        for (String key : systemProperties.keySet()) {
            System.out.println("system properties, key: " + key + " value: " + systemProperties.get(key));
	}
        for (String key : envProperties.keySet()) {
	    System.out.println("env properties, key: " + key + " value: " + envProperties.get(key));
	}
        overrideProperties.putAll(startParameter.getProjectProperties());
}
    

private void setSystemProperties(Map<String, String> properties) {
    addSystemPropertiesFromGradleProperties(defaultProperties);
    addSystemPropertiesFromGradleProperties(overrideProperties);
    System.getProperties().putAll(properties);
}

private void addSystemPropertiesFromGradleProperties(Map<String, String> properties) {
    for (String key : properties.keySet()) {
        if (key.startsWith(Project.SYSTEM_PROP_PREFIX + '.')) {
            System.setProperty(key.substring((Project.SYSTEM_PROP_PREFIX + '.').length()), properties.get(key));
        }
    }
}


在loadProperties方法裏面,它首先調用addGradleProperties方法分別把

a. 項目路徑下面的gradle.properties

b. gradle_user_home路徑下面的gradle.properties

兩個文件分別讀取到defaultProperties和overrideProperties兩個map中。

比如,像這種屬性:

org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true


然後調用addSystemProperties方法把項目目錄和gradle_user_home目錄gradle.properties兩個文件裏面'systemProp'開頭的屬性設置到系統屬性裏面;同時把讀取到的系統屬性也設置進去。


再然後調用overrideProperties.putAll把環境變量中'ORG_GRADLE_PROJECT_'和系統屬性中'org.gradle.project.'開頭的變量,存放到overrideProperties map集合中。


最後,把projectProperties屬性加入到overrideProperties map集合中。


4. 繼續看ScriptEvaluatingSettingsProcessor.process

因爲是裝飾者模式,所以在調用完PropertiesLoadingSettingsProcessor.process之後,就會繼續調用它裏面包裝的ScriptEvaluatingSettingsProcessor.process,代碼如下:


文件路徑:subprojects\core\src\main\java\org\gradle\initialization\ScriptEvaluatingSettingsProcessor.java

public class ScriptEvaluatingSettingsProcessor implements SettingsProcessor {
    ...

    public SettingsInternal process(GradleInternal gradle,
                                    SettingsLocation settingsLocation,
                                    ClassLoaderScope baseClassLoaderScope,
                                    StartParameter startParameter) {
    	...
        Map<String, String> properties = propertiesLoader.mergeProperties(Collections.<String, String>emptyMap());
        SettingsInternal settings = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(),
                settingsLocation.getSettingsScriptSource(), properties, startParameter, baseClassLoaderScope);
        applySettingsScript(settingsLocation, settings);
        ...
    }
 
     ...
     
     
     //文件路徑:subprojects\core\src\main\java\org\gradle\initialization\DefaultGradlePropertiesLoader.java
     public Map<String, String> mergeProperties(Map<String, String> properties) {
        Map<String, String> result = new HashMap<String, String>();
        result.putAll(defaultProperties);
        result.putAll(properties);
        result.putAll(overrideProperties);
        return result;
     }   
     
     
     private void applySettingsScript(SettingsLocation settingsLocation, final SettingsInternal settings) {
        ScriptSource settingsScriptSource = settingsLocation.getSettingsScriptSource();
        ClassLoaderScope settingsClassLoaderScope = settings.getClassLoaderScope();
        ScriptHandler scriptHandler = scriptHandlerFactory.create(settingsScriptSource, settingsClassLoaderScope);
        ScriptPlugin configurer = configurerFactory.create(settingsScriptSource, scriptHandler, settingsClassLoaderScope, settings.getRootClassLoaderScope(), true);
        
        System.out.println("applySettingsScript configurer: " + configurer);
        configurer.apply(settings);
    }
}


首先呢,把上一步加載好的屬性都merge在一起,放到properties裏面。

然後調用configurer.apply(settings)設置項目目錄下settings.gradle文件屬性


那DefaultSettingsLoader.findAndLoadSettings就基本結束了,回過頭去看看NotifyingSettingsLoader.findAndLoadSettings的剩餘部分代碼。



5. 繼續看NotifyingSettingsLoader.


public class NotifyingSettingsLoader implements SettingsLoader {
    ...

    @Override
    public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
        SettingsInternal settings = settingsLoader.findAndLoadSettings(gradle);
        ...
        
        gradle.getBuildListenerBroadcaster().settingsEvaluated(settings);
        buildLoader.load(settings.getRootProject(), settings.getDefaultProject(), gradle, settings.getRootClassLoaderScope());
        gradle.getBuildListenerBroadcaster().projectsLoaded(gradle);
        return settings;
    }
}

首先發送一個事件通知settingsEvaulated,就是settings配置文件處理完畢。


然後調用buildLoader.load(xxx)

buildLoader又是使用了一個裝飾者模式!從外到內包裝如下:

public class ProjectPropertySettingBuildLoader implements BuildLoader {
    ...

    public void load(ProjectDescriptor rootProjectDescriptor, ProjectDescriptor defaultProject, GradleInternal gradle, ClassLoaderScope classLoaderScope) {
        buildLoader.load(rootProjectDescriptor, defaultProject, gradle, classLoaderScope);
        setProjectProperties(gradle.getRootProject(), new CachingPropertyApplicator());
    }
}


public class InstantiatingBuildLoader implements BuildLoader {
    private final IProjectFactory projectFactory;

    public InstantiatingBuildLoader(IProjectFactory projectFactory) {
        this.projectFactory = projectFactory;
    }

    /**
     * Creates the {@link org.gradle.api.internal.GradleInternal} and {@link ProjectInternal} instances for the given root project, ready for the projects to be configured.
     */
    public void load(ProjectDescriptor rootProjectDescriptor, ProjectDescriptor defaultProject, GradleInternal gradle, ClassLoaderScope baseClassLoaderScope) {
        createProjects(rootProjectDescriptor, gradle, baseClassLoaderScope);
        attachDefaultProject(defaultProject, gradle);
    }

    private void attachDefaultProject(ProjectDescriptor defaultProject, GradleInternal gradle) {
        gradle.setDefaultProject(gradle.getRootProject().getProjectRegistry().getProject(defaultProject.getPath()));
    }

    private void createProjects(ProjectDescriptor rootProjectDescriptor, GradleInternal gradle, ClassLoaderScope baseClassLoaderScope) {
        ProjectInternal rootProject = projectFactory.createProject(rootProjectDescriptor, null, gradle, baseClassLoaderScope.createChild("root-project"), baseClassLoaderScope);
        
        System.out.println("create project, rootProject: " + rootProject);
        gradle.setRootProject(rootProject);
        addProjects(rootProject, rootProjectDescriptor, gradle, baseClassLoaderScope);
    }   
}


那上面這段代碼的作用就是配置gradle project層級關係,比如上層是項目根目錄,也就是rootProject。

然後試各個模塊,包括主模塊app


日誌如下:

createProject buildFile: E:\work_space\Android-Prototype\build.gradle parent: null
createProject buildFile: E:\work_space\Android-Prototype\app\build.gradle parent: root project 'Android-Prototype'
createProject buildFile: E:\work_space\Android-Prototype\pushsdk\build.gradle parent: root project 'Android-Prototype'
createProject buildFile: E:\work_space\Android-Prototype\moduletest\build.gradle parent: root project 'Android-Prototype'


然後發送project loaded通知。

gradle.getBuildListenerBroadcaster().projectsLoaded(gradle);


至此,Load步驟結束!

if (stage == null) {
  // Evaluate init scripts
  initScriptHandler.executeScripts(gradle);

  // Build `buildSrc`, load settings.gradle, and construct composite (if appropriate)
   settings = settingsLoader.findAndLoadSettings(gradle);

  stage = Stage.Load;
}


下一篇文章來繼續Configure過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章