configure阶段完毕后,接下来就是任务执行,但执行有个先后依赖顺序,所以gradle需要在执行任务前,构建有向无环图,为后续执行任务做铺垫,接下来我们看看gradle是如何实现的?
taskExecutionPreparer对象为BuildOperationFiringTaskExecutionPreparer,它也是在GradleScopeServices类中创建的,具体如何调用,此处不在赘述(可以参见之前gradle篇)
前三个步骤都是层层转发任务,最终来到DefaultTaskExecutionPreparer类
public class DefaultTaskExecutionPreparer implements TaskExecutionPreparer {
...
@Override
public void prepareForTaskExecution(GradleInternal gradle) {
// 1. select
buildConfigurationActionExecuter.select(gradle);
// 2. populate taskGraph
TaskExecutionGraphInternal taskGraph = gradle.getTaskGraph();
taskGraph.populate();
// 默认情况下buildControllers.values()为空,所以这里相当不执行,可以忽略
includedBuildControllers.populateTaskGraphs();
// 3. gradle的生命周期projectsEvaluated方法回调
if (gradle.getStartParameter().isConfigureOnDemand()) {
new ProjectsEvaluatedNotifier(buildOperationExecutor).notify(gradle);
}
}
}
configure方法是个典型的拦截器模式,不断消费procesingConfigurationActions,所以我们只要关注下processingBuildActions内部那几个action就好了;断点调试可以发现,有三个buildAction
/**
* A {@link BuildConfigurationAction} which filters excluded tasks.
*/
public class ExcludedTaskFilteringBuildConfigurationAction implements BuildConfigurationAction {
...
@Override
public void configure(BuildExecutionContext context) {
GradleInternal gradle = context.getGradle();
// 启动参数读取剔除的任务名
Set<String> excludedTaskNames = gradle.getStartParameter().getExcludedTaskNames();
if (!excludedTaskNames.isEmpty()) {
final Set<Spec<Task>> filters = new HashSet<Spec<Task>>();
for (String taskName : excludedTaskNames) {
filters.add(taskSelector.getFilter(taskName));
}
// 2. 有向任务图对象设置任务过滤器
gradle.getTaskGraph().useFilter(Specs.intersect(filters));
}
context.proceed();
}
}
从名字就可以看出,这个action是用来剔除指定任务(从启动参数中读取),eg
gradle dist --exclude-task test
从启动参数中读取需要剔去的任务名后,构建相关的过滤器,并设置到gradle的taskGraph对象中,这样后面将任务添加到taskGraph时都会经过这些过滤器
因为终端输入
./gradlew assembleWapaDebug
/**
* A {@link BuildConfigurationAction} that selects the default tasks for a project, or if none are defined, the 'help' task.
*/
public class DefaultTasksBuildExecutionAction implements BuildConfigurationAction {
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTasksBuildExecutionAction.class);
private final ProjectConfigurer projectConfigurer;
public DefaultTasksBuildExecutionAction(ProjectConfigurer projectConfigurer) {
this.projectConfigurer = projectConfigurer;
}
@Override
public void configure(BuildExecutionContext context) {
StartParameter startParameter = context.getGradle().getStartParameter();
for (TaskExecutionRequest request : startParameter.getTaskRequests()) {
if (!request.getArgs().isEmpty()) {
context.proceed();
return;
}
}
// Gather the default tasks from this first group project
ProjectInternal project = context.getGradle().getDefaultProject();
//so that we don't miss out default tasks
projectConfigurer.configure(project);
List<String> defaultTasks = project.getDefaultTasks();
if (defaultTasks.size() == 0) {
defaultTasks = Collections.singletonList(ProjectInternal.HELP_TASK);
LOGGER.info("No tasks specified. Using default task {}", GUtil.toString(defaultTasks));
} else {
LOGGER.info("No tasks specified. Using project default tasks {}", GUtil.toString(defaultTasks));
}
startParameter.setTaskNames(defaultTasks);
context.proceed();
}
}
从上述代码可以分析出:
这个类的职责就是通过任务名找出所有项目中的任务
/**
* A {@link BuildConfigurationAction} which selects tasks which match the provided names. For each name, selects all tasks in all
* projects whose name is the given name.
*/
public class TaskNameResolvingBuildConfigurationAction implements BuildConfigurationAction {
...
@Override
public void configure(BuildExecutionContext context) {
GradleInternal gradle = context.getGradle();
TaskExecutionGraphInternal taskGraph = gradle.getTaskGraph();
List<TaskExecutionRequest> taskParameters = gradle.getStartParameter().getTaskRequests();
for (TaskExecutionRequest taskParameter : taskParameters) {
// 1. taskName解析成多个taskSections
List<TaskSelection> taskSelections = commandLineTaskParser.parseTasks(taskParameter);
for (TaskSelection taskSelection : taskSelections) {
LOGGER.info("Selected primary task '{}' from project {}", taskSelection.getTaskName(), taskSelection.getProjectPath());
// 2. 将taskSection中的任务依次添加到任务图中
taskGraph.addEntryTasks(taskSelection.getTasks());
}
}
context.proceed();
}
}
因为gradle执行任务支持执行多个,所以taskParameters是个列表我们好理解;看下图
./gradlew assembleWapaDebug
assembleWapaDebug任务是怎样被gradle正确解析成多个task?被解析成二个任务了(本次构建中项目有2个一个app 一个module_base)
我们知道task是gradle中的执行基本单元,他是隶属project;要明确task必须只要它属于哪个project;大胆猜测下,gradle是根据task名称去在project遍历寻找的,如果这个taskName没显式所在工程,则遍历所有工程,如果显式声明所在工程则无须遍历project
TaskNameResolver类就是我们刚才猜测任务名是如何被gradle寻找解析的,看下图
方法第三个参数依赖于taskName前是否添加了前缀,如果存在说明是指定project中的task,如果没有gradle会从所有工程中寻找(本例就是属于后者),找到后会把任务搜集起来最终生成TaskSelection对象,交给TaskNameResolvingBuildConfigurationAction处理,它会把任务添加到taskGraph中
最终调用DefaultTaskExecutionGraph.addEntryTasks方法,将任务添加到结点列表中,这个图每个节点都有对应的依赖任务,见下图
:app:assembleWapaDebug 依赖三个任务
task执行的顺序由谁来控制呢?其实就是任务的依赖处理,doAddNodes方法主要就是针对依赖
节点类型分为LocalTaskNode和Resolved,以LocalTaskNode为例
// LocalTaskNode.java
@Override
public void resolveDependencies(TaskDependencyResolver dependencyResolver, Action<Node> processHardSuccessor) {
for (Node targetNode : getDependencies(dependencyResolver)) {
addDependencySuccessor(targetNode);
processHardSuccessor.execute(targetNode);
}
for (Node targetNode : getFinalizedBy(dependencyResolver)) {
if (!(targetNode instanceof TaskNode)) {
throw new IllegalStateException("Only tasks can be finalizers: " + targetNode);
}
addFinalizerNode((TaskNode) targetNode);
processHardSuccessor.execute(targetNode);
}
for (Node targetNode : getMustRunAfter(dependencyResolver)) {
addMustSuccessor((TaskNode) targetNode);
}
for (Node targetNode : getShouldRunAfter(dependencyResolver)) {
addShouldSuccessor(targetNode);
}
}
该方法会对该任务进行任务依赖解析处理,包括finalizedBy 、mustRunAfter、shouldRunAfter等都在这儿解析处理的,只有这样才能实现gradle的有向无环图才能顺利生成
DefaultTaskExecutionGraph.populate
@Override
public void populate() {
// 1. 生成任务有向任务图
ensurePopulated();
if (!hasFiredWhenReady) {
// 2. 有向无环图graphPopulated时间通知
fireWhenReady();
hasFiredWhenReady = true;
} else if (!graphListeners.isEmpty()) {
LOGGER.info("Ignoring listeners of task graph ready event, as this build (" + gradleInternal.getIdentityPath() + ") has already executed work.");
}
}
private void ensurePopulated() {
switch (graphState) {
case EMPTY:
throw new IllegalStateException(
"Task information is not available, as this task execution graph has not been populated.");
case DIRTY:
executionPlan.determineExecutionPlan();
allTasks = null;
graphState = GraphState.POPULATED;
return;
case POPULATED:
}
}
调用链路:
step1: DefaultExecutionPlan.determineExecutionPlan
step2: fireWhenReady() ==》NotifyTaskGraphWhenReady.run
graphPopulated事件通知而已
//DefaultTaskExecutionGraph.java
private void fireWhenReady() {
// We know that we're running single-threaded here, so we can use coarse grained project locks
projectStateRegistry.withMutableStateOfAllProjects(
() -> buildOperationExecutor.run(
new NotifyTaskGraphWhenReady(DefaultTaskExecutionGraph.this, graphListeners.getSource(), gradleInternal)
)
);
}
@Override
public void run(BuildOperationContext context) {
graphListener.graphPopulated(taskExecutionGraph);
context.setResult(NotifyTaskGraphWhenReadyBuildOperationType.RESULT);
}