Gradle 生態(tài)系統(tǒng)源碼分析

Gradle 進階 第七篇

鍥而不舍,金石可鏤

Gradle Project 上卷

Gradle Project 對應(yīng)一個模塊,對于一個多模塊構(gòu)建的項目,它會有 RootProject,和所有 SubProject。這些都是在 setting.gradle 中配置的:

include ':lib'
include ':app'
rootProject.name = "GradleDebug"

Project 的實現(xiàn)類是 DefaultProject。創(chuàng)建它的類是 ProjectFactory。在解析完 setting 文件之后便創(chuàng)建了所有的 Project,并且把 RootProject 設(shè)置成為所有 SubProject 的 Parent。創(chuàng)建代碼如下。

 @Override
    public DefaultProject createProject(GradleInternal gradle, ProjectDescriptor projectDescriptor, ProjectInternal parent, ClassLoaderScope selfClassLoaderScope, ClassLoaderScope baseClassLoaderScope) {
        File buildFile = projectDescriptor.getBuildFile();
        TextResource resource = textFileResourceLoader.loadFile("build file", buildFile);
        ScriptSource source = new TextResourceScriptSource(resource);
        DefaultProject project = instantiator.newInstance(DefaultProject.class,
                projectDescriptor.getName(),
                parent,
                projectDescriptor.getProjectDir(),
                buildFile,
                source,
                gradle,
                gradle.getServiceRegistryFactory(),
                selfClassLoaderScope,
                baseClassLoaderScope
        );
        project.beforeEvaluate(new Action<Project>() {
            @Override
            public void execute(Project project) {
                NameValidator.validate(project.getName(), "project name", DefaultProjectDescriptor.INVALID_NAME_IN_INCLUDE_HINT);
            }
        });

        if (parent != null) {
            parent.addChildProject(project);
        }
        projectRegistry.addProject(project);

        return project;
    }

TextResource resource = textFileResourceLoader.loadFile("build file", buildFile); 表示在創(chuàng)建 DefaultProject 的時候需要獲得 build.gradle 文件。最終把生成的 DefaultProject 文件加入到注冊表中。在這里需要注意一點,Project 的創(chuàng)建早于對應(yīng)的 "build.gradle" 的編譯。
DefaultProject 中維護了許多重要的成員變量。這里展示出部分,并通過講解這些成員變量來剖析一下 Project 扮演的角色。這里我放一張圖來加深理解:

Project.png

    private TaskContainerInternal taskContainer;

    private DependencyHandler dependencyHandler;

    private ConfigurationContainer configurationContainer;

    private ArtifactHandler artifactHandler;

    private ExtensibleDynamicObject extensibleDynamicObject;

TaskContainer

TaskContainer 主要作用是管理一些列的 Task,TaskContainer 本身是一個接口,同時繼承了 NamedDomainObjectCollection 接口,TaskContainerInternal 也是一個接口,繼承了 TaskContainer,真正的實現(xiàn)類是 DefaultTaskContainer。

public class DefaultTaskContainer extends DefaultTaskCollection<Task> implements TaskContainerInternal {

private Task doCreate(Map<String, ?> options, final Action<? super Task> configureAction) {
    ...
}

private <T extends Task> T doCreate(final String name, final Class<T> type, @Nullable final Object[] constructorArgs, final Action<? super T> configureAction) throws InvalidUserDataException {
    ...
}

}

Task 的創(chuàng)建就是通過這兩個重要的方法創(chuàng)建的,后面的 Task 章節(jié)會詳細的講解。這里會簡單用個例子來作一個引子:

task clean(type: Delete) {
    delete rootProject.buildDir
}

在 Gradle 的腳本文件中,直接調(diào)用 task 函數(shù)來創(chuàng)建。上述例子編譯成的.class 文件,用IJ Ide 打開,有這么一段:

         acallsite[1].callCurrent(this, ScriptBytecodeAdapter.createMap(new Object[] {
            "type", org/gradle/api/tasks/Delete
        }), "clean", new _run_closure2(this, this))

其中 acallsite[1] 是 "task",結(jié)合我們在第二篇文章里的方法查找機制,這個方法最終映射到 DefaultProject 的下面這個方法上:

    @Override
    public Task task(Map options, String task, Closure configureClosure) {
        return taskContainer.create(addMaps(options, singletonMap(Task.TASK_NAME, task))).configure(configureClosure);
    }    

這里的 taskContainer 的 create 最終調(diào)用上面所說的 doCreate.

    @Override
    public Task create(Map<String, ?> options) {
        assertMutable("create(Map<String, ?>)");
        return doCreate(options, Actions.doNothing());
    }

這里需要注意一點 { delete rootProject.buildDir } 這個 closure 的作用是為了配置創(chuàng)建出來的 Task。細節(jié)同樣在后續(xù) Task 章節(jié)里講解。
在 doCreate 中會把創(chuàng)建好的 task 加入集合中,同時也會根據(jù)設(shè)置 task 的一些屬性來配置相關(guān)依賴等等。

DependencyHandler

DependencyHandler 的工作主要是幫助 Project 來管理依賴,DefaultDependencyHandler 實現(xiàn)了 DependencyHandler 的接口,DefaultDependencyHandler 是抽象類,Gradle 會在運行時通過 ASM 創(chuàng)建DefaultDependencyHandler_Decorated 類,并實例化。這塊細節(jié)也會在后面的一章講解。
這里舉一個栗子,我們經(jīng)常會為項目添加依賴,在 Gradle 腳本文件中往往是下面這種形式:

dependencies {
    implementation 'com.android.tools.build:gradle:3.1.0'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

這個方法映射到 DefaultProject 里的方法:

    @Override
    public void dependencies(Closure configureClosure) {
        ConfigureUtil.configure(configureClosure, getDependencies());
    } 

其中 getDependencies() 返回的便是 DependencyHandler。這里需要講的一點是,在 dependencies 代碼塊里面的 implementation 'com.android.tools.build:gradle:3.1.0' 實際上是一個函數(shù)調(diào)用,函數(shù)名稱是 implementation,參數(shù)是 'com.android.tools.build:gradle:3.1.0',同時需要注意一點,這里的函數(shù)名其實是和 Configuration 對應(yīng)的。

但是事實上,DefaultDependencyHandler(_Decorated) 中并沒有類似于 'implementaion' 這樣的函數(shù),我在前文講過 BeanDynamicObject,BeanDynamicObject 的作用是使用常規(guī)反射來提供對bean的屬性和方法的訪問,這里的 bean 就是 DefaultDependencyHandler(_Decorated),Gradel 在沒有直接找到方法調(diào)用時,還會有一些備用邏輯,如下:

        if (bean instanceof MethodMixIn) {
            // If implements MethodMixIn, do not attempt to locate opaque method, as this is expensive
            MethodMixIn methodMixIn = (MethodMixIn) bean;
            return methodMixIn.getAdditionalMethods().tryInvokeMethod(name, arguments);
        }

對于 MethodMixIn 類型的 bean,會調(diào)用 getAdditionalMethods() 來繼續(xù)查找。

    public MethodAccess getAdditionalMethods() {
        return dynamicMethods;
    }
    ...
    dynamicMethods = new DynamicAddDependencyMethods(configurationContainer, new DirectDependencyAdder());

DefaultDependencyHandler(_Decorated) 繼承自 MethodMixIn 接口,提供的額外的方法搜索路徑 DynamicAddDependencyMethods。

class DynamicAddDependencyMethods implements MethodAccess {
    ...

    @Override
    public boolean hasMethod(String name, Object... arguments) {
        return arguments.length != 0 && configurationContainer.findByName(name) != null;
    }

    @Override
    public DynamicInvokeResult tryInvokeMethod(String name, Object... arguments) {
        if (arguments.length == 0) {
            return DynamicInvokeResult.notFound();
        }
        Configuration configuration = configurationContainer.findByName(name);
        if (configuration == null) {
            return DynamicInvokeResult.notFound();
        }

        List<?> normalizedArgs = CollectionUtils.flattenCollections(arguments);
        if (normalizedArgs.size() == 2 && normalizedArgs.get(1) instanceof Closure) {
            return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), (Closure) normalizedArgs.get(1)));
        } else if (normalizedArgs.size() == 1) {
            return DynamicInvokeResult.found(dependencyAdder.add(configuration, normalizedArgs.get(0), null));
        } else {
            for (Object arg : normalizedArgs) {
                dependencyAdder.add(configuration, arg, null);
            }
            return DynamicInvokeResult.found();
        }
    }
    ...
}

可以看到在 tryInvokeMethod 方法里實際上通過 configurationContainer,來找到和調(diào)用函數(shù)名相同的 Configuration。然后通過 dependencyAdder 的add 方法調(diào)用到 DefaultDependencyHandler 中的 doAdd 方法。

     private Dependency doAdd(Configuration configuration, Object dependencyNotation, @Nullable Closure configureClosure) {
        if (dependencyNotation instanceof Configuration) {
            DeprecationLogger.deprecateBehaviour("Adding a Configuration as a dependency is a confusing behavior which isn't recommended.")
                .withAdvice("If you're interested in inheriting the dependencies from the Configuration you are adding, you should use Configuration#extendsFrom instead.")
                .willBeRemovedInGradle8()
                .withDslReference(Configuration.class, "extendsFrom(org.gradle.api.artifacts.Configuration[])")
                .nagUser();
            return doAddConfiguration(configuration, (Configuration) dependencyNotation);
        }
        if (dependencyNotation instanceof Provider<?>) {
            return doAddProvider(configuration, (Provider<?>) dependencyNotation, configureClosure);
        } else {
            return doAddRegularDependency(configuration, dependencyNotation, configureClosure);
        }
    }

后續(xù)的如何解析依賴,以及依賴沖突解決等等問題,在后面會詳細講解。

ConfigurationContaine

在上面講 DependencyHandler 一節(jié)中提到了 ConfigurationContainer。它是一個接口,繼承自 NamedDomainObjectContainer 接口,什么是 NamedDomainObjectContainer,舉個栗子:

 configurations {
    // declare a "configuration" named "someConfiguration"
    someConfiguration
 }
 dependencies {
    // add a project dependency to the "someConfiguration" configuration
    someConfiguration project(":lib")
}

就是用戶可以在集合中自定義一些對象,而這些對象可以被加入 Gradle 動態(tài)系統(tǒng)。再舉一個栗子,buildTypes 就提供用戶創(chuàng)建各種各樣的 BuildType:

   buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug{

        }
        xxx{
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'  
        }
    }

Gradle 是如何實現(xiàn)這個功能的,我會在后面的文章單獨來講解?;氐秸},這里的 ConfigurationContaine 是管理構(gòu)建時的 Configuration。

以下是官網(wǎng)解釋什么是 Configuration:

Every dependency declared for a Gradle project applies to a specific scope. For example some dependencies should be used for compiling source code whereas others only need to be available at runtime. Gradle represents the scope of a dependency with the help of a Configuration. Every configuration can be identified by a unique name.

為Gradle項目聲明的每個依賴項都應(yīng)用于特定范圍。例如,某些依賴項應(yīng)該用于編譯源代碼,而其他依賴項只需要在運行時可用。Gradle在配置的幫助下表示依賴關(guān)系的范圍。每個配置都可以用一個唯一的名稱來標(biāo)識。

貼一張官網(wǎng)的圖:

Configurations.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容