1 背景
這一篇我們就從這里開始分析 Gradle 構建的生命周期核心委托對象創建的流程。
2 構建生命周期宏觀淺析
我們在執行 gradle taskName 命令進行 Gradle 框架自身初始化以后會執行到 DefaultGradleLauncher 的 doBuild(Stage.Build) 方法,而該方法首先會觸發構建的 buildListener.buildStarted(gradle) 回調通知構建開始,接著執行 doBuildStages(upTo),然后等待結束后包裝觸發構建的 buildListener.buildFinished(buildResult) 回調通知構建結束,所以我們重點關注下 doBuildStages(upTo) 方法,如下:
private void doBuildStages(Stage upTo) { if (stage == Stage.Build) { throw new IllegalStateException("Cannot build with GradleLauncher multiple times");
} if (stage == null) { initScriptHandler.executeScripts(gradle); settings = settingsLoader.findAndLoadSettings(gradle); stage = Stage.Load;
} if (upTo == Stage.Load) { return;
} if (stage == Stage.Load) { buildOperationExecutor.run("Configure build", new ConfigureBuildAction()); stage = Stage.Configure;
} if (upTo == Stage.Configure) { return;
} stage = Stage.Build; buildOperationExecutor.run("Calculate task graph", new CalculateTaskGraphAction()); buildOperationExecutor.run("Run tasks", new RunTasksAction());
}
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
怎么樣,是不是有點意思了,了解 Gradle 構建生命周期的同學看完上面這個方法基本就能發現 Gradle 構建的核心觸發點都在這里了(不了解 Gradle 構建生命周期的同學請先移步《Gradle腳本基礎全攻略》看看),到此基本就揭示了一個大多數人迷惑的疑問,那就是很多人都想知道自己每次執行 gradle taskName 命令后 Gradle 怎么就能進入構建生命周期,然后按著周期執行,因為當我們輸入命令后首先會初始化 Gradle 構建框架自己,接著把命令行參數包裝好送給 DefaultGradleLauncher,然后觸發 DefaultGradleLauncher 中 Gradle 構建的真正生命周期,從此開始標準構建流程。
關于 Gradle 構建生命周期描述更多細節可以查看官方文檔的 The Build Lifecycle。
【工匠若水 http://blog.csdn.net/yanbober 未經允許嚴禁轉載,請尊重作者勞動成果。私信聯系我】
3 構建生命周期委托對象淺析
在 Gradle 官方 DSL 文檔的 Some basics 部分描述了編寫 Gradle 腳本的靈魂說明,具體如下截圖:
也就是說圍繞整個 Gradle 構建過程的核心是幾個委托實例對象,既然這樣那我們就去分別看看這三種對象是怎么創建的吧。
3-1 Gradle 對象淺析
首先對于 Gradle 對象的繼承關系如下:
public class DefaultGradle extends AbstractPluginAware implements GradleInternal public interface GradleInternal extends Gradle public interface Gradle extends PluginAware
其實這個對象的創建時機我們前面基本已經接觸過了,只是沒有單獨突出說明而已;該對象的創建時機就是 Gradle 框架自身初始化接近尾聲創建 DefaultGradleLauncher 對象時,具體是在 DefaultGradleLauncherFactory 的 doNewInstance 方法中創建,如下:
public class DefaultGradleLauncherFactory implements GradleLauncherFactory { ...... private DefaultGradleLauncher doNewInstance(StartParameter startParameter, GradleLauncher parent,
BuildCancellationToken cancellationToken, BuildRequestMetaData requestMetaData, BuildEventConsumer buildEventConsumer, final BuildSessionScopeServices sessionScopeServices, List<?> servicesToStop) {
BuildScopeServices serviceRegistry = BuildScopeServices.forSession(sessionScopeServices);
...... GradleInternal parentBuild = parent == null ? null : parent.getGradle(); GradleInternal gradle = serviceRegistry.get(Instantiator.class).newInstance(DefaultGradle.class, parentBuild, startParameter, serviceRegistry.get(ServiceRegistryFactory.class)); DefaultGradleLauncher gradleLauncher = new DefaultGradleLauncher(
gradle,
......
);
nestedBuildFactory.setParent(gradleLauncher); return gradleLauncher;
}
......
}
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
上面就是我們說的 Gradle 實例創建時機,可見 Gradle 實例的實現其實是 DefaultGradle 對象,也就是 Gradle 官方 DSL 文檔的 Some basics 中描述的 init script 的委托對象 Gradle。由此在我們編寫 Gradle 腳本時獲取 Gradle 實例后進行的設置其實都是在對該對象進行設置咯,具體編寫參考Gradle 實例對象 DSL Reference 和 Gradle 對象 API 文檔 或者框架源碼。這沒啥可說的,和寫 Java 一樣咯。
3-2 Settings 對象淺析
上面已經介紹了委托實例對象 Gradle 的創建時機,這里再來簡單看看委托實例對象 Settings 的創建和作用;老規矩,先看下 Settings 對象的繼承關系,如下:
public class DefaultSettings extends AbstractPluginAware implements SettingsInternal public interface SettingsInternal extends Settings public interface Settings extends PluginAware
納尼?看起來和 Gradle 對象關系比較類似,其共同祖先都是 PluginAware 接口,先不管這些,我們先去看看他在哪創建的吧。
在上面第二小節的構建生命周期宏觀淺析中我們分析了執行 gradle taskName 命令進行 Gradle 框架自身初始化以后會執行到 DefaultGradleLauncher 的 doBuildStages(Stage upTo) 方法,該方法里有這么一行調用,如下:
settings = settingsLoader.findAndLoadSettings(gradle);
這里的 settingsLoader 和 gradle 實例都是創建 DefaultGradleLauncher 實例時傳入的,關于 gradle 前面已經分析了,settingsLoader 其實是在 DefaultGradleLauncherFactory 創建 DefaultGradleLauncher 實例前通過 settingsLoaderFactory.forTopLevelBuild() 創建的,也就是 DefaultSettingsLoaderFactory 中創建,如下:
public class DefaultSettingsLoaderFactory implements SettingsLoaderFactory { ...... @Override public SettingsLoader forTopLevelBuild() { return new NotifyingSettingsLoader( new CompositeBuildSettingsLoader( new DefaultSettingsLoader(
settingsFinder,
settingsProcessor,
buildSourceBuilder
),
buildServices
),
buildLoader);
}
......
}
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
得到 SettingsLoader 對象以后在 DefaultGradleLauncher 的 doBuildStages(Stage upTo) 方法中就創建了我們要說的 Settings 委托實例對象,至于創建的過程無非就是嘗試依據命令行參數進行匹配查找譬如 settings.gradle 文件等(感興趣的可以去看看 DefaultSettingsLoader 的 findAndLoadSettings 方法中的 findSettingsAndLoadIfAppropriate(gradle, startParameter); 調用實現)。
可見 Settings 實例的實現其實是 DefaultSettings 對象,也就是 Gradle 官方 DSL 文檔的 Some basics 中描述的 settings script 的委托對象 Settings。由此在我們編寫 Gradle 腳本時獲取 Settings 實例后進行的設置其實都是在對該對象進行設置咯,具體編寫參考 Settings 實例對象 DSL Reference 和 Settings 對象 API 文檔 或者框架源碼。不過關于 Settings 委托對象的使用和核心說明除過看上面提到的官方文檔以外,Settings.java 文件的注釋也很言簡意賅,如下:
/**
* <p>Declares the configuration required to instantiate and configure the hierarchy of {@link
* org.gradle.api.Project} instances which are to participate in a build.</p>
*
* <p>There is a one-to-one correspondence between a <code>Settings</code> instance and a <code>{@value
* #DEFAULT_SETTINGS_FILE}</code> settings file. Before Gradle assembles the projects for a build, it creates a
* <code>Settings</code> instance and executes the settings file against it.</p>
*
* <h3>Assembling a Multi-Project Build</h3>
*
* <p>One of the purposes of the <code>Settings</code> object is to allow you to declare the projects which are to be
* included in the build. You add projects to the build using the {@link #include(String[])} method. There is always a
* root project included in a build. It is added automatically when the <code>Settings</code> object is created. The
* root project's name defaults to the name of the directory containing the settings file. The root project's project
* directory defaults to the directory containing the settings file.</p>
*
* <p>When a project is included in the build, a {@link ProjectDescriptor} is created. You can use this descriptor to
* change the default values for several properties of the project.</p>
*
* <h3>Using Settings in a Settings File</h3>
*
* <h4>Dynamic Properties</h4>
*
* <p>In addition to the properties of this interface, the {@code Settings} object makes some additional read-only
* properties available to the settings script. This includes properties from the following sources:</p>
*
* <ul>
*
* <li>Defined in the {@value org.gradle.api.Project#GRADLE_PROPERTIES} file located in the settings directory of the
* build.</li>
*
* <li>Defined the {@value org.gradle.api.Project#GRADLE_PROPERTIES} file located in the user's {@code .gradle}
* directory.</li>
*
* <li>Provided on the command-line using the -P option.</li>
*
* </ul>
*/
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
3-3 Project 對象淺析
上面已經介紹了委托實例對象 Gradle、Settings 的創建時機,這里再來簡單看看委托實例對象 Project 的創建和作用;老規矩,先看下 Project 對象的繼承關系,如下:
public class DefaultProject extends AbstractPluginAware implements ProjectInternal, DynamicObjectAware public interface ProjectInternal extends Project, ProjectIdentifier, FileOperations, ProcessOperations, DomainObjectContext, DependencyMetaDataProvider, ModelRegistryScope, PluginAwareInternal public interface Project extends Comparable<Project>, ExtensionAware, PluginAware
納尼?看起來繼承關系略顯復雜,但是還是挺直觀的,先不管這些,我們從這里看出 Project 接口的實現實例是 DefaultProject 就行。關于這貨在哪創建我想眼尖的人已經知道答案了,前面我們分析 Settings 對象時后有段源碼注釋還記得么,如下(Settings.java):
You add projects to the build using the {@link #include(String[])} method.
When a project is included in the build, a {@link ProjectDescriptor} is created.
You can use this descriptor to change the default values for several properties
of the project.
通過這段注釋可以發現,其實 Project 創建很有可能依賴于 ProjectDescriptor,而 ProjectDescriptor 又是我們在 settings.gradle 中添加的 module 工程。所以我們還是先把目光挪到上面分析 Settings 創建時的 settingsLoader.findAndLoadSettings(gradle);調用,可以發現 NotifyingSettingsLoader 的 findAndLoadSettings(GradleInternal gradle) 方法中調用了如下語句:
public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
SettingsInternal settings = settingsLoader.findAndLoadSettings(gradle);
......
buildLoader.load(settings.getRootProject(), settings.getDefaultProject(), gradle, settings.getRootClassLoaderScope());
gradle.getBuildListenerBroadcaster().projectsLoaded(gradle); return settings;
}
可以看見對應的一個 Settings 里關聯的 ProjectDescriptor 都生成了對應的 Project 對象。可見 Project 實例的實現其實是 DefaultProject 對象(是 Settings 里面對應 ProjectDescriptor 進行轉換生成),也就是 Gradle 官方 DSL 文檔的 Some basics 中描述的 build script 的委托對象 Project。由此在我們編寫 Gradle 腳本時獲取對應不同的 Project 實例后進行的設置其實都是在對該對象進行設置咯,具體編寫參考 Project 實例對象 DSL Reference 和 Project 對象 API 文檔 或者框架源碼。不過關于 Project 委托對象的使用和核心說明除過看上面提到的官方文檔以外,Project.java 文件的注釋也很言簡意賅,值得推薦。
【工匠若水 http://blog.csdn.net/yanbober 未經允許嚴禁轉載,請尊重作者勞動成果。私信聯系我】
4 拋開源碼淺析回到常規腳本編寫總結
上面分析了一堆源碼其實無非就是為了讓我們編寫腳本時能夠做到胸有成竹,不至于被牽著鼻子配,所以有了上面的分析我們可以在我們各個 Gradle 腳本中加入類似如下 log 進行實例對象哈希碼測試(不同 gradle 腳本 log 寫法可能有出入),如下:
println("Gradle Object Test>>>>>>>> gradle="+gradle.hashCode()+", getGradle="+getGradle().hashCode());
println("Gradle Object Test>>>>>>>> project="+this.hashCode());
println("Gradle Object Test>>>>>>>> settings="+settings.hashCode()+", getSettings="+getSettings().hashCode());
經過測試我們會發現結果和我們源碼分析完全一致,也就是說無論在哪個 Gradle 工程模塊的腳本中打印 gradle 或者 getGradle() 對象的 hashCode 都是同一個對象,而一個 settings.gradle 一一對應一個 Settings 實例對象,一個 project module 的 build.gradle 一一對應一個 Project 對象。所以我們在編寫 Gradle 腳本時要時刻銘記這個分析結論,因為記住這個結論對于我們 Gradle 腳本的編寫和理解非常重要,很多人寫不明白 Gradle 腳本的實質其實就是分不清哪個對象有啥 API,哪個 API 屬于哪個層次的對象。
趣聞:譬如想在 Gradle 修改整個項目工程的 buildDir 路徑,有的人模棱兩可認為修改 buildDir 屬性可以加到任何的 build.gradle 文件即可,其實這樣好嗎?(加在 settings.gradle 可能會更好些)通過這篇分析相信你能明白本篇分析帶來的好處。
通過這個系列的這幾篇分析我們漸漸的揭開了 Gradle 構建腳本的神秘之處,這一篇主要分析了構建過程中需要的幾個核心委托對象是怎么創建的,關于創建的這些對象如何使用我建議還是看官方 API 文檔比較直接,后面還會分析其他的,我們慢慢循序漸進吧,限于近身體不太舒服,所以更新比較慢了。官方文檔太多、看不過來,看來得長期戰線慢慢看了,類似 Android 文檔咯。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。