首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Gradle 创建 Task 的写法不是 Groovy 的标准语法吧?

Gradle 创建 Task 的写法不是 Groovy 的标准语法吧?

作者头像
bennyhuo
发布于 2021-04-26 04:08:12
发布于 2021-04-26 04:08:12
1.3K00
代码可运行
举报
文章被收录于专栏:BennyhuoBennyhuo
运行总次数:0
代码可运行

关键词:Gradle Groovy

任务名居然是以标识符的形式写出来的,你们难道没有觉得奇怪吗?

你一定写过这样的代码来创建一个 Task:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
task clean(type: Delete) {
    delete rootProject.buildDir
}

它定义了一个叫做 "clean" 的任务,这个任务的类型是 Delete。

其中 Delete 是一个类的名字,这是 Groovy 的语法,相当于 Delete.class。这个还好,至少人家语法上支持这样做。

后面的 { ... } 有 Kotlin 经验的小伙伴们自然也不会觉得陌生,这肯定是接收一个 Lambda (在 Groovy 当中就是 Closure)作为参数,里面的 delete rootProject.buildDir 则等价于 delete(rootProject.buildDir),这也是 Groovy 的语法,在 Groovy 当中只要不引起歧义,函数的调用是可以去掉括号的,类似的例子有很多:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
dependencies {
    classpath 'com.android.tools.build:gradle:4.0.1'
    ...
}

这里的 classpath 也是如此。

这都很容易理解。那么问题来了,task clean(...){ ... } 这是个什么语法?我们定义一个名叫 "clean" 的任务,这个任务名不应该是一个字符串字面量吗,但现在按照 Groovy 的语法,它应该等价于 task(clean(...){ ... }) ,这个 clean 看上去其实是个方法名,而不是一个常量。

如果大家跟我一样一开始就绞尽脑汁地去研究这个玩意究竟是什么 Groovy 语法,那你从一开始就错了。这个答案直到我们在翻阅 Gradle 源码的时候,看到有一个叫做 TaskDefinitionScriptTransformer 的类,这个类在 Gradle 脚本编译运行的第二个阶段时被调用,它和其他几个类似的 Transformer 一样,作用就是对源代码的语法树做了一些转换。

大家在 Gradle 源码当中找到这个类之后就会发现,注释已经写的非常清晰了,例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (args.getExpression(0) instanceof MapExpression && args.getExpression(1) instanceof VariableExpression) {
    // Matches: task <name-value-pairs>, <identifier>, <arg>?
    // Map to: task(<name-value-pairs>, '<identifier>', <arg>?)
    transformVariableExpression(call, 1);
} else if (args.getExpression(0) instanceof VariableExpression) {
    // Matches: task <identifier>, <arg>?
    transformVariableExpression(call, 0);
}

通过注释我们可以看到,task 实际上是被当做函数来调用的,我们也确实可以在 Project 当中找到它的定义:

image-20210411072516707

这个映射实际上就是给 identifier 加了个引号,变成字符串字面量。注意到 transformVariableExpression(call, 1); 的第二个参数 1 对应的就是 <identifier>,第二个分支里面的位置则是 0。

这个方法的实现也很显而易见:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void transformVariableExpression(MethodCallExpression call, int index) {
    ArgumentListExpression args = (ArgumentListExpression) call.getArguments();
    //拿到 identifier 对应的表达式
    VariableExpression arg = (VariableExpression) args.getExpression(index);
    if (!isDynamicVar(arg)) {
        return;
    }

    // Matches: task args?, <identifier>, args? or task(args?, <identifier>, args?)
    // Map to: task(args?, '<identifier>', args?)
    String taskName = arg.getText(); // 表达式的内容就是任务名
    call.setMethod(new ConstantExpression("task"));
    // 创建一个以任务名为内容的字符串字面量
    args.getExpressions().set(index, new ConstantExpression(taskName));
}

除了这个转换以外,还有很多其他的情况,现在我们的问题是文章一开始提到的 task clean(...){ ... }应当属于那种转换?属于嵌套方法调用的转换。前面我们已经分析到这个写法其实可以等价于 task(clean(...){ ... }),对应的转换在 maybeTransformNestedMethodCall 方法当中给出了实现,我们摘录一部分给大家了解一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private boolean maybeTransformNestedMethodCall(MethodCallExpression nestedMethod, MethodCallExpression target) {
    ...
    // Matches: task <identifier> <arg-list> | task <string> <arg-list>
    // Map to: task("<identifier>", <arg-list>) | task(<string>, <arg-list>)
    Expression taskName = nestedMethod.getMethod();
    Expression mapArg = null;
    List<Expression> extraArgs = Collections.emptyList();

    if (nestedMethod.getArguments() instanceof TupleExpression) {
        TupleExpression nestedArgs = (TupleExpression) nestedMethod.getArguments();
        if (nestedArgs.getExpressions().size() == 2 && nestedArgs.getExpression(0) instanceof MapExpression && nestedArgs.getExpression(1) instanceof ClosureExpression) {
            // Matches: task <identifier>(<options-map>) <closure>
            mapArg = nestedArgs.getExpression(0);
            extraArgs = nestedArgs.getExpressions().subList(1, nestedArgs.getExpressions().size());
        } else {
            ...
        }
    }

    target.setMethod(new ConstantExpression("task"));
    ArgumentListExpression args = (ArgumentListExpression) target.getArguments();
    args.getExpressions().clear();
    // 如果有 map 参数,放到第一个
    if (mapArg != null) {
        args.addExpression(mapArg);
    }
    // 注意,taskName 被当做参数传入
    args.addExpression(taskName);
    // 剩下的参数
    for (Expression extraArg : extraArgs) {
        args.addExpression(extraArg);
    }
    return true;
}

mapArg 是否为 null,对应了 task 方法的两个重载版本:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Task task(String name, Closure configureClosure);
Task task(Map<String, ?> args, String name, Closure configureClosure);

这么来看,文章开头提到的创建任务的写法,实际上相当于:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
task(type: Delete, "clean") {
    delete rootProject.buildDir
}

其他类似的 Transformer 大家可以自行分析。


C 语言是所有程序员应当认真掌握的基础语言,不管你是 Java 还是 Python 开发者,欢迎大家关注我的新课 《C 语言系统精讲》:

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Kotlin 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
快速迁移 Gradle 脚本至 KTS
大家可以在我的 GitHub 页面找到这个工程:bennyhuo/Android-LuaJavax: Powerful Kotlin style API for Android Lua(https://github.com/bennyhuo/Android-LuaJavax),在提交记录当中可以看到 release 1.0 和 use kts 这两笔提交,前者使用 Groovy 编写 Gradle 脚本,后者使用 Kotlin 编写。
bennyhuo
2021/04/26
3.4K0
快速迁移 Gradle 脚本至 KTS
【Android Gradle 插件】工程根目录下 build.gradle 配置文件 ( 远程仓库配置 | 依赖配置 | 所有子模块配置 | task clean 任务 )
Android Plugin DSL Reference 参考文档 : https://google.github.io/android-gradle-dsl/2.3/
韩曙亮
2023/03/30
1.5K0
【Android 组件化】从模块化到组件化
Android 应用项目 , 都存在一个应用模块 ( Application Module ) , 在 build.gradle 构建脚本中 , 第一个插件配置 com.android.application , 表明 该 Module 编译打包后的输出是 APK 安装包 ; 该项目可以直接运行 ;
韩曙亮
2023/03/29
1.1K0
将构建配置从 Groovy 迁移到 KTS
作为Android开发习惯了面向对象编程,习惯了IDEA提供的各种辅助开发快捷功能。
静默加载
2021/06/28
3.9K0
将构建配置从 Groovy 迁移到 KTS
Android Gradle 学习笔记整理
对于Android开发人员已经了解build.gradle 的 android{} 和 dependencies{} ,但是他的编译过程是什么样的?这个过程中可以干些什么事了解吗?
做个快乐的码农
2021/12/14
1.1K0
Android Gradle 学习笔记整理
Android Studio的build.gradle文件使用(一)
Gradle 是一个非常先进强大的项目构建工具,它使用了一种基于 Groovy 领域的特定语言(DSL)来声明项目设置。
李小白是一只喵
2020/11/26
9880
Android Studio的build.gradle文件使用(一)
Gradle打包工具入门
Gradle是一种自动化构建语言,是一种DSL。目前是Android的默认构建工具,是一个编程框架
仙人技术
2022/03/28
1.2K0
Android Gradle配置分析
Android 开发目前大家使用的IDE是Android Studio,所以和Gradle打交道就是必不可少的了。 大部分时间可能我们关注的都是业务代码的开发,然而了解gradle可以帮助我们更好的构建我们的项目
艳龙
2021/12/16
1.1K0
Android Gradle配置分析
Android—Gradle教程(九)完结篇
到目前为止,Gradle基础以及Kotlin基础讲解完毕。因此,在本篇里,将会以Gradle的构建优化以及如何从Groovy迁移到KTS进行详解!
全栈程序员站长
2022/09/07
3.7K0
Android—Gradle教程(九)完结篇
掌控 Android Gradle
写在前面 目前国内对Android领域的探索已经越来越深,不少技术领域如插件化、热修复、构建系统等都对Gradle有迫切的需求,不懂Gradle将无法完成上述事情。所以Gradle必须要学习。 Gradle 里的几乎任何东西都是基于这两个基础概念: task project 掌握了这两个,你就掌握了一大半的 Gradle 知识了。 首先讲 Task 字面理解为任务,Gradle 中所有执行的事件都是借由 Task 执行的。 例如我们新建一个 Android 工程,在其根目录中输入: gradle tasks
用户1907613
2018/07/20
6720
Gradle-Groovy语法
Groovy 是一种基于 JVM 的动态语言,他的语法和 Java 相似,最终也是要编译 .class 在JVM上运行。
佛系编码
2019/12/11
1.7K0
Gradle-Groovy语法
Android-Gradle(二) Gradle相关配置详解
Gradle是一个项目自动化建构工具,它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的XML。Gradle主要帮我们做了依赖,打包,部署,发布,各种渠道的差异管理等工作。当前其支持的语言限于Java、Groovy和Scala,计划未来将支持更多的语言。
android_薛之涛
2019/08/23
4.4K0
Android-Gradle(二) Gradle相关配置详解
【Android 组件化】使用 Gradle 实现组件化 ( Gradle 变量定义与使用 )
在 Project 层级的 build.gradle 中 , 使用 apply from: “component.gradle” , 引入 component.gradle 配置 ;
韩曙亮
2023/03/29
1.5K0
【Android 组件化】使用 Gradle 实现组件化 ( Gradle 变量定义与使用 )
如何为 Gradle 的 KTS 脚本添加扩展?
现在我们的 Gradle 脚本都迁移到 KTS 了。接下来我们要考虑的问题是,能不能添加一些好用的扩展,方面后续脚本的编写?
bennyhuo
2021/05/14
2.4K0
深入理解gradle中的task
在之前的文章中,我们讲到了如何使用gradle创建一个简单的task,以及task之间怎么依赖,甚至使用了程序来创建task。在本文中,我们会更加深入的去了解一下gradle中的task。
程序那些事
2021/02/15
5210
Gradle和Maven仓库介绍
Gradle是一个基于JVM的构建工具,是一款通用灵活的构建工具,支持maven, Ivy仓库,支持传递性依赖管理,而不需要远程仓库或者是pom.xml和ivy.xml配置文件,基于Groovy,build脚本使用Groovy编写。
李小白是一只喵
2021/01/21
2.3K0
Gradle插件开发-上传Apk到Bugly
前言 上一篇文章已经给大家详细介绍了如何通过Gradle将我们开发好的Library上传到JCenter,基本上就是一系列配置,最后通过Gradle脚本将Library打包成jar或者aar包上传到maven仓库,然后添加到JCenter仓库进行审核,通过之后就能让开发者在gradle脚本进行引用。本篇博客还是基于Gradle,但稍微进阶一下,将跟大家分享一下如何开发一个Gradle插件,这个插件是我为Bugly开发的自动上传apk文件的Gradle插件,目前已经开源,有兴趣的朋友可以到github看看:h
巫山老妖
2018/07/20
1.5K0
2--Gradle入门 - Groovy简介、基本语法
Gradle 需要 Groovy 语言的支持,所以本章节主要来介绍 Groovy 的基本语法。
Devops海洋的渔夫
2023/09/01
1.4K0
2--Gradle入门 - Groovy简介、基本语法
Gradle 中的Task
项目实质上是 Task 对象的集合。一个 Task 表示一个逻辑上较为独立的执行过程,比如编译 Java 源代码,拷贝文件, 打包 Jar 文件,甚至可以是执行一个系统命令。另外,一个 Task 可以读取和设置 Project 的 Property 以完成特定的操作。
鱼找水需要时间
2023/02/16
1.5K0
Gradle 中的Task
Gradle For Android(7)--创建Task以及Plugin
到目前为止,我们已经看到了很多Gradle构建的属性,并且知道了怎么去执行Tasks。这一章,会更多的了解这些属性,并且创建我们自己的Task。一旦知道如何自定义Task之后,就可以完成更多的事情,并且自定义自己的插件,而在多工程中使用这些Task和Plugin。
None_Ling
2018/10/24
2.1K0
Gradle For Android(7)--创建Task以及Plugin
相关推荐
快速迁移 Gradle 脚本至 KTS
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档