您好,登錄后才能下訂單哦!
接上一篇 17. Gradle編譯其他應用代碼流程(五) - 設置Task過程,這篇帖子講task的執行過程。
以gradle pmd為例
一. 入口
文件路徑:
subprojects\core\src\main\java\org\gradle\initialization\DefaultGradleLauncher.java
// Execute build buildOperationExecutor.run("Run tasks", new Runnable() { @Override public void run() { buildExecuter.execute(gradle); } });
subprojects\core\src\main\java\org\gradle\execution\DefaultBuildExecuter.java
public class DefaultBuildExecuter implements BuildExecuter { ... public void execute(GradleInternal gradle) { execute(gradle, 0); } private void execute(final GradleInternal gradle, final int index) { if (index >= executionActions.size()) { return; } BuildExecutionAction action = executionActions.get(index); System.out.println("DefaultBuildExecuter. action: " + action + " index: " + index); action.execute(new BuildExecutionContext() { public GradleInternal getGradle() { return gradle; } public void proceed() { execute(gradle, index + 1); } }); } }
DefaultBuildExecuter采用的方法和上一篇帖子一樣,使用遞歸的方法處理executionActions這個集合(估計是同一個人寫 ^_^)。
executionActions包含兩個action
org.gradle.execution.DryRunBuildExecutionAction@1e4d93f7 index: 0 org.gradle.execution.SelectedTaskExecutionAction@76673ed index: 1
其實這兩個action也是繼承自同一接口
public class DryRunBuildExecutionAction implements BuildExecutionAction {} public class SelectedTaskExecutionAction implements BuildExecutionAction {}
下面來看每個action的執行流程:
DryRunBuildExecutionAction跳過所有的task
文件路徑:
subprojects\core\src\main\java\org\gradle\execution\DryRunBuildExecutionAction.java
/** * A {@link org.gradle.execution.BuildExecutionAction} that disables all selected tasks before they are executed. */ public class DryRunBuildExecutionAction implements BuildExecutionAction { public void execute(BuildExecutionContext context) { GradleInternal gradle = context.getGradle(); if (gradle.getStartParameter().isDryRun()) { for (Task task : gradle.getTaskGraph().getAllTasks()) { task.setEnabled(false); } } context.proceed(); } }
從代碼可以看到,它把所有的task都變成了enable=false;那這個是什么意思?
就是不執行任何task!
大家可以加上 --dry-run參數試試,比如gradle assemble --dry-run 這樣就會跳過task執行過程,其他流程,比如配置,plugin等等都會執行。
這種情況對于一些不需要執行task的場景,可以加快執行速度。
:pushsdk:transformNative_libsWithSyncJniLibsForDebug SKIPPED :pushsdk:bundleDebug SKIPPED :pushsdk:compileDebugSources SKIPPED :pushsdk:assembleDebug SKIPPED :pushsdk:compileReleaseSources SKIPPED :pushsdk:assembleRelease SKIPPED :pushsdk:assemble SKIPPED
2. SelectedTaskExecutionAction
文件路徑:
subprojects\core\src\main\java\org\gradle\execution\SelectedTaskExecutionAction.java
public class SelectedTaskExecutionAction implements BuildExecutionAction { public void execute(BuildExecutionContext context) { ... taskGraph.addTaskExecutionGraphListener(new BindAllReferencesOfProjectsToExecuteListener()); taskGraph.execute(); } ... }
文件路徑:
subprojects\core\src\main\java\org\gradle\execution\taskgraph\DefaultTaskGraphExecuter.java
public class DefaultTaskGraphExecuter implements TaskGraphExecuter { ... public void execute() { ... taskPlanExecutor.process(taskExecutionPlan, new EventFiringTaskWorker(taskExecuter.create(), buildOperationExecutor.getCurrentOperationId())); ... } }
文件路徑:
subprojects\core\src\main\java\org\gradle\execution\taskgraph\DefaultTaskPlanExecutor.java
class DefaultTaskPlanExecutor extends AbstractTaskPlanExecutor { ... @Override public void process(TaskExecutionPlan taskExecutionPlan, Action<? super TaskInternal> taskWorker) { System.out.println("DefaultTaskPlanExecutor current thread: " + Thread.currentThread()); taskWorker(taskExecutionPlan, taskWorker, buildOperationWorkerRegistry).run(); taskExecutionPlan.awaitCompletion(); } }
文件路徑:
subprojects\core\src\main\java\org\gradle\execution\taskgraph\AbstractTaskPlanExecutor.java
private static class TaskExecutorWorker implements Runnable { ... public void run() { ... while ((task = taskExecutionPlan.getTaskToExecute()) != null) { BuildOperationWorkerRegistry.Completion completion = buildOperationWorkerRegistry.operationStart(); try { ... processTask(task); ... } finally { completion.operationFinish(); } } ... } protected void processTask(TaskInfo taskInfo) { ... taskWorker.execute(taskInfo.getTask()); ... } }
文件路徑:
subprojects\core\src\main\java\org\gradle\execution\taskgraph\DefaultTaskGraphExecuter.java
/** * This action will set the start and end times on the internal task state, and will make sure * that when a task is started, the public listeners are executed after the internal listeners * are executed and when a task is finished, the public listeners are executed before the internal * listeners are executed. Basically the internal listeners embrace the public listeners. */ private class EventFiringTaskWorker implements Action<TaskInternal> { ... @Override public void execute(TaskInternal task) { ... try { taskListeners.getSource().beforeExecute(task); System.out.println("EventFiringTaskWorker taskExecuter: " + taskExecuter +" task.getState(): " + task.getState()); taskExecuter.execute(task, task.getState(), new DefaultTaskExecutionContext()); taskListeners.getSource().afterExecute(task, state); } finally { long endTime = timeProvider.getCurrentTime(); internalTaskListeners.getSource().afterExecute(taskOperation, new OperationResult(startTime, endTime, task.getState().getFailure())); } } } }
上面幾段代碼都是一直調用下來,并沒有影響到主流程。
下面來看taskExecuter.execute(task, task.getState(), new DefaultTaskExecutionContext());
這個taskExecuter使用了裝飾者模式,裝飾者模式在gradle中使用的太多了(這句話好像不是第一次說了 ^_^)。
下面是包裝的代碼。
文件路徑:
subprojects\core\src\main\java\org\gradle\internal\service\scopes\TaskExecutionServices.java
return new ExecuteAtMostOnceTaskExecuter( new SkipOnlyIfTaskExecuter( new SkipTaskWithNoActionsExecuter( new SkipEmptySourceFilesTaskExecuter( taskInputsListener, new ValidatingTaskExecuter( new SkipUpToDateTaskExecuter( repository, createSkipCachedExecuterIfNecessary( startParameter, gradle.getTaskCaching(), packer, new PostExecutionAnalysisTaskExecuter( new ExecuteActionsTaskExecuter( listenerManager.getBroadcaster(TaskActionListener.class) ) ) ) ) ) ) ) ) );
最后會調用到ExecuteActionsTaskExecuter里面。
不過我們可以稍微解釋下各個封裝Executer的作用。
ExecuteAtMostOnceTaskExecuter:檢查是否已經執行過
SkipOnlyIfTaskExecuter:檢查是否是skip(這個估計是個屬性配置,暫時還沒有找到在哪里配)
SkipTaskWithNoActionsExecuter:檢查是否有action,沒有則返回
SkipEmptySourceFilesTaskExecuter:檢查是否有source file
這種設計模式的話,可以讓每個類專注于自己的功能,然后像一根鏈條一樣把他們串起來,從外到內,依次執行。同時外層還可以處理內層的返回結果。
3. ExecuteActionsTaskExecuter
這個executer從名字上面看就可以知道它的大概意思:執行actions,按照先后順序,逐個執行action,如果某個action出錯,那么將終止執行。
在gradle里面,有幾個概念比較重要,從大到小: project,task, actions
一個project可以有多個子project; 一個project可以有多個task,task用來描述具體的操作;同時一個task可以包含多個action。
文件路徑:
subprojects\core\src\main\java\org\gradle\api\internal\tasks\execution\ExecuteActionsTaskExecuter.java
/** * A {@link org.gradle.api.internal.tasks.TaskExecuter} which executes the actions of a task. */ public class ExecuteActionsTaskExecuter implements TaskExecuter { ... public void execute(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) { ... GradleException failure = executeActions(task, state, context); ... } private GradleException executeActions(TaskInternal task, TaskStateInternal state, TaskExecutionContext context) { ... for (ContextAwareTaskAction action : actions) { state.setDidWork(true); task.getStandardOutputCapture().start(); executeAction(task, action, context); ... } return null; } private void executeAction(TaskInternal task, ContextAwareTaskAction action, TaskExecutionContext context) { action.contextualise(context); try { action.execute(task); } finally { action.contextualise(null); } } }
關于task的描述,大家可以看下Task.java的注釋:
A Task represents a single atomic piece of work for a build, such as compiling classes or generating javadoc. Each task belongs to a Project. You can use the various methods on org.gradle.api.tasks.TaskContainer to create and lookup task instances. For example, org.gradle.api.tasks.TaskContainer.create(String) creates an empty task with the given name. You can also use the task keyword in your build file: task myTask task myTask { configure closure } task myType << { task action } task myTask(type: SomeType) task myTask(type: SomeType) { configure closure } Each task has a name, which can be used to refer to the task within its owning project, and a fully qualified path, which is unique across all tasks in all projects. The path is the concatenation of the owning project's path and the task's name. Path elements are separated using the {@value org.gradle.api.Project#PATH_SEPARATOR} character. Task Actions A Task is made up of a sequence of Action objects. When the task is executed, each of the actions is executed in turn, by calling Action.execute. You can add actions to a task by calling doFirst(Action) or doLast(Action). Groovy closures can also be used to provide a task action. When the action is executed, the closure is called with the task as parameter. You can add action closures to a task by calling doFirst(groovy.lang.Closure) or doLast(groovy.lang.Closure) or using the left-shift << operator. There are 2 special exceptions which a task action can throw to abort execution and continue without failing the build. A task action can abort execution of the action and continue to the next action of the task by throwing a org.gradle.api.tasks.StopActionException. A task action can abort execution of the task and continue to the next task by throwing a org.gradle.api.tasks.StopExecutionException. Using these exceptions allows you to have precondition actions which skip execution of the task, or part of the task, if not true. Task Dependencies and Task Ordering A task may have dependencies on other tasks or might be scheduled to always run after another task. Gradle ensures that all task dependencies and ordering rules are honored when executing tasks, so that the task is executed after all of its dependencies and any "must run after" tasks have been executed. Dependencies to a task are controlled using dependsOn(Object) or setDependsOn(Iterable), and mustRunAfter(Object), setMustRunAfter(Iterable), shouldRunAfter(Object) and setShouldRunAfter(Iterable) are used to specify ordering between tasks. You can use objects of any of the following types to specify dependencies and ordering: A String, CharSequence or groovy.lang.GString task path or name. A relative path is interpreted relative to the task's Project. This allows you to refer to tasks in other projects. A Task. A closure. The closure may take a Task as parameter. It may return any of the types listed here. Its return value is recursively converted to tasks. A null return value is treated as an empty collection. A TaskDependency object. A Buildable object. A Iterable, Collection, Map or array. May contain any of the types listed here. The elements of the iterable/collection/map/array are recursively converted to tasks. A Callable. The call() method may return any of the types listed here. Its return value is recursively converted to tasks. A null return value is treated as an empty collection. Using a Task in a Build File Dynamic Properties A Task has 4 'scopes' for properties. You can access these properties by name from the build file or by calling the property(String) method. You can change the value of these properties by calling the setProperty(String, Object) method. The Task object itself. This includes any property getters and setters declared by the Task implementation class. The properties of this scope are readable or writable based on the presence of the corresponding getter and setter methods. The extensions added to the task by plugins. Each extension is available as a read-only property with the same name as the extension. The convention properties added to the task by plugins. A plugin can add properties and methods to a task through the task's Convention object. The properties of this scope may be readable or writable, depending on the convention objects. The extra properties of the task. Each task object maintains a map of additional properties. These are arbitrary name -> value pairs which you can use to dynamically add properties to a task object. Once defined, the properties of this scope are readable and writable. Dynamic Methods A Plugin may add methods to a Task using its Convention object. Parallel Execution By default, tasks are not executed in parallel. Parallel execution can be enabled by the --parallel flag when the build is initiated. In parallel mode, the tasks of different projects (i.e. in a multi project build) are able to be executed in parallel. If a task is annotated with org.gradle.api.tasks.ParallelizableTask, it may also be executed in parallel with other tasks of the same project. See org.gradle.api.tasks.ParallelizableTask for more details on writing parallelizable tasks.
個人理解意思是(僅供參考):
Task
一個task是一個獨立的原子任務,比如編譯一個類或者生成javadoc。
每個task都屬于一個project,你可以使用TaskContainer里面的多個方法去創建或者查找task引用,例如:TaskContainer.create(String) 可以使用你傳入的名字創建一個空的task。你也可以在你的build文件里面使用'task'關鍵字創建task。
task myTask task myTask { configure closure } task myType << { task action } task myTask(type: SomeType) task myTask(type: SomeType) { configure closure }
每個task都有一個名字,這個名字可以用來和它所在的project關聯,and a fully qualified path, which is unique across all tasks in all projects. The path is the concatenation of the owning project's path and the task's name. Path elements are separated using the {@value org.gradle.api.Project#PATH_SEPARATOR} character.(這段不是很理解,應該是完全限定路徑)。
Task Actions
每個task都有一系列有順序的action,當一個task被執行的時候,它的action會按照順序被逐一執行。你可以通過doFirst(Action)或者doLast(Action)來添加action。
Groovy閉包當然也可以用來提供action,當action被執行的時候,閉包會被作為參數調用。你可以通過doFirst(groovy.lang.Closure)和doLast(groovy.lang.Closure)或者通過左偏移符號"<<"來添加閉包action。
這里有兩個特殊的異常可以允許你去中斷execution,但是不會讓編譯失敗。
org.gradle.api.tasks.StopActionException可以中斷當前執行的action,繼續執行下一個action。
org.gradle.api.tasks.StopExecutionException可以中斷當前task,繼續執行下一個task。
通過這些異常控制,你可以進行一些預處理檢查等等。
其實這兩個異常通過代碼也可以看出來。
for (ContextAwareTaskAction action : actions) { state.setDidWork(true); task.getStandardOutputCapture().start(); try { executeAction(task, action, context); } catch (StopActionException e) { // Ignore LOGGER.debug("Action stopped by some action with message: {}", e.getMessage()); } catch (StopExecutionException e) { LOGGER.info("Execution stopped by some action with message: {}", e.getMessage()); break; } catch (Throwable t) { return new TaskExecutionException(task, t); } finally { task.getStandardOutputCapture().stop(); } }
Task Dependencies and Task Ordering
一個task可能會依賴于其他task,或者需要在其他task之后運行,gradle支持了這種功能。
依賴一個task可以使用dependsOn(Object)或者setDependsOn(Iterable)和 mustRunAfter(Object), setMustRunAfter(Iterable), shouldRunAfter(Object) and setShouldRunAfter(Iterable) ,你可以使用上面這些方法指定先后關系。
在Build文件中使用Task
動態屬性
task有4種范圍的屬性,你可以通過property(String)獲取這個屬性值,或者通過setProperty(String, Object)來設置值。
task本身
extensions
convention
extra
動態方法
plugin可能會通過Convention對象添加方法
并發執行
默認task是順序執行的,并發執行可以通過--parallel 參數開啟。
下一篇會講講gradle的守護進程編譯。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。