Gradle 源码分析
Posted 薛瑄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Gradle 源码分析相关的知识,希望对你有一定的参考价值。
这是一篇Gradle 进阶文章,我假设你已经会 gradle task,自定义plugin 开发。我会带你从源码分析Gradle的整体过程。建议你参照文章 反复阅读源码。文章是基于Gradle 6.7版本
文章目录
一、 启动Gradle
在构建一个任务的时候,经常是通过这个命令 ./gradlew assembleDebug (AS 的按键 打包,也是通过gradlew 命令完成的),先来看下这个脚本最后执行的命令:
# eval 会先把其中的变量进行解析
# set -- 设置到参数中 ,可通过 $@ 来获取到
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\\"-Dorg.gradle.appname=$APP_BASE_NAME\\"" -classpath "\\"$CLASSPATH\\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# 执行命令,$JAVACMD 根据本地的java环境变量 来得到该值
exec "$JAVACMD" "$@"
命令内容如下:
java -Xdock:name=Gradle -Xdock:icon=/Users/android/media/gradle.icns -Dorg.gradle.appname=gradlew -classpath /Users/android/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain :app:assembleUatDebug
最终是执行了gradle-wrapper.jar 的GradleWrapperMain 类,它主要有两个功能:
- 下载指定版本的Gradle
- 启动Gradle流程
下图是GradleWrapperMain的main函数的断点截图:
-
获取 gradle-wrapper.properties文件的路径 ,在
WrapperExecutor.forWrapperPropertiesFile
解析出gradle-wrapper.properties的信息 -
获取gradleUserHome 的路径,可通过命令行指定,默认是系统当前用户目录下的.gradle
下载指定版本的Gradle
1、从gradle-wrapper.properties 获取下载Gradle的URL
2、获取存储路径
- distDir -> /Users/01407714/.gradle/wrapper/dists/gradle-6.7-all/cuy9mc7upwgwgeb72wkcrupxe
- localZipFile -> /Users/01407714/.gradle/wrapper/dists/gradle-6.7-all/cuy9mc7upwgwgeb72wkcrupxe/gradle-6.7-all.zip
wrapper/dists -> 在 gradle-wrapper.properties中进行配置
gradle-6.7-all 下载文件名
cuy9mc7upwgwgeb72wkcrupxe -> Url 变换后的md5值
如果没有下载过,会下载,到上述文件夹的 临时文件 gradle-6.7-all.zip.part
启动Gradle流程
DefaultBuildOperationExecutor $run (RunnableBuildOperation 或者 CallableBuildOperation)
DefaultBuildOperationRunner run ->execute -> 最终执行上面这两个参数的run
BootstrapMainStarter的start 函数
执行到 org.gradle.launcher.GradleMain 会执行到 DefaultGradleLauncher 类的executeTasks函数,Gradle 整体的五个大生命周期,都是从这里开始的,
下面就从这里开始分析
二、 Gradle 的五大流程
以下五大点都是从 org/gradle/initialization/DefaultGradleLauncher.java 类作为起点来分析
2.1 LoadSettings
private void prepareSettings()
if (stage == null)
buildListener.buildStarted(gradle);
settingsPreparer.prepareSettings(gradle);
stage = Stage.LoadSettings;
主要任务:
1、生命周期接口回调 buildListener.buildStarted(gradle);
2、 执行init.gradle
3、查找setting.gradle 文件位置
4、解析gradle.properties
5、解析setting.gradle文件
6、创建 projectState,用于下一阶段创建Project
2.1.1 、生命周期接口回调
buildListener.buildStarted(gradle);
这个生命周期回调时期很早,所以在dsl 或自定义的plugin、task中都不会被调用,它是gradle内部使用的
2.1.2 、执行init.gradle
调用链
-> BuildOperationFiringSettingPreparer.prepareSettings
-> LoadBuild.run
-> DefaultSettingPreparer.preparerSetting
-> InitScriptHandler.executeScripts 从参数从获取到脚本名,并读取出文件
-> DefaultInitScriptProcessor.process 执行脚本
2.1.3 、查找setting.gradle 文件位置
调用链
BuildOperationFiringSettingPreparer.prepareSettings
-> LoadBuild.run
-> DefaultSettingPreparer.preparerSetting
-> CompositeBuildSettingsLoader.findAndLoadSettings
-> ChildBuildRegisteringSettingsLoader.findAndLoadSettings
-> CommandLineIncludedBuildSettingsLoader.findAndLoadSettings
-> SettingsAttachingSettingsLoader.findAndLoadSettings
-> DefaultSettingsLoader.findAndLoadSettings
-> SettingsLocation settingsLocation = buildLayoutFactory.getLayoutFor(new BuildLayoutConfiguration(startParameter));
查找顺序:
1、启动参数中查找是否指定了setting 文件
2、当前目录下查找
3、当前目录的master 下查找
4、往上一层的目录下查找
5、往上一层的目录master下查找
2.1.4 、解析gradle.properties
调用链,接上一小节
-> DefaultSettingsLoader.findAndLoadSettings
-> loadGradlePropertiesFrom
-> DefaultGradlePropertiesController.loadGradlePropertiesFrom
-> NotLoaded.loadGradlePropertiesFrom
-> DefaultGradlePropertiesLoader.loadGradleProperties -> loadProperties
解析gradle.properties 中的值,已map形式保存,赋值给DefaultGradleProperties ,它作为变量,被引用的链路
ScriptEvaluatingSettingProcessor-> DefaultGradlePropertiesController ->State -> DefaultGradleProperties
BuildOperationSettingsProcessor 的代理会引用到ScriptEvaluatingSettingProcessor,下一小节的调用链,可以看到
2.1.5 、解析setting.gradle
调用链,接上一小节
-> DefaultSettingsLoader.findAndLoadSettings
-> SettingsInternal settings = findSettingsAndLoadIfAppropriate(gradle, startParameter, settingsLocation, gradle.getClassLoaderScope());
-> BuildOperationSettingsProcessor.process
-> RootBuildCacheControllerSettingProcessor.process
-> SettingEvaluatedCallbackFiringSettingProcessor.process
-> ScriptEvaluatingSettingProcessor. applySettingsScript 解析出setting.gradle文件,通过ScriptPluginFactorySelector 创建BuildOperationScriptPlugin
->BuildOperationScriptPlugin.apply 编译脚本并执行
@Override
public SettingsInternal process(GradleInternal gradle,
SettingsLocation settingsLocation,
ClassLoaderScope baseClassLoaderScope,
StartParameter startParameter)
Timer settingsProcessingClock = Time.startTimer();
Map<String, String> properties = gradleProperties.mergeProperties(emptyMap());
TextResourceScriptSource settingsScript = new TextResourceScriptSource(textFileResourceLoader.loadFile("settings file", settingsLocation.getSettingsFile()));
//setting.gradle 文件的信息 保存在SettingsInternal对象,其中包含了gradle.properties的信息
//此时,setting.gradle的内容还没被的处理
SettingsInternal settings = settingsFactory.createSettings(gradle, settingsLocation.getSettingsDir(), settingsScript, properties, startParameter, baseClassLoaderScope);
gradle.getBuildListenerBroadcaster().beforeSettings(settings);
applySettingsScript(settingsScript, settings);
LOGGER.debug("Timing: Processing settings took: ", settingsProcessingClock.getElapsed());
return settings;
private void applySettingsScript(TextResourceScriptSource settingsScript, final SettingsInternal settings)
ScriptPlugin configurer = configurerFactory.create(settingsScript, settings.getBuildscript(), settings.getClassLoaderScope(), settings.getBaseClassLoaderScope(), true);
//在这里编译执行脚本,这个函数在后面处理build.gradle 的时候还会遇到,在那里进行分析
configurer.apply(settings);
gradle.properties的信息 保存在settings的如下storage位置:
执行完后,会继续执行DefaultSettingsLoader.findAndLoadSettings 的后续代码setDefaultProject(spec, settings);
设置默认Project,它是根据启动Gradle的命令中指定的 项目路径,来决定哪个module 被设置为默认的
2.1.6 、创建 projectState,用于下一阶段创建Project
根据setting.gradle其中的内容添加project
调用链
BuildOperationFiringSettingPreparer.prepareSettings
-> LoadBuild.run
-> DefaultSettingPreparer.preparerSetting
-> CompositeBuildSettingsLoader.findAndLoadSettings
-> ChildBuildRegisteringSettingsLoader.findAndLoadSettings
-> CommandLineIncludedBuildSettingsLoader.findAndLoadSettings
-> SettingsAttachingSettingsLoader.findAndLoadSettings
-> DefaultProjectStateRegistry.registerProjects(gradle.getServices().get(BuildState.class)); 到这里settings 已经解析出,工程的项目,项目名称,项目路径等等
-> addProject
一个DefaultProjectDescriptor descriptor ,代表一个module,循环调用addProject ,把这些module,按照path,id,comId,多维度保存起来
private void addProject(BuildState owner, DefaultProjectDescriptor descriptor)
Path projectPath = descriptor.path();
Path identityPath = owner.getIdentityPathForProject(projectPath);
ProjectComponentIdentifier projectIdentifier = owner.getIdentifierForProject(projectPath);
ProjectStateImpl projectState = new ProjectStateImpl(owner, identityPath, projectPath, descriptor.getName(), projectIdentifier);
projectsByPath.put(identityPath, projectState);
projectsById.put(projectIdentifier, projectState);
projectsByCompId.put(Pair.of(owner.getBuildIdentifier(), projectPath), projectState);
2.2、Configure
在网上你肯定看到过,Gradle 分为配置阶段和执行阶段,配置阶段指的就是Configure
private void prepareProjects()
if (stage == Stage.LoadSettings)
projectsPreparer.prepareProjects(gradle);
stage = Stage.Configure;
主要任务:
1、为每个module 创建Project
2、Evaluate
3、处理build.gradle
4、apply Plugin
2.2.1 为每个module 创建Project
注意,这里的Project 和AS 中的Project 不是一个概念
调用链:
BuildOperationFiringProjectsPreparer.prepareProjects
-> ConfigureBuild.run
->BuildTreePreparingProjectsPreparer.prepareProjects ->buildLoader.load(gradle.getSettings(), gradle)
-> NotifyingBuildLoader.load
-> ProjectPropertySettingBuildLoader.load
-> InstantiatingBuildLoader.load -> createProject
-> ProjectFactory.createProject (根据2.1.5 的ProjectState 来创建)
->attachDefaultProject
下面来看一下,ProjectFactory.createProject 的函数
private void createProjects(GradleInternal gradle, ProjectDescriptor rootProjectDescriptor)
ClassLoaderScope baseProjectClassLoaderScope = gradle.baseProjectClassLoaderScope();
ClassLoaderScope rootProjectClassLoaderScope = baseProjectClassLoaderScope.createChild("root-project");
//在这里创建Project,这个project 对应的build.gradle 是根路径的
ProjectInternal rootProject = projectFactory.createProject(gradle, rootProjectDescriptor, null, rootProjectClassLoaderScope, baseProjectClassLoaderScope);
//设置根project,
gradle.setRootProject(rootProject);
//递归创建,子project
createChildProjectsRecursively(gradle, rootProject, rootProjectDescriptor, rootProjectClassLoaderScope, baseProjectClassLoaderScope);
private void createChildProjectsRecursively(GradleInternal gradle, ProjectInternal parentProject, ProjectDescriptor parentProjectDescriptor, ClassLoaderScope parentProjectClassLoaderScope, ClassLoaderScope baseProjectClassLoaderScope)
//循环创建Project ,他们是在setting.gradle中声明的module。
for (ProjectDescriptor childProjectDescriptor : parentProjectDescriptor.getChildren())
ClassLoaderScope childProjectClassLoaderScope = parentProjectClassLoaderScope.createChild("project-" + childProjectDescriptor.getName());
ProjectInternal childProject = projectFactory.createProject(gradle, childProjectDescriptor, parentProject, childProjectClassLoaderScope, baseProjectClassLoaderScope);
createChildProjectsRecursively(gradle, childProject, childProjectDescriptor, childProjectClassLoaderScope, baseProjectClassLoaderScope);
下面是创建Project 的代码
@Override
public ProjectInternal createProject(GradleInternal gradle, ProjectDescriptor projectDescriptor, @Nullable ProjectInternal parent, ClassLoaderScope selfClassLoaderScope, ClassLoaderScope baseClassLoaderScope)
Path projectPath = ((DefaultProjectDescriptor) projectDescriptor).path();
ProjectState projectContainer = projectStateRegistry.stateFor(owner.getBuildIdentifier(), projectPath);
//根路径的build.gradle 或是module 下的build.gradle
File buildFile = projectDescriptor.getBuildFile();
TextResource resource = textFileResourceLoader.loadFile("build file", buildFile);
ScriptSource source = new TextResourceScriptSource(resource);
//创建project,你会看到很多地方都是使用instantiator.newInstance 来创建一个对象
DefaultProject project = instantiator.newInstance(DefaultProject.class,
projectDescriptor.getName(),
parent,
projectDescriptor.getProjectDir(),
buildFile,
source,
gradle,
projectContainer,
gradle.getServiceRegistryFactory(),
selfClassLoaderScope,
baseClassLoaderScope
);
//评估前,需要执行的代码,下一小节会介绍评估的流程
project.beforeEvaluate(p -> NameValidator.validate(project.getName(), "project name", DefaultProjectDescriptor.INVALID_NAME_IN_INCLUDE_HINT));
if (parent != null)
parent.addChildProject(project);
//所有的project 都会被添加到projectRegistry
projectRegistry.addProject(project);
projectContainer.attachMutableModel(project);
return project;
在2.1.5中只是确定了哪个module是默认的,但是它对应的Project 还没有,这里创建Project后,接着会调用InstantiatingBuildLoader.load ->attachDefaultProject
把它加入到GradleInternal中的默认Project中
2.2.2 Evaluate
调用链:
-> DefaultProjectsPreparer.prepareProjects 通过GradleInternal 获取到ProjectInternal
-> TaskPathProjectEvaluator. configureHierarchy 遍历ProjectInternal 中的每个project,解析其中的build.gradle
-> DefaultProject.evaluate. 获取evaluator 默认是LifecycleProjectEvaluator
-> LifecycleProjectEvaluator.evaluate org/gradle/configuration/project/LifecycleProjectEvaluator.java
-> EvaluateProject.run
-> ConfigureActionsProjectEvaluator.evaluate
EvaluateProject的run函数
@Override
public void run(final BuildOperationContext context)
project.getMutationState().applyToMutableState(p ->
// Note: beforeEvaluate and afterEvaluate ops do not throw, instead mark state as failed
try
//设置状态
state.toBeforeEvaluate();
//业务逻辑在NotifyBeforeEvaluate,执行project 的BeforeEvaluate中的代码
buildOperationExecutor.run(new NotifyBeforeEvaluate(project, state));
if (!state.hasFailure())
state.toEvaluate();
try
//执行到 ConfigureActionsProjectEvaluator.evaluate
delegate.evaluate(project, state);
catch (Exception e)
addConfigurationFailure(project, state, e, context);
finally
state.toAfterEvaluate();
//业务逻辑在NotifyAfterEvaluate,执行project 的AfterEvaluate中的代码
buildOperationExecutor.run(new NotifyAfterEvaluate(project, state));
if (state.hasFailure())
state.rethrowFailure();
else
context.setResult(ConfigureProjectBuildOperationType.RESULT);
finally
state.configured();
);
ConfigureActionsProjectEvaluator.evaluate 中有3次循环,分别调用如下函数
-> ConfigureActionsProjectEvaluator.evaluate
->PluginsProjectConfigureActions.execute 第一次循环
最终会应用这两插件
->project.getPluginManager().apply("org.gradle.help-tasks");
-> project.getPluginManager().apply("org.gradle.build-init");
->BuildScriptPeocessor.execute 第二次循环
-> BuildOperationScriptPlugin.apply 编译脚本并执行,这个在下一小节分析
->DelayedConfigurationActions.execute 第三次循环
2.2.3 处理build.gradle
借助这个流程来分析一下,源码中编译脚本和运行的逻辑,还记得上面setting.gradle 文件吗?最后也要经过这个处理
-> BuildOperationScriptPlugin.apply
->DefaultScriptPluginFactory$ScriptPluginImpl.apply 首先 编译运行buildscript,在编译运行其他部分
->DefaultScriptCompilerFactory.compile
->BuildScopeInMemoryCachingScriptClassCompiler.compile 在cachedCompiledScripts缓存中查找,如果找到,就直接返回了
->CrossBuildInMemoryCachingScriptClassCache.compile 根据内容生成缓存key,
->FileCacheBackedScriptClassCompiler.compile 根据脚本内容hash,classLoader hash 、dslId ,这三个参数生成文件夹名,例如 gjs5brs4utyodezxmxs4ugji,存储编译后的文件,锁文件等
->DefaultCacheRepository.open 获取缓存路径 例如 .gradle/caches/6.7/scripts/gjs5brs4utyodezxmxs4ugji
->DefaultCacheFactory.open.doOpen
->DefaultPeristenDirectoryStore.open
->DefaultCacheAccess.open
->FixedSharedModeCrossProcessCacheAccess -> open 这里根据锁文件,来判断是否需要编译
->DefaultPeristenDirectoryCache.initialize
->FileCacheBackedScriptClassCompiler.execute 在缓存路径下,创建classesDir、metadataDir两个文件夹
->CompileToCrossBuildCacheAction.execute
->BuildOperationBackedScriptCompilationHandler.compileToDir
->DefaultScriptCompilationHandler.compileToDir
compileScript 参数:待编译资源,classLoader,配置参数,metadataDir,dsl的其他参数(例如:路径、所属项目、类型、id等等)
->GroovyClassLoader.doParseClass 执行编译
编译完后,会在,每个闭包是一个类,紧接着会执行,该阶段不会执行action
@Override
public void apply(final Object target)
DefaultServiceRegistry services = new DefaultServiceRegistry(scriptServices);
services.add(ScriptPluginFactory.class, scriptPluginFactory);
services.add(ClassLoaderScope.class, baseScope);
services.add(LoggingManagerInternal.class, loggingFactoryManager.create());
services.add(ScriptHandler.class, scriptHandler);
final ScriptTarget initialPassScriptTarget = initialPassTarget(target);
ScriptCompiler compiler = scriptCompilerFactory.createCompiler(scriptSource);
// Pass 1, extract plugin requests and plugin repositories and execute buildscript , ignoring (i.e. not even compiling) anything else
CompileOperation<?> initialOperation = compileOperationFactory.getPluginsBlockCompileOperation(initialPassScriptTarget);
Class<? extends BasicScript> scriptType = initialPassScriptTarget.getScriptClass();
ScriptRunner<? extends BasicScript, ?> initialRunner = compiler.compile(scriptType, initialOperation, baseScope, Actions.doNothing());
//执行编译后的class文件 的run函数
initialRunner.run(target, services);
//获取需要初始化的插件,例如这种引用 plugins id 'com.android.application'
PluginRequests initialPluginRequests = getInitialPluginRequests(initialRunner);
PluginRequests mergedPluginRequests = autoAppliedPluginHandler.mergeWithAutoAppliedPlugins(initialPluginRequests, target);
PluginManagerInternal pluginManager = topLevelScript ? initialPassScriptTarget.getPluginManager() : null;
//调用插件的apply函数,下一小节会分析
pluginRequestApplicator.applyPlugins(mergedPluginRequests, scriptHandler, pluginManager, targetScope);
// Pass 2, compile everything except buildscript , pluginManagement, and plugin requests, then run
final ScriptTarget scriptTarget = secondPassTarget(target);
scriptType = scriptTarget.getScriptClass();
CompileOperation<BuildScriptData> operation = compileOperationFactory.getScriptCompileOperation(scriptSource, scriptTarget);
final ScriptRunner<? extends BasicScript, BuildScriptData> runner = compiler.compile(scriptType, operation, targetScope, ClosureCreationInterceptingVerifier.INSTANCE);
if (scriptTarget.getSupportsMethodInheritance() && runner.getHasMethods())
scriptTarget.attachScript(runner.getScript());
if (!runner.getRunDoesSomething())
return;
//执行编译后的class文件 的run函数
Runnable buildScriptRunner = () -> runner.run(target, services);
boolean hasImperativeStatements = runner.getData().getHasImperativeStatements();
scriptTarget.addConfiguration(buildScriptRunner, !hasImperativeStatements);
来看一下,根目录下的build.gradle 的编译结果,buildscript 和其他dsl脚本 ,编译后的缓存位置,是不一样的
build.gradle 内容:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript
ext.kotlin_version = "1.5.0"
repositories
google()
mavenCentral()
maven url 'https://jitpack.io'
dependencies
classpath "com.android.tools.build:gradle:4.1.3"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
allprojects
repositories
google()
mavenCentral()
jcenter() // Warning: this repository is going to shut down soon
task clean(type: Delete)
delete rootProject.buildDir
task task1
println('configure task xuexuan')
doLast
println('action task xuexuan')
buildscript 编译后的缓存路径:
buildscript 编译后的class,每个 build.gradle 脚本和其中的 buildscript,对应一个继承了 ProjectScript 的类。每个闭包,对应一个继承自 Closure 的类
SSH开发模式——Struts2(第二小节)