idea调用ecj方法
因为工作项目中同事用的编译器是eclipse,想移植用idea,发现项目中很多文件包含有UTF8格式的BOM字符,如果更改文件格式也可以解决,想到idea调用外部ecj.jar来进行编译,如果能在编译读取文件时判断一下读取的字符,去掉包含的特殊bom字符,就能从根源上解决这个问题了,参照网上之前的修改ecj.jar里Utils类里的一个读取方法,试了一下没有效果,用旧版17年的idea是可以使用的,可能是idea后面更改了调用的方式,2019.3版本听说性能有很大提升,所以决定查找一下idea是如何调用ecj的方法来进行编译的。
-
查看github上,有idea的社区版源码,先clone下来;
-
有4个多G的文件,克隆下来之后,导入idea,直接open根目录;堆内存加到1.5G,索引20分钟超时,生成了部分索引可以用来查找
-
有很多报错,不过没事,目的不是启动它,主要找编译时调用ecj的方法
-
查找关键字终于在JavaBuilder找到了构建项目的方法
public ExitCode build(@NotNull CompileContext context, @NotNull ModuleChunk chunk, @NotNull DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget> dirtyFilesHolder, @NotNull OutputConsumer outputConsumer) throws ProjectBuildException, IOException { JavaCompilingTool compilingTool = COMPILING_TOOL.get(context); if (!IS_ENABLED.get(context, Boolean.TRUE) || compilingTool == null) { return ExitCode.NOTHING_DONE; } // 用于构建项目的方法 return doBuild(context, chunk, dirtyFilesHolder, outputConsumer, compilingTool); }
-
在doBuild方法里,从变量里的Map获得编译所需要的编译器(ecj使用的是JavaCompilingTool的子类EclipseCompilerTool),还有对java版本的校验,是否使用javac进行编译;
搭建ecj的运行环境
- 去eclipse官网找到对应的ecj版本源码,这里用的是ecj-4.10.jar;需要同时下载这个版本的sdk,org.eclipse.jdt-4.10
2. 使用idea打开,下载需要依赖的jar然后添加进去,这里需要下载ant的jar包;
-
这个版本的ecj,使用了一些java11的api,使用java8无法编译,切换为java11;
-
在代码里找有没有可执行的Main方法,看项目还有没有错误,执行,控制台输出
Eclipse Compiler for Java(TM) bundle_qualifier, bundle_version Copyright IBM Corp 2000, 2015. All rights reserved. Usage: <options> <source files | directories> If directories are specified, then their source contents are compiled. Possible options are listed below. Options enabled by default are prefixed with '+'. Classpath options: -cp -classpath <directories and ZIP archives separated by ;> specify location for application classes and sources. Each directory or file can specify access rules for types between '[' and ']' (e.g. [-X] to forbid access to type X, [~X] to discourage access to type X, [+p/X;-p/*] to forbid access to all types in package p but allow access to p/X) .... 省略掉,意思就是调用这个方法,需要跟的一些args的解释 VMOption跟java文件夹路径,会对文件夹里的java文件进行编译
-
查看org.eclipse.jdt.internal.compiler.tool.EclipseCompiler#getTask方法,如何调用,然后就能debug,查看调用的方法了
-
调用call之后,实际上是去调用EclipseCompilerImpl的call方法了
-
最后发现,getTask里的代码,读取java文件确实用到了Util#getInputStreamAsCharArray,对Util的两个读取文件方法都做了bom移除处理。
-
执行ecj的Main方法,args跟一个java文件夹的路径,会对文件夹的java文件进行编译,自己创建一个bom文件,发现可以处理,重新打包,然后idea配置compile为eclipse,关联重新打包的ecj-4.10.jar,还是不能处理,GG。
卡了好一阵子,还是想用新版的,应该是调用了其他方法,重新换一个地方找调用的方法,先导入默认的ecj包,不处理,查看它打印错误日志的方法,
debug找到另一个读取内容的地方(org/eclipse/jdt/internal/compiler/parser/Parser.java),在前面处理一下bom字符
重新编译,替换文件,在2019.3.1是可以处理bom的,设置编译方式为eclipse,关联ecj的jar包,不要用默认的(替换掉默认的也可以)
最后一个问题
如果整体项目使用UTF-8编译(setting->encoding里设置),有一些GBK里的字符无法识别,
如果整体项目使用GBK编译,报”诺缝ublic“,debug发现是文件是UTF-8 bom类型的文件,当做GBK文件来处理,直接按char读的,所以就读成了这个不识别的字,在parse.java里面,再处理一下这两个字符设置为空字符和p字符就好了;
Util.java里的读取字节流默认是处理了bom字符的,但是由于项目设置,这个encoding到这里的时候可能是个空选项,反正就是最后把所有可能遇到的问题都处理一下,项目文件分支比较多,编码杂乱,别人用eclipse是可以正常运行的,原则上就不改变文件默认的东西,往已有的上面适配就好了
// Do not keep first character for UTF-8 BOM encoding
int start = 0;
if (totalRead > 0 && UTF_8.equals(encoding)) {
if (contents[0] == 0xFEFF) { // if BOM char then skip
totalRead--;
start = 1;
}
}
ecj.jar资源地址://download.csdn.net/download/qq_23747281/12077645