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 类,它主要有两个功能:

  1. 下载指定版本的Gradle
  2. 启动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 脚本和

以上是关于Gradle 源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Gradle 源码分析

Gradle 源码分析

Gradle 源码分析

Replugin 源码分析------replugin-plugin-gradle插件源码分析

Replugin 源码分析------replugin-plugin-gradle插件源码分析

Replugin 源码分析------replugin-host-gradle插件源码分析