掌桥专利:专业的专利平台
掌桥专利
首页

编译处理方法、装置、设备及存储介质

文献发布时间:2023-06-19 11:11:32


编译处理方法、装置、设备及存储介质

技术领域

本发明实施例涉及计算机技术领域,尤其涉及编译处理方法、装置、设备及存储介质。

背景技术

目前,应用程序开发者在进行应用程序开发时,通常需要经过项目编译来输出应用程序安装包等文件。项目编译一般指将项目的源代码和资源等打包成可以发布或安装的文件的过程,不同操作系统对应的编译产物可能不同,以安卓(Android)为例,编译的最终产物一般是*.apk文件或者*.aab文件。

应用程序一般会经历不断优化的过程,开发者在开发过程中经常会遇到修改少量代码后重新编译项目来查看效果的需求,这时,为了提高开发效率,可以采用增量编译的方式进行编译。其中,增量编译可以理解为利用之前构建的产物来进行编译,可以提高编译速度。然而,在现有的增量编译方案中,仍存在一些比较耗时的操作,导致开发效率仍有待提升,需要改进。

发明内容

本发明实施例提供了编译处理方法、装置、设备及存储介质,可以优化现有的增量编译方案。

第一方面,本发明实施例提供了一种编译处理方法,该方法包括:

在预设项目编译工具对目标项目进行增量编译的过程中,阻止所述预设项目编译工具中的目标编译任务的执行;

获取所述目标编译任务对应的待编译文件,其中,所述待编译文件包括相比于上一次编译发生变化的第一类型的文件,所述第一类型与所述目标编译任务的输入相对应;

对所述待编译文件进行编译处理,得到第二类型的编译文件,其中,所述第二类型与所述目标编译任务的输出相对应。

第二方面,本发明实施例提供了一种编译处理装置,该装置包括:

编译任务阻止模块,用于在预设项目编译工具对目标项目进行增量编译的过程中,阻止所述预设项目编译工具中的目标编译任务的执行;

待编译文件获取模块,用于获取所述目标编译任务对应的待编译文件,其中,所述待编译文件包括相比于上一次编译发生变化的第一类型的文件,所述第一类型与所述目标编译任务的输入相对应;

编译处理模块,用于对所述待编译文件进行编译处理,得到第二类型的编译文件,其中,所述第二类型与所述目标编译任务的输出相对应。

第三方面,本发明实施例提供了一种计算机设备,包括存储器、处理器及存储在存储器上并可在处理器上运行的计算机程序,所述处理器执行所述计算机程序时实现如本发明实施例提供的编译处理方法。

第四方面,本发明实施例提供了一种计算机可读存储介质,其上存储有计算机程序,该程序被处理器执行时实现如本发明实施例提供的编译处理方法。

本发明实施例中提供的编译处理方案,在预设项目编译工具对目标项目进行增量编译的过程中,阻止预设项目编译工具中的目标编译任务的执行,通过获取目标编译任务对应的待编译文件并对待编译文件进行编译处理的方式对目标编译任务进行替换,其中,待编译文件包括相比于上一次编译发生变化的与目标编译任务的输入相对应的文件,编译后得到与目标编译任务的输出相对应的文件。通过采用上述技术方案,可以针对最近修改过的文件进行编译,减少编译操作,提高增量编译的效率。

附图说明

图1为本发明实施例提供的一种编译处理方法的流程示意图;

图2为本发明实施例提供的又一种编译处理方法的流程示意图;

图3为本发明实施例提供的一种编译任务抽象模型示意图;

图4为本发明实施例提供的一种编译任务划分示意图;

图5为本发明实施例提供的另一种编译处理方法的流程示意图;

图6为本发明实施例提供的一种编译处理装置的结构框图;

图7为本发明实施例提供的一种计算机设备的结构框图。

具体实施方式

下面结合附图和实施例对本发明作进一步的详细说明。可以理解的是,此处所描述的具体实施例仅仅用于解释本发明,而非对本发明的限定。另外还需要说明的是,为了便于描述,附图中仅示出了与本发明相关的部分而非全部结构。此外,在不冲突的情况下,本发明中的实施例及实施例中的特征可以相互组合。

图1为本发明实施例提供的一种编译处理方法的流程示意图,该方法可以由编译处理装置执行,其中该装置可由软件和/或硬件实现,一般可集成在计算机设备中。如图1所示,该方法包括:

步骤101、在预设项目编译工具对目标项目进行增量编译的过程中,阻止所述预设项目编译工具中的目标编译任务的执行。

相关技术中,在进行应用程序开发时,应用程序通常以项目为单位进行构建和管理,应用程序所适用的操作系统不同时,可能需要采用不同的项目构建工具。项目编译可理解为项目构建中的一部分,本发明实施例中的预设项目编译工具的具体类型不做限定,可以是预设项目构建工具,也可以是预设项目构建工具对应的用于编译的插件,统称为预设项目编译工具,也就是说,可以由预设项目构建工具自身对目标项目进行增量编译,也可以利用预设项目构建工具对应的插件对目标项目进行增量编译。项目构建工具以及对应的插件一般为开源的工具,为了便于与本发明实施例中的方案进行区分,可以将预设项目编译工具称为官方编译工具。目标项目可以理解为当前正在开发的应用程序对应的项目。以安卓操作系统为例,安卓应用程序采用的项目构建工具可以是Gradle,可以采用安卓Gradle插件(Android Gradle Plugin,AGP)进行编译。

相关技术中,在进行增量编译的过程中,预设项目编译工具通常会进行大量的分析(如语法树分析),例如,当一个需要进行编译的文件发生变化时,需要分析该文件所依赖的类是否需要参与编译,分析过程比较耗时,还有一些场景的分析也比较简单粗暴,容易导致一些编译任务的全量编译。因此,基于预设项目编译工具的增量编译过程整体上比较耗时,严重影响增量编译效率。

本发明实施例中,可以开发自定义的插件(可称为增量编译加速插件),在增量编译过程中,利用该增量编译加速插件来实现本发明实施例中的编译处理方法。例如,可以在项目对应的根目录下添加增量编译加速插件的源码地址,并设置需要调用或开启该插件的位置,进而在采用预设项目编译工具进行增量编译的过程中,适时地运行增量编译加速插件,以提高增量编译速度。

示例性的,可以对采用预设项目编译工具进行增量编译的流程进行分析,得到多个编译任务,并进一步分析出其中比较耗时的可以进行优化的编译任务,将这类编译任务确定为目标编译任务,目标编译任务的数量不做限定,可以是一个或多个。在预设项目编译工具对目标项目进行增量编译的过程中,对目标编译任务的执行进行阻止,并替换为本发明实施例中的自定义的编译处理操作。

本发明实施例中,阻止预设项目编译工具中的目标编译任务的执行的具体方式不做限定,例如可以采用钩子(Hook)技术实现。本发明实施例中Hook可以指不允许预设项目编译工具执行目标编译任务本身的逻辑,而是切换到执行特定的逻辑,这里的特定的逻辑可以指步骤102和步骤103的相关内容,可以预先编写在增量编译加速插件中。具体的,阻止方式可以是将目标编译任务对应的开关或启用功能关闭,如设置目标编译任务对应的enable=false。

步骤102、获取所述目标编译任务对应的待编译文件,其中,所述待编译文件包括相比于上一次编译发生变化的第一类型的文件,所述第一类型与所述目标编译任务的输入相对应。

示例性的,编译任务通常是将一种类型的文件编译成为另外一种类型的文件,也即将一种类型的文件输入到该编译任务中,经过该编译任务的编译操作会输出另外一种类型的文件。对于目标编译任务来说,可以将需要输入到目标编译任务的文件的类型记为第一类型,将从目标编译任务输出的文件的类型记为第二类型,本发明实施例中,对需要输入到目标编译任务的文件的数量以及具体将那些文件输入到目标编译任务的确定方式进行优化,但输入的文件的类型以及输出的文件的类型与预设项目编译工具的要求一致。例如,预设项目编译工具中的目标编译任务需要将kt文件(采用kotlin语言编写的源码文件,又称*.kt文件)编译成class文件(类文件,又称*.class文件),则第一类型为kt,第二类型为class。开发人员在存在增量编译需求时,通常修改了少量代码,因此,本发明实施例中,针对目标编译任务,可以将相比于上一次编译发生变化的第一类型的文件确定为对应的待编译文件。

示例性的,可以遍历目标编译任务对应的所有第一类型的文件的当前的最近修改时间,将当前的最近修改时间晚于上一次编译对应的最近修改时间的第一类型的文件确定为对应的待编译文件。例如,每次进行编译时,可以记录第一类型的文件的最近修改时间(可以从该文件的文件属性信息中获取),在进行下一次编译时,用于与下一次编译时获取的最近修改时间进行比较。以其中一个第一类型的文件为例,记为第一文件,先查询第一文件的最近修改时间,再查询第一文件在进行上一次编译时所查询到的最近修改时间(也可理解为历史修改时间),其中,上一次编译可以是全量编译,也可以是增量编译,比较当前的最近修改时间和上一次的最近修改时间的大小关系,若当前的最近修改时间晚于上一次的最近修改时间,说明第一文件在两次编译期间发生了修改,可作为待编译文件,若当前的最近修改时间与上一次的最近修改时间相同,则说明第一文件在两次编译期间未发生修改,可以不用参与目标编译任务的编译,也即不会成为待编译文件。这样,不需要进行语法分析,可以快速确定待编译文件,且不会导致某个编译任务的全量编译,可减少待编译文件的数量。

步骤103、对所述待编译文件进行编译处理,得到第二类型的编译文件,其中,所述第二类型与所述目标编译任务的输出相对应。

本发明实施例中,可以获取预设项目编译工具中目标编译任务对应的相关可执行文件(如批处理文件)或命令行等,利用所获取的可执行文件或命令行来完成对待编译文件的编译处理,进而输出第二类型的编译文件。此外,也可以自定义用于对待编译文件的编译处理的命令行,具体编写方式不做限定。

本发明实施例中提供的编译处理方法,在预设项目编译工具对目标项目进行增量编译的过程中,阻止预设项目编译工具中的目标编译任务的执行,通过获取目标编译任务对应的待编译文件并对待编译文件进行编译处理的方式对目标编译任务进行替换,其中,待编译文件包括相比于上一次编译发生变化的与目标编译任务的输入相对应的文件,编译后得到与目标编译任务的输出相对应的文件。通过采用上述技术方案,可以针对最近修改过的文件进行编译,减少编译操作,提高增量编译的效率。

在一些实施例中,所述目标编译任务包括将应用层对应的源码文件编译成类文件的第一目标编译任务、将类文件编译成中间可执行文件的第二目标编译任务、以及将中间可执行文件编译成虚拟机可执行文件的第三目标编译任务中的至少一种。这样设置的好处在于,可以合理的设置目标编译任务,提升增量编译的优化效果。经过发明人对很多预设项目编译工具的增量编译流程进行分析可以得出,以上三种编译任务均比较耗时,尤其是对于一些应用程序来说,应用(Application,App)层的代码量巨大,业务迭代快速,很多业务开发都在应用层进行,因此,第一目标编译任务的耗时往往非常多。当然,具体将上述哪个或哪些编译任务确定为目标编译任务可以根据操作系统、预设项目编译工具以及目标项目本身的各种特点或属性进行设置,具体不做限定。

在一些实施例中,当所述目标编译任务包括第一目标编译任务时,将对应的待编译文件记为待编译源码文件;所述对所述待编译文件进行编译处理,得到第二类型的编译文件,包括:配置所述第一目标编译任务对应的执行环境;在所述执行环境中利用对应的命令行将所述待编译源码文件编译成对应的类文件。这样设置的好处在于,在第一目标编译任务的执行环境中进行编译,可以保证编译结果的准确性,减少出错。其中,配置第一目标编译任务对应的执行环境例如可以包括配置编译文件的输出目录、配置编译处理的依赖路径以及配置其他相关运行参数等,具体不做限定。所述对应的命令行可以理解为用于将待编译源码文件编译成对应的类文件的命令行。

在一些实施例中,所述待编译源码文件包括采用第一编程语言编写的第一待编译源码文件,对应的第一目标编译任务记为第一目标编译子任务;在所述配置所述第一目标编译任务对应的执行环境之前,还包括:下载第一目标编译子任务对应的执行环境文件;解压所述执行环境文件,得到用于将所述第一待编译源码文件编译成相应的类文件的包含第一命令行的可执行文件或第一命令行。这样设置的好处在于,可以通过增量编译加速插件的运行自动下载执行环境文件,并获取用于编译的命令行,为后续编译处理做准备。其中,第一编程语言一般包括出现较晚的编程语言,对由第一编程语言编写的源码文件进行编译时所需的执行环境一般是当前构建环境所不支持的,因此需要利用增量编译加速插件进行下载安装。以安卓应用为例,项目构建环境可以支持Java编写的java文件(又称*.java文件)的编译环境,但对于Kotlin来说,一般并不支持Kotlin编写的kt文件的编译环境,因此,第一编程语言例如可以是Kotlin。需要说明的是,在增量编译加速插件首次执行时,可以进行上述的执行环境文件相关操作,再次执行时可无需再重复下载。可选的,下载第一目标编译子任务对应的执行环境文件,可包括:检测是否存在用于将所述第一待编译源码文件编译成相应的类文件的包含第一命令行的可执行文件或第一命令行,若不存在,则下载第一目标编译子任务对应的执行环境文件。

在一些实施例中,所述配置所述第一目标编译任务对应的执行环境,包括:将虚拟机的堆的大小设置为预设数值。这样设置的好处在于,可以降低出现内存溢出异常的情况的概率。在实施本发明实施例的过程中,发现当第一待编译源码文件数量较多时,可能会抛出内存溢出异常,因此,可以在增量编译加速插件中编写设置虚拟机的堆的大小的代码,根据目标项目的特点将虚拟机的堆的大小设置成合适的数值,也即预设数值。

在一些实施例中,所述待编译源码文件包括采用第二编程语言编写的第二待编译源码文件,对应的第一目标编译任务记为第二目标编译子任务;获取所述第二目标编译子任务对应的第二待编译文件,包括:查询所述第二目标编译子任务对应的源码文件的当前的最近修改时间,将当前的最近修改时间晚于上一次编译对应的最近修改时间的源码文件确定为候选源码文件;从所述候选源码文件中滤除数据绑定功能对应的文件目录中的源码文件,得到所述第二目标编译子任务对应的第二待编译源码文件。这样设置的好处在于,可以合理确定第二目标编译子任务对应的第二待编译文件。第二编程语言可以与第一编程语言不同。在一些预设项目编译工具中,会存在数据绑定功能(databinding),该功能可以以插件形式实现,在实施本发明实施例的过程中,发现数据绑定功能有时会导致第二目标编译子任务对应的所有源码文件的修改时间被更改,进而在利用修改时间进行待编译文件识别时,部分源码文件被误识别为第二目标编译子任务对应的第二待编译文件,而实质上源码文件中的内容并未发生变化,因此可以不需要编译,将数据绑定功能对应的文件目录中的源码文件滤除,可以有效减少第二待编译文件的数量,提升第二目标编译子任务的执行效率。

在一些实施例中,所述配置所述第一目标编译任务对应的执行环境,包括:配置所述待编译源码文件经过编译后得到的类文件的输出目录为预设输出目录,其中,所述预设输出目录与所述预设项目编译工具中的所述第一目标编译任务的输出目录不同。这样设置的好处在于,可以利用预设输出目录对经过编译后得到的类文件进行单独管理,便于后续的编译任务有针对性的选择待编译文件,提高待编译文件的筛选效率。示例性的,可以采用预设项目编译工具中的用于配置输出目录的命令来指定编译结果的存储目录为预设输出目录。

在一些实施例中,当所述目标编译任务包括第二目标编译任务时,将对应的待编译文件记为待编译类文件,获取所述第二目标编译任务对应的待编译类文件,包括:从所述预设输出目录中获取所述第二目标编译任务对应的待编译类文件。这样设置的好处在于,可以进一步提升编译效率。由于预设项目编译工具中各编译任务是顺序执行的,也就是说一个编译任务的输出是下一个编译任务的输入,若一个编译任务的输入未发生变化,则该任务的状态可以是最新状态,可以不用执行,在第一目标编译任务和第二目标编译任务之间存在编译目标为类文件的第四编译任务(可以存在一个或多个)时,将第一目标编译任务输出的类文件输出到与预设项目编译工具中的第一目标编译任务的输出目录不同的预设输出目录,这样第四编译任务就不会感知到输入的变化,因此,可以不用执行,节省增量编译时间,且第二目标编译任务的执行被阻止,因此可以通过从预设输出目录中获取第二目标编译任务对应的待编译类文件的方式来保证第二目标编译任务的输入准确。

在一些实施例中,当所述目标编译任务包括第二目标编译任务时,将对应的待编译文件记为待编译类文件;所述对所述待编译文件进行编译处理,得到第二类型的编译文件,包括:从所述目标项目对应的软件开发工具包中获取预设编译工具对应的用于将所述待编译类文件编译成相应的中间可执行文件的第二命令行;获取所述第二命令行对应的配置信息,并基于所述配置信息利用所述第二命令行将所述待编译类文件编译成相应的中间可执行文件。这样设置的好处在于,可以直接从目标项目对应的软件开发工具包(SoftwareDevelopment Kit,SDK)中获取用于编译处理的第二命令行,降低增量编译加速插件的开发难度及成本,另外可以通过添加配置信息的方式对第二命令行的执行进行配置,保证编译处理过程的准确性。

在一些实施例中,所述方法还包括:将所述预设项目编译工具中的摘要校验功能关闭。这样设置的好处在于,由于对部分编译任务以及待编译文件进行了改动,可能会出现编译错误,经过分析后发现,导致编译错误的原因可能是预设项目编译工具中原有的摘要校验功能发生异常,为了避免发生编译错误的情况,可以将该功能关闭。

在一些实施例中,所述预设项目编译工具包括Gradle,所述第一编程语言包括Kotlin,所述第二编程语言包括Java。这样设置的好处在于,可以适用于同时采用Kotlin和Java进行开发的安卓应用项目的增量编译,有效提升增量编译效率。

图2为本发明实施例提供的又一种编译处理方法的流程示意图,该方法包括:

步骤201、在预设项目编译工具对目标项目进行增量编译的过程中,阻止预设项目编译工具中的第一目标编译子任务的执行,并获取第一目标编译子任务对应的第一待编译源码文件。

其中,第一待编译源码文件采用第一编程语言编写,第一目标编译子任务为将应用层对应的第一待编译源码文件编译成类文件。

步骤202、下载第一目标编译子任务对应的执行环境文件,解压所述执行环境文件,得到用于将所述第一待编译源码文件编译成相应的类文件的第一命令行。

步骤203、配置第一目标编译子任务对应的执行环境,在执行环境中利用对应的命令行将第一待编译源码文件编译成对应的类文件,并输出至预设输出目录。

示例性的,配置第一目标编译子任务对应的执行环境可包括:配置第一待编译源码文件经过编译后得到的类文件的输出目录为预设输出目录,预设输出目录与预设项目编译工具中的第一目标编译子任务的输出目录不同;将虚拟机的堆的大小设置为预设数值;以及配置编译处理的依赖路径等。

步骤204、阻止预设项目编译工具中的第二目标编译子任务的执行,并获取第二目标编译子任务对应的第二待编译源码文件。

其中,第二待编译源码文件采用第二编程语言编写,第一编程语言的出现晚于第二编程语言。第二目标编译子任务为将应用层对应的第二待编译源码文件编译成类文件。

步骤205、配置第二目标编译子任务对应的执行环境,在执行环境中利用对应的命令行将第二待编译源码文件编译成对应的类文件,并输出至预设输出目录。

步骤206、阻止预设项目编译工具中的第二目标编译任务的执行,并从预设输出目录中获取第二目标编译任务对应的待编译类文件。

步骤207、从目标项目对应的软件开发工具包中获取预设编译工具对应的用于将待编译类文件编译成相应的中间可执行文件的第二命令行,获取第二命令行对应的配置信息,并基于配置信息利用所述第二命令行将待编译类文件编译成相应的中间可执行文件。

步骤208、将所编译的中间可执行文件输出至预设项目编译工具中的第二目标编译任务的输出目录。

本发明实施例提供的编译处理方法,在预设项目编译工具对目标项目进行增量编译的过程中,对将应用层对应的源码文件编译成类文件的第一目标编译任务以及将类文件编译成中间可执行文件的第二目标编译任务进行替换,针对相比于上一次编译发生变化的相关文件进行编译,在保证编译结果正确性的情况下,可有效提升增量编译效率。

下面以目标项目对应的应用程序为安卓应用程序,预设项目编译工具为Gradle,第一编程语言为Kotlin,第二编程语言为Java为例进行进一步说明。Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建开源工具。Android项目编译是指将Android项目的源代码、资源打包成可以发布或安装的过程,编译的最终产物一般是*.apk或者*.aab。Android中经过一次项目编译后,由于Gradle的缓存机制,之后的编译都可以利用之前构建的产物来提高编译速度,即称为Android增量编译。Gradle scan是Gradle里用来分析编译构建的官方工具,可以用来分析编译速度、依赖以及性能等等,利用Gradlescan对增量编译流程进行分析后,可以得到编译任务(Task,简称T)的执行流程图,经过分析可以将增量编译执行流程划分为7个阶段。需要说明的是,下文中的App模块指的是applyplugin:'com.android.application'的模块;library(库)模块是指apply plugin:'com.android.library'的模块,或者只有java或只有kt的模块;动态模块是指applyplugin:'com.android.dynamic-feature'的模块。

如果遵守Gradle的最佳实践,在经过一次编译后,如果没有任何修改再次执行编译的时候,所有的任务应该都能跳过执行,也即不会重复执行。图3为本发明实施例提供的一种编译任务抽象模型示意图,Gradle里的任务可以抽象为如图3所示的模型,该模型可以表示将输入(inputs)利用任务执行转化为输出(outputs)的过程。在对预设项目编译工具的编译流程进行优化时,可以考虑复用Gradle的任务而不用重新执行,具体可以为Gradle的任务定义输入和输出,如图3所示,任务执行阶段将输入转化为特定的输出,如果任务的输入相比于上一次编译的输入没有变化,同时输出也没有被修改,这个时候任务的状态就是最新状态(UP-TO-DATE),就不会重复执行,这样就能利用缓存提高编译速度。

图4为本发明实施例提供的一种编译任务划分示意图,下面结合图4对7个阶段对应的编译任务进行简单介绍:

第一任务(T1):执行所有library模块的编译,即library模块执行kotlinc(将kt文件编译为class文件)以及javac(将java文件编译为class文件)等任务的过程,各library模块间是可以并行执行的,优化的空间较小。

第二任务(T2):App层执行kotlinc的过程,也就是App模块将*.kt文件(以下简称kt文件)编译为*.class的过程,此阶段是完全串行执行的,也就是执行该任务的过程中,其他任务都需要等待。App层的kotlinc的任务需要依赖library层编译好的class文件,也就是T1的输出是T2的输入,所以T2需要等待T1执行完成,若T1的输出有变化会导致T2重新执行。该阶段效率很低,需要探索优化的空间。

第三任务(T3):App层执行javac的过程,也就是App模块将*.java代码编译为*.class的过程,此阶段是完全串行执行的,javac需要依赖kotlinc的任务编译好的class文件(Kotlin作为一门晚于Java出现的新语言,Kotlin编译的时候可识别Java源文件,但是Java语言不能直接识别kt文件,Java语言只能识别kt的编译产物即class文件,这也是Kotlin先于Java编译的原因)。另外,动态模块会依赖App模块,即T4需要等待获取T3阶段编译好的class文件。该阶段效率很低,需要探索优化的空间。

第四任务(T4):动态(dynamic-feature)模块执行编译的过程,即动态模块执行kotlinc以及javac等任务的过程,该过程各动态模块之间也是可以并行执行的,此阶段的执行效率也较高,优化空间较小。

第五任务(T5):执行项目中自定义的变形(Transform)插件的过程,该阶段需要得到所有模块的class文件,所以T5需要等待T4,该阶段比较耗时,可能存在优化空间。

第六任务(T6):执行d8的第一个阶段任务,将*.class代码编译为中间*.dex的过程,dex文件(*.dex文件)是安卓平台上虚拟机的可执行文件,中间dex文件是不能直接在虚拟机中运行的dex文件,需要经过后面的合并处理后,成为真正的dex文件。d8是新一代的编译工具,替代之前的dx工具,Android Gradle插件使用该工具来将项目的kt或Java字节码编译为在Android设备上运行的DEX字节码。该阶段生成的dex只是中间产物,不能直接在运行时(Android Runtime,ART)虚拟机等虚拟机中运行,还需要经过T7的融合(merge)过程。原有的dx命令已经废弃,迁移到最新的d8后,为了让生成dex的过程支持增量编译,d8支持了仅编译项目的部分Java字节码。如果启用了按类dexing(指将*.class代码编译为中间*.dex)处理,则只需重新编译自上次构建以来修改过的类文件。例如项目中有三个类文件:A.class、B.class和C.class,当只有A.class有修改时,可以在增量编译时只编译A.class生成A.dex,B.class和C.class不用编译,完全复用上一次编译的产物。该阶段执行时间较长,需要探索优化的空间。

第七任务(T7):执行d8的第二个阶段任务,将各中间*.dex一起merge为一个或者多个*.dex的过程,该阶段执行时间较长,可以探索优化的空间。

经过上述分析,可以得出存在优化空间的任务,本发明实施例中可以针对上述T2、T3、T6、T4和T5进行优化,下面简单介绍一下优化思路:

对于T2和T3阶段:原有的增量编译方式在代码文件被修改后,会进行语法树分析,例如分析出由于A.java文件变化了,会继续分析A.java文件依赖的类需不需要参与编译,而这个过程是比较耗时的,另外有些场景的分析也是简单粗暴的,例如javac过程中只要修改了一个具有公共(public)常量的java文件,都会导致javac的全量编译,也就是说原有的增量编译为了正确性简单粗暴的处理方式经常会导致javac的全编。本发明实施例中,针对T2和T3阶段,替换掉kotlinc和javac阶段的编译任务,切换为只编译修改过的文件,不进行语法树的分析,可以大幅提高这两个阶段的增量编译速度。

对于T6阶段:原有的编译过程,即使是对一个类进行按类dex处理也耗时很久,原因是d8有一个叫做脱糖(desugar)的编译进程,可以在代码中使用Java8语言功能,并将java8语言功能转换为可以在Android平台上运行的字节码。脱糖时需要传入所有的类文件所在的文件路径,而分析这一系列的文件路径的过程就会很耗时,因此,优化的方式是可以传入少量的文件路径。

对于T4和T5阶段:参考图3,任务间的输入和输出的传递关系是:假如任务A的输出是任务B的输入,也就是说任务B依赖于任务A,如果任务A的输出有修改,就会导致任务B重新执行,如果任务A的输出没有修改,任务B也就不会重复执行。由于T2和T3的输出是T4和T5的输入,如果不修改T2和T3的输出,将T2和T3变化部分生成的产物放到单独的目录下,T4和T5就感知不到输入有变化,也就不会重复执行了。这样做的影响就是T6也感知不到T2和T3是否有变化,而本发明实施例中计划将T6替换掉,因此,可以把修改的目录通知给被替换后的T6,这样它就能感知到了,也就能实现跳过T4和T5,直接执行T6了。

经过上述分析,得出了具体的优化方向,下面对具体的实现细节进行进一步介绍。图5为本发明实施例提供的另一种编译处理方法的流程示意图,如图5所示,该方法可包括:

步骤501、在安卓对应的预设项目编译工具对目标项目进行增量编译的过程中,阻止预设项目编译工具中的第一目标编译子任务的执行,并获取第一目标编译子任务对应的第一待编译源码文件。

其中,预设项目编译工具为AGP,第一待编译源码文件采用Kotlin语言编写,具体为*.kt文件,第一目标编译子任务为将应用层对应的第一待编译源码文件编译成类文件,也即将修改过的kt文件编译成对应的class文件。

示例性的,可以利用增量编译加速插件实现本发明实施例的编译处理方法。可以在项目的根目录的build.gradle文件(安卓的项目配置文件)中添加类路径(classpath),该类路径用于指向增量编译加速插件的源码地址,然后在App模块的build.gradle文件中添加应用增量编译加速插件以及配置增量编译加速插件的相关代码,表示可以在App模块处调用或者开启增量编译加速插件,另外,还可以在项目的根目录中设置增量编译加速插件的开关,当开关状态为开时,增量编译加速插件就可以运行,并执行本发明实施例中的相关步骤。

示例性的,在增量编译加速插件中自定义一个任务来收集应用层的kt文件的最近修改时间,用于确定参与编译的修改过的kt文件,也即第一待编译源码文件。

示例性的,可以利用钩子技术来实现阻止并替换预设项目编译工具中的第一目标编译子任务的执行。

步骤502、下载第一目标编译子任务对应的执行环境文件,解压所述执行环境文件,得到用于将所述第一待编译源码文件编译成相应的类文件的第一命令行。

示例性的,可以在增量编译加速插件中添加kotlinc执行环境文件的下载地址,在检测到当前不支持kotlinc执行环境时,根据该下载地址下载kotlinc的执行环境文件,如kotlin-compiler-${kt_version}.zip文件,解压后得到kotlinc可执行文件。

步骤503、配置第一目标编译子任务对应的输出目录为预设输出目录、配置依赖路径以及配置虚拟机的堆大小为预设数值,在相应的执行环境中利用对应的命令行将第一待编译源码文件编译成对应的类文件,并输出至预设输出目录。

示例性的,利用-d命令配置第一待编译源码文件经过编译后得到的类文件的输出目录为预设输出目录;利用-classpath命令配置编译处理的依赖路径,也即指定用来搜寻类文件的目录或者压缩包列表,可以指定多个目录,多个目录之间用分隔符来分开,这个分隔符在不同的操作系统上一般不一样,可以根据实际情况设置。这里的依赖路径的获取方式可以通过扫描或解析预设目录的方式来获取,具体可根据实际情况设置。此外,当需要进行解析的kt文件较多时,可能会出现内存溢出异常,这时可以通过JAVA_OPTS来将JVM(java虚拟机)的堆的大小设置为预设数值。

示例性的,可以利用解压后得到的kotlinc/bin目录下的可执行文件来编译,不同的操作系统对应的命令行可能不同,例如窗口(windows)系统上采用kotlinc.bat,苹果(Mac)系统和Linux系统上采用kotlinc。

此外,还可考虑对kotlinx的支持,kotlin安卓扩展插件中的kotlinx可以在kt文件中直接访问视图(view),若用命令行kotlinc编译含有kotlinx语法的kt文件时,可能会出现编译错误提示,提示找不多对应的视图,可以利用kotlinc的插入(plugin)机制调用相应的扩展插件来实现对含有kotlinx语法的kt文件的编译。

上述步骤501至步骤502可以视为对T2阶段的优化,经验证,经过优化后,T2阶段的增量编译时间可以从30~45s(秒)左右降低到1~2s左右。

步骤504、阻止预设项目编译工具中的第二目标编译子任务的执行,查询第二目标编译子任务对应的源码文件的当前的最近修改时间,将当前的最近修改时间晚于上一次编译对应的最近修改时间的源码文件确定为候选源码文件,从候选源码文件中滤除数据绑定功能对应的文件目录中的源码文件,得到第二目标编译子任务对应的第二待编译源码文件。

其中,第二待编译源码文件采用Java编写,具体为java文件,第二目标编译子任务为将应用层对应的第二待编译源码文件编译成类文件。

同样的,可以在增量编译加速插件中自定义一个任务来收集应用层的java文件的最近修改时间,用于确定参与编译的修改过的java文件,也即第二待编译源码文件。

步骤505、配置第二目标编译子任务对应的输出目录为预设输出目录、配置依赖路径,在相应的执行环境中利用对应的命令行将第二待编译源码文件编译成对应的类文件,并输出至预设输出目录。

示例性的,第二目标编译子任务的执行环境为JAVA_HOME环境,可以执行Javac命令来将java文件编译为class文件。

上述步骤504至步骤505可以视为对T3阶段的优化,经验证,经过优化后,T3阶段的增量编译时间可以从30~60s左右降低到1~2s左右。

步骤506、阻止预设项目编译工具中的第二目标编译任务的执行,并从预设输出目录中获取第二目标编译任务对应的待编译类文件。

在增量编译流程中,d8的编译操作分为两个阶段,第一个阶段是执行按类dexing的过程,即将class文件编译成中间的dex文件,第二个阶段是将中间的dex文件合并为dex文件。本发明实施例中,第二目标编译任务对应于上面的第一个阶段。本步骤中,从预设输出目录中获取T2和T3输出的发生变化的class文件,其中,若预设输出目录中的文件在历次编译过程中不会被清理,则本步骤中可以获取预设输出目录中的class文件的最近修改时间,将该最近修改时间与本次编译开始时的时间进行比对,进而确定本次编译中T2和T3输出的发生变化的class文件作为待编译类文件。

步骤507、从目标项目对应的软件开发工具包中获取预设编译工具对应的用于将待编译类文件编译成相应的中间可执行文件的第二命令行,获取第二命令行对应的配置信息,并基于配置信息利用所述第二命令行将待编译类文件编译成相应的中间可执行文件。

示例性的,d8可执行程序位于Android sdk里,可以识别出当前需要的构建工具(buildtool)的版本并进行获取,进而得到包含第二命令行的可执行文件或第二命令行。另外,在得到第二命令行后,还需要进行相应的配置,使得其可以完成与原编译任务相应的功能,例如按类dexing等。

可选的,原来的增量编译流程中,第二目标编译任务中可能会遇到java8的一些特殊语法,如在脱糖操作中涉及的java8的接口的默认(default)关键字和接口里的静态方法(static interface method),在编译接口的基础上还需要扫描并分析接口的实现类,也非常耗时,可以略过这些特殊语法的编译,以节省增量编译时间。

步骤508、将所编译的中间可执行文件输出至预设项目编译工具中的第二目标编译任务的输出目录。

示例性的,第二目标编译任务的输出目录作为第三目标编译任务的输入,由于Gradle编译流程中,第三目标编译任务若采用替换的方式实现需要配置的信息很多,工作量大,尤其是项目较大时,更加复杂,因此,可以由预设项目编译工具自身完成。但经过上述针对第一目标编译任务和第二目标编译任务的改动后,经测试发现会出现编译错误,经过分析后发现是摘要校验(checksum)功能导致的,经过上述改动,会导致摘要校验结果失败,因此,可以将该功能的开关关闭。例如,将builder.setIncludeClassesChecksum设置为false。示例性的,可以复制摘要校验功能开关设置的源码文件,将开关状态设置为false,得到修改后的摘要校验功能开关设置的原始源码文件,记为目标源码文件,将目标源码文件的类路径配置在AGP的类路径之前,以实现将AGP中的原始源码文件替换为目标源码文件。

上述步骤507至步骤508可以视为对T6阶段的优化,经验证,经过优化后,T6阶段的增量编译时间可以从10~20s左右降低到1~2s左右。

本发明实施例提供的编译处理方法,在增量编译过程中,替换(Hook,又称劫持)预设项目编译工具中的目标编译任务的执行,目标编译任务包括了App层的kotlinc和javac以及d8编译的第一个阶段,针对发生变化的待编译文件进行编译,省略了语法分析等耗时操作,且不会导致目标编译任务的全量编译,通过将kotlinc和javac的产物输出至单独的目录下,使得动态模块和自定义变形阶段都感知不到输入的变化,不会重复执行,且d8编译的第一个阶段可以直接从该单独的目录中获取需要编译的类文件,可以极大地改善传递依赖导致的编译耗时,有效提高增量编译的效率。

经试验证明,在进行增量编译时,修改某个项目中的一行java代码,现有方案的编译时间为1分56秒,本发明实施例的编译处理方案的编译时间可缩短至24.23秒;修改某个项目中的一行kotlin代码,现有方案的编译时间为1分11秒,本发明实施例的编译处理方案的编译时间可缩短至28.67秒。可见,本发明实施例提供的编译处理方案可以大幅提高增量编译速度,从而提高开发效率。

图6为本发明实施例提供的一种编译处理装置的结构框图,该装置可由软件和/或硬件实现,一般可集成在计算机设备中,可通过执行编译处理方法来进行编译处理。如图6所示,该装置包括:

编译任务阻止模块601,用于在预设项目编译工具对目标项目进行增量编译的过程中,阻止所述预设项目编译工具中的目标编译任务的执行;

待编译文件获取模块602,用于获取所述目标编译任务对应的待编译文件,其中,所述待编译文件包括相比于上一次编译发生变化的第一类型的文件,所述第一类型与所述目标编译任务的输入相对应;

编译处理模块603,用于对所述待编译文件进行编译处理,得到第二类型的编译文件,其中,所述第二类型与所述目标编译任务的输出相对应。

本发明实施例提供的编译处理装置,在预设项目编译工具对目标项目进行增量编译的过程中,阻止预设项目编译工具中的目标编译任务的执行,通过获取目标编译任务对应的待编译文件并对待编译文件进行编译处理的方式对目标编译任务进行替换,其中,待编译文件包括相比于上一次编译发生变化的与目标编译任务的输入相对应的文件,编译后得到与目标编译任务的输出相对应的文件。通过采用上述技术方案,可以针对最近修改过的文件进行编译,减少编译操作,提高增量编译的效率。

本发明实施例提供了一种计算机设备,该计算机设备中可集成本发明实施例提供的编译处理装置。图7为本发明实施例提供的一种计算机设备的结构框图。计算机设备700包括存储器701、处理器702及存储在存储器701上并可在处理器702上运行的计算机程序,所述处理器702执行所述计算机程序时实现本发明实施例提供的编译处理方法。

本发明实施例还提供一种包含计算机可执行指令的存储介质,所述计算机可执行指令在由计算机处理器执行时用于执行本发明实施例提供的编译处理方法。

上述实施例中提供的编译处理装置、设备以及存储介质可执行本发明任意实施例所提供的编译处理方法,具备执行该方法相应的功能模块和有益效果。未在上述实施例中详尽描述的技术细节,可参见本发明任意实施例所提供的编译处理方法。

注意,上述仅为本发明的较佳实施例。本领域技术人员会理解,本发明不限于这里所述的特定实施例,对本领域技术人员来说能够进行各种明显的变化、重新调整和替代而不会脱离本发明的保护范围。因此,虽然通过以上实施例对本发明进行了较为详细的说明,但是本发明不仅仅限于以上实施例,在不脱离本发明构思的情况下,还可以包括更多其他等效实施例,而本发明的范围由权利要求范围决定。

相关技术
  • 一种编译的预处理方法、编译装置及存储介质
  • 编译处理方法、装置、设备及存储介质
技术分类

06120112837525