文章

03.Gradle原理

03.Gradle原理

Gradle 原理

以 Gradle6.5 源码来分析

Gradle 构建核心流程解析

  • 整体流程:
    zk27z
  • 具体流程:
    b58yy

我们执行一个构建任务的时候,都是执行 ./gradlew assembleDebug 这样的命令,其中的 gradlew 脚本就是整个 gradle 构建的入口,前面的代码基本上就是判断环境,设置变量的,直接看最后一行:

1
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

最后执行的命令基本上如下:

1
exec $JAVA_HOME/bin/java -classpath $APP_HOME/gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain

基本上可以看到,就是执行了 gradle/wrapper/gradle-wrapper.jar 里的 org.gradle.wrapper.GradleWrapperMain

Gradle Wrapper

  • Gradle Wrapper 源码路径:
    $HOME/.gradle/wrapper/dists/gradle-6.5-all/src/wrapper/

当我们执行一个 gralde 命令时,便会调用 gradle/wrapper/gradle-wrapper.jar 里面 org.gradle.wrapper.GradleWrapperMain 类的 main 方法,它就是 gradle 的一个入口方法

1
2
3
4
5
6
7
8
9
// GradleWrapperMain
public static void main(String[] args) throws Exception {
    // ...
    // 1、索引到 gradle-wrapper.properties 文件中配置的 gradle zip 地址,并由此包装成一个WrapperExecutor 实例。
    WrapperExecutor wrapperExecutor = WrapperExecutor.forWrapperPropertiesFile(propertiesFile);
    
    // 2、使用 wrapperExecutor 实例的 execute 方法执行 gradle 命令。
    wrapperExecutor.execute(args, new Install(logger, new Download(logger, "gradlew", "0"), new PathAssembler(gradleUserHome)), new BootstrapMainStarter());
}

继续看看 wrapperExecutor 的 execute 方法:

1
2
3
4
5
6
7
// WrapperExecutor
public void execute(String[] args, Install install, BootstrapMainStarter bootstrapMainStarter) throws Exception {
    // 1、下载 gradle wrapper 需要的依赖与源码。
    File gradleHome = install.createDist(this.config);
    // 2、从这里开始执行 gradle 的构建流程。
    bootstrapMainStarter.start(args, gradleHome);
}

这里就做了两件事:

  1. 下载 gradle wrapper 需要的依赖以及源码

其中的 gradle wrapper 版本就是我们在 gradle/wrapper/gradle-wrapper.properties 里配置的 distributionUrl,下载位置就是在 gradle-wrapper.properties 里配置的 distributionPath 和 zipStorePath。zipStorePath 是下载的压缩包位置,distributionPath 是解压后的位置,一般默认的位置就是 HOME/.gradle/wrapper/dists/,在这里就可以找到 gradle wrapper 的内容了。如果创建过多个项目的话,我们在 HOME/.gradle/wrapper/dists/ 里可以看到不同版本的 gradle wrapper,这也说明了我们之前最开始说的,为什么要使用 gradle wrapper 而不是直接在电脑里安装 gradle,就是因为 gradle wrapper 会根据不同的项目下载不同版本的内容,项目彼此之间互不影响。

  1. 执行 gradle 构建流程 调用 bootstrapMainStarter 的 start 方法从这里开始执行 gradle 的构建流程。其内部最终会依次调用 DefaultGradleLaunchergetLoadedSettingsgetConfiguredBuildexecuteTasksfinishBuild 方法,它们对应的状态都定义在 DefaultGradleLauncher 中的 Stage 枚举类中。

顺着 BootstrapMainStarter.start() 往下执行了,中间过程就不看了,比较曲折,对理解整体流程也没什么太大的帮助。最终会运行到 DefaultGradleLauncher.executeTasks(),然后再往下的流程就非常清晰了

  • Stage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static enum Stage {
    LoadSettings,
    Configure,
    TaskGraph,
    RunTasks {
        String getDisplayName() {
            return "Build";
        }
    },
    Finished;
    private Stage() {
    }
    String getDisplayName() {
        return this.name();
    }
}

当调用 SettingsInternal#getLoadedSettings 方法时便开始了加载 Setting.gradle 的流程:

1
2
3
4
5
// DefaultGradleLauncher Gradle6.5
public SettingsInternal getLoadedSettings() {
    this.doBuildStages(DefaultGradleLauncher.Stage.LoadSettings);
    return this.gradle.getSettings();
}

继续调用了 doBuildStages 方法进行处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// DefaultGradleLauncher Gradle6.5
private void doBuildStages(DefaultGradleLauncher.Stage upTo) {
    Preconditions.checkArgument(upTo != DefaultGradleLauncher.Stage.Finished, "Stage.Finished is not supported by doBuildStages.");
    try {
        // ...
        // 当 Stage 是 RunTask 的时候执行。
        if (upTo == DefaultGradleLauncher.Stage.RunTasks && this.instantExecution.canExecuteInstantaneously()) {
            this.doInstantExecution();
        } else {
           // 当 Stage 不是 RunTask 的时候执行。 this.doClassicBuildStages(upTo);
        }
    } catch (Throwable var3) {
        this.finishBuild(upTo.getDisplayName(), var3);
    }
}

继续调用 doClassicBuildStages 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// DefaultGradleLauncher Gradle6.5
private void doClassicBuildStages(DefaultGradleLauncher.Stage upTo) {
    // 1、当 Stage 为 LoadSettings 时执行 prepareSettings 方法去配置并生成 Setting 实例。
    this.prepareSettings();
    if (upTo != DefaultGradleLauncher.Stage.LoadSettings) {
        // 2、当 Stage 为 Configure 时执行 prepareProjects 方法去配置工程。
        this.prepareProjects();
        if (upTo != DefaultGradleLauncher.Stage.Configure) {
            // 3、当 Stage 为 TaskGraph 时执行 prepareTaskExecution 方法去构建 TaskGraph。
            this.prepareTaskExecution();
            if (upTo != DefaultGradleLauncher.Stage.TaskGraph) {
               // 4、当 Stage 为 RunTasks 时执行 saveTaskGraph 方法 与 runWork 方法保存 TaskGraph 并执行相应的 Tasks。 this.instantExecution.saveTaskGraph();
               this.runWork();
            }
        }
    }
}

doClassicBuildStages 方法是个很重要的方法,它对所有的 Stage 任务进行了分发

  1. 当 Stage 为 LoadSettings 时执行 prepareSettings 方法去配置并生成 Setting 实例
  2. 当 Stage 为 Configure 时执行 prepareProjects 方法去配置工程
  3. 当 Stage 为 TaskGraph 时执行 prepareTaskExecution 方法去构建 TaskGraph
  4. 当 Stage 为 RunTasks 时执行 saveTaskGraph 方法 与 runWork 方法保存 TaskGraph 并执行相应的 Tasks

下面就是各个阶段的分析。

LoadSettings

hqfax
接着继续看看 prepareSettings 方法:

1
2
3
4
5
6
7
8
9
10
11
// DefaultGradleLauncher
private void prepareSettings() {
    if (stage == null) {
        // 1、回调 BuildListener.buildStarted() 回调接口。
        buildListener.buildStarted(gradle);
        // 2、调用 settingPreparer 接口的实现类 DefaultSettingsPreparer 的 prepareSettings 方法。
        settingsPreparer.prepareSettings(gradle);
        // 当前stage状态置为LoadSettings
        stage = Stage.LoadSettings;
    }
}

在 DefaultGradleLauncher#prepareSettings 方法做了两件事:

  1. 回调 BuildListener.buildStarted 接口
  2. 调用 settingPreparer 接口的实现类 DefaultSettingsPreparer 的 prepareSettings 方法。

继续看到 DefaultSettingsPreparer 的 prepareSettings 方法:

1
2
3
4
5
6
7
8
9
@Override
// DefaultSettingsPreparer
public void prepareSettings(GradleInternal gradle) {
    // Evaluate init scripts // 1. 执行 init.gradle,它会在每个项目 build 之前被调用,用于做一些初始化的操作。
    initScriptHandler.executeScripts(gradle);
    // Build `buildSrc`, load settings.gradle, and construct composite (if appropriate) // 2. 调用 SettingLoader 接口的实现类 DefaultSettingsLoader 的 findAndLoadSettings 找到 Settings.gradle 文件的位置。
    SettingsLoader settingsLoader = gradle.isRootBuild() ? settingsLoaderFactory.forTopLevelBuild() : settingsLoaderFactory.forNestedBuild();
    settingsLoader.findAndLoadSettings(gradle);
}

DefaultSettingsPreparer#prepareSettings 方法中做了两项处理

  1. 执行 init.gradle,它会在每个项目 build 之前被调用,用于做一些初始化的操作
  2. 调用 SettingLoader 接口的实现类 DefaultSettingsLoader 的 findAndLoadSettings 找到 Settings.gradle 文件的位置

DefaultSettingLoader 的 findAndLoadSettings 方法关联的实现代码非常多,限于篇幅,我这里直接点出 findAndLoadSettings 方法中的主要处理流程:

  1. 查找 settings.gradle 位置
  2. 编译 buildSrc(Android 默认的 Plugin 目录)文件夹下的内容
  3. 解析 gradle.properties 文件:这里会读取 gradle.properties 文件里的配置信息与命令行传入的配置属性并存储
  4. 解析 settings.gradle 文件:这里最后会调用 BuildOperationScriptPlugin.apply 去执行 settings.gradle 文件。
  5. 根据 settings.gradle 文件中获得的信息去创建 project 以及 subproject 实例

初始化 init.gradle

初始化 init.gradle 代码路径:

1
DefaultSettingsPreparer#prepareSettings → InitScriptHandler#executeScripts

查找 settings.gradle 位置

1
2
3
4
5
6
7
// DefaultSettingsLoader
public SettingsInternal findAndLoadSettings(GradleInternal gradle) {
    // ...
    SettingsLocation settingsLocation = buildLayoutFactory.getLayoutFor(new BuildLayoutConfiguration(startParameter));
    loadGradlePropertiesFrom(settingsLocation);
    // ...
}

现在看 BuildLayoutFactory#getLayoutFor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// BuildLayoutFactory
public BuildLayout getLayoutFor(BuildLayoutConfiguration configuration) {
    if (configuration.isUseEmptySettings()) {
        return buildLayoutFrom(configuration, null);
    }
    File explicitSettingsFile = configuration.getSettingsFile();
    if (explicitSettingsFile != null) {
        if (!explicitSettingsFile.isFile()) {
            throw new MissingResourceException(explicitSettingsFile.toURI(), String.format("Could not read settings file '%s' as it does not exist.", explicitSettingsFile.getAbsolutePath()));
        }
        return buildLayoutFrom(configuration, explicitSettingsFile);
    }

    File currentDir = configuration.getCurrentDir();
    boolean searchUpwards = configuration.isSearchUpwards();
    return getLayoutFor(currentDir, searchUpwards ? null : currentDir.getParentFile());
}

在 getLayoutFor 里,查找 settings.gradle 文件逻辑如下:

  1. 如果参数里通过 -c xxx.gradle 指定了 settings.gradle 文件的位置,那么直接使用指定的 settings.gradle 文件
  2. 如果没有指定 settings 文件,就在当前目录下查找
  3. 如果当前目录没有,会一直往上级目录查找,以及同级的 maseter/ 目录下查找
  4. 如果都没有找到,还是默认在当前目录下

编译 buildSrc 文件夹下的内容,buildSrc 可以看作插件类似的功能

解析 gradle.properties

解析 settings.gradle

创建 project 以及 subproject

Configure

当执行完 LoadSetting 阶段之后,就会执行 Configure 阶段,而配置阶段所作的事情就是 把 gradle 脚本编译成 class 文件并执行

1
2
3
4
5
6
7
8
// DefaultGradleLauncher
private void prepareProjects() {
    // 1、调用 ProjectsPreparer 接口的实现类DefaultProjectsPreparer的prepareProjects()方法。
    if (stage == Stage.LoadSettings) {
        projectsPreparer.prepareProjects(gradle);
        stage = Stage.Configure;
    }
}

继续看 DefaultProjectsPreparer#prepareProjects()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// DefaultProjectsPreparer
public void prepareProjects(GradleInternal gradle) {
    // 1、如果在 gradle.properties 文件中指定了参数 configure-on-demand,则只会配置主项目以及执行 task 所需要的项目。
    if (gradle.getStartParameter().isConfigureOnDemand()) {
        IncubationLogger.incubatingFeatureUsed("Configuration on demand");
        projectConfigurer.configure(gradle.getRootProject());
    } else {
        // // 2、如果没有指定在 gradle.properties 文件中指定参数 configure-on-demand,则会调用 ProjectConfigurer 接口的实现类 TaskPathProjectEvaluator 的 configureHierarchy 方法去配置所有项目。
        projectConfigurer.configureHierarchy(gradle.getRootProject());
        new ProjectsEvaluatedNotifier(buildOperationExecutor).notify(gradle);
    }
    modelConfigurationListener.onConfigure(gradle);
}
public void configureHierarchy(ProjectInternal project) {
        this.configure(project);
        Iterator var2 = project.getSubprojects().iterator();

        while(var2.hasNext()) {
            Project sub = (Project)var2.next();
            this.configure((ProjectInternal)sub);
        }
}

可以看到在 configureHierarchy 方法中使用了 Iterator 遍历并配置了所有 Project。而 configure 方法最终会调用到 EvaluateProject 类的 run 方法:

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
// LifecycleProjectEvaluator.EvaluateProject
public void run(final BuildOperationContext context) {
    this.project.getMutationState().withMutableState(new Runnable() {
        public void run() {
            try {
                
                EvaluateProject.this.state.toBeforeEvaluate();
               
                // 1、 回调 ProjectEvaluationListener 的 beforeEvaluate 接口。 
                LifecycleProjectEvaluator.this.buildOperationExecutor.run(new LifecycleProjectEvaluator.NotifyBeforeEvaluate(EvaluateProject.this.project, EvaluateProject.this.state));
               
                if (!EvaluateProject.this.state.hasFailure()) {
                    EvaluateProject.this.state.toEvaluate();

                    try {
                       // 2、在 evaluate 方法中会设置默认的 init、wrapper task 和 默认插件,然后便会编译、执行 build.gradle 脚本
                       LifecycleProjectEvaluator.this.delegate.evaluate(EvaluateProject.this.project, EvaluateProject.this.state);
                    } catch (Exception var10) {
                        LifecycleProjectEvaluator.addConfigurationFailure(EvaluateProject.this.project, EvaluateProject.this.state, var10, context);
                    } finally {
                        EvaluateProject.this.state.toAfterEvaluate();
                        // 3、回调  ProjectEvaluationListener.afterEvaluate 接口。     
                        LifecycleProjectEvaluator.this.buildOperationExecutor.run(new LifecycleProjectEvaluator.NotifyAfterEvaluate(EvaluateProject.this.project, EvaluateProject.this.state));
                    }
                }

                if (EvaluateProject.this.state.hasFailure()) {
                    EvaluateProject.this.state.rethrowFailure();
                } else {
                    context.setResult(ConfigureProjectBuildOperationType.RESULT);
                }
            } finally {
                EvaluateProject.this.state.configured();
            }

        }
    });
}

在 EvaluateProject 的 run 方法中有如下 三个重要的处理:

  1. 回调 ProjectEvaluationListener 的 beforeEvaluate 接口
  2. 在 evaluate 方法中会设置默认的 init、wrapper task 和 默认插件,然后便会编译并执行 build.gradle 脚本。
  3. 回调 ProjectEvaluationListener.afterEvaluate 接口。

TaskGraph

执行完 初始化阶段 与 配置阶段 之后,就会 调用到 DefaultGradleLauncher 的 prepareTaskExecution 方法去创建一个由 Tasks 组成的一个有向无环图。

1
2
3
4
5
6
7
private void prepareTaskExecution() {
    if (this.stage == DefaultGradleLauncher.Stage.Configure) {
        // 1、调用 TaskExecutionPreparer 接口的实现类 BuildOperatingFiringTaskExecutionPreparer 的 prepareForTaskExecution 方法。
        this.taskExecutionPreparer.prepareForTaskExecution(this.gradle);
        this.stage = DefaultGradleLauncher.Stage.TaskGraph;
    }
}

这里继续调用了 TaskExecutionPreparer 接口的实现类 BuildOperatingFiringTaskExecutionPreparer 的 prepareForTaskExecution 方法

1
2
3
public void prepareForTaskExecution(GradleInternal gradle) {
    this.buildOperationExecutor.run(new BuildOperatingFiringTaskExecutionPreparer.CalculateTaskGraph(gradle));
}

可以看到,这里使用 buildOperationExecutor 实例执行了 CalculateTaskGraph 这个构建操作,我们看到它的 run 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void run(BuildOperationContext buildOperationContext) {
    // 1、填充任务图
    final TaskExecutionGraphInternal taskGraph = this.populateTaskGraph();
    buildOperationContext.setResult(new Result() {
         getRequestedTaskPaths() {
            return this.toTaskPaths(taskGraph.getRequestedTasks());
        }
        public List<String> getExcludedTaskPaths() {
            return this.toTaskPaths(taskGraph.getFilteredTasks());
        }
        private List<String> toTaskPaths(Set<Task> tasks) {
            return ImmutableSortedSet.copyOf(Collections2.transform(tasks, new Function<Task, String>() {
                public String apply(Task task) {
                    return task.getPath();
                }
            })).asList();
        }
    });
}

直接调用了 populateTaskGraph 填充了 Tasks 有向无环图

1
2
3
4
5
TaskExecutionGraphInternal populateTaskGraph() {           
    // 1、这里又调用了 TaskExecutionPreparer 接口的另一个实现类 DefaultTaskExecutionPreparer 的 prepareForTaskExecution 方法。
    BuildOperatingFiringTaskExecutionPreparer.this.delegate.prepareForTaskExecution(this.gradle);
    return this.gradle.getTaskGraph();
}

可以看到,在注释 1 处,又调用了 TaskExecutionPreparer 接口的另一个实现类 DefaultTaskExecutionPreparer 的 prepareForTaskExecution 方法

1
2
3
4
5
6
7
8
9
10
11
public void prepareForTaskExecution(GradleInternal gradle) {
        // 1
        this.buildConfigurationActionExecuter.select(gradle);
        TaskExecutionGraphInternal taskGraph = gradle.getTaskGraph();
        // 2、根据 Tasks 与 Tasks 间的依赖信息填充 taskGraph 实例。
        taskGraph.populate();
        this.includedBuildControllers.populateTaskGraphs();
        if (gradle.getStartParameter().isConfigureOnDemand()) {
            (new ProjectsEvaluatedNotifier(this.buildOperationExecutor)).notify(gradle);
        }
}

1 处调用了 buildConfigurationActionExecuter 接口的 select 方法,这里采用了 策略 + 接口隔离 设计模式,后续会依次调用 ExcludedTaskFilteringBuildConfigurationAction 的 configure 方法、 DefaultTasksBuildExecutionAction 的 configure 方法、TaskNameResolvingBuildConfigurationAction  的 configure 方法

ExcludedTaskFilteringBuildConfigurationAction#configure:处理需要排除的 task

DefaultTasksBuildExecutionAction#configure:添加默认的 task

TaskNameResolvingBuildConfigurationAction#configure:计算 task 依赖图

填充 task 依赖图

RunTasks

1
2
3
4
5
// DefaultGradleLauncher
public GradleInternal executeTasks() {
    this.doBuildStages(DefaultGradleLauncher.Stage.RunTasks);
    return this.gradle;
}

Ref

本文由作者按照 CC BY 4.0 进行授权