NetBeans IDE有C/C++支持,该模块的名字叫做“CND”(就跟Eclipse的C/C++支持有项目名叫做CDT一样)。它从8.1版本可以用Clang的前端来提供C/C++源码的语法分析支持,可以通过 -J-Dapt.use.clank=true 开启该支持。(注意是clank而不是clang喔,下面马上就要说为啥了)
但是!有趣的是,NetBeans是一个用Java写的IDE,而Clang是用C++写的。如果一个IDE只是在用户点击“编译”按钮时去调用编译器,那么这个交互会很简单:基本上等于让IDE直接执行一个命令行命令去调用编译器即可。但像NetBeans这种带有正确的语法高亮以及一定程度的语法分析、自动补全功能的IDE,在用户编辑代码的过程中就已经需要跟语法分析、语义分析器以及代码的元数据打交道,如果这些交互每次都通过调用外部程序、或者通过JNI来从Java调用C++写的功能的话,效率会颇为低下。所以NetBeans是如何使用Clang的?
用某种方式把Clang的C++源码机械式翻译成了Java源码,然后做一些人工修正/裁剪…得到的结果就是一个跟Clang前端完全对应的Java版代码。这个项目的名字叫做 Clank / JClank。如果大家在NetBeans CND的二进制发布版里看到名字里有"clank"字样的模块,说的就是这个项目了。
由于翻译是尽可能机械完成的,所以Clang升级的话这边的Clank / JClank也可以跟着一起升级——把新版本的Clang重新翻译一遍就好。这就不怕被C++新标准的各种新繁杂功能拖着疲于奔命。
肯定有同学会觉得这很神奇…咋会选用这么奇葩的解决方案?
据说NetBeans CND的开发组是有考虑过很多种解决方案的,其中当然也包括比较常规的通过JNI来调用libClang / libTooling库里的Clang功能,但最后评估下来觉得NetBeans原本的C/C++支持的卖点之一就是parser的解析速度快,如果parser换成用Clang而性能下降得太厉害的话就得不偿失了。所以评估下来反而是把Clang源码翻译为Java,避开JNI开销,这样的方案的总体开销较小。
在换成用Clang之前,NetBeans的C/C++支持其实有一个自己写的parser,是基于魔改过的ANTLR 2.x系生成器来实现的。它为当时NetBeans的定位而优化,通过牺牲少量准确性来大幅提升解析速度——当时NB的定位觉得说IDE的parser不需要像编译器一样准确,反正不是用来生成目标代码。后来NetBeans CND想把C/C++支持做得更完善,原有的parser就有点吃力了,例如说它所牺牲的准确性使得它无法完全准确地报告符号的find references的结果,快是快但是难以在它的基础上同时改进准确度和跟上C++新规范(11/14/17)的功能。所以后来就考虑用Clang来替代原先的parser。
补充:2017年的EuroLLVM上会有关于Clank的演讲:Clank: Java-port of C/C++ compiler frontend
(我还没用过CLion…没有第一手感受)
听说过JetBrains家的CLion的同学或许也听说过,CLion里的C/C++ parser是自己用Java手写的。
嗯,用Java写的。
很多人都吐槽说“Clang都那么成熟了,你们怎么不用Clang?”。但事情当然不是那么简单的咯。
JetBrains的IDE自成一套框架,整个都是用Java写的。某种意义上说跟NetBeans有着相似的处境——如果有功能是需要频繁调用、每次调用数据量都不大(例如说增量式重新parse + 语法高亮更新 + 自动补全提示更新等),通过JNI去调用native组件反而会降低性能。而且自成一套框架的东西常常难以与外部的别的成熟程序整合。结果过了这么一段时间,JetBrains还在咬着牙自己不断去修复/更新/完善那parser…
最近有几位NetBeans CND的开发跳槽去了JetBrains做CLion…于是这俩故事就联系上了。不知道他们会碰撞出怎样的火花呢(吃瓜状
我最近一次听说把C++写的编译器机械式翻译成别的语言然后再手工修整的项目,还得属微软的Roslyn。它把原本微软的C#编译器(所谓csc.exe)从C++机械式翻译成了C#代码,然后再人手一点点把代码风格修整到符合C#习惯的写法。
当然这出发点跟NetBeans的Clank项目完全不一样,毕竟Roslyn在把C#编译转换成C#代码后,这代码就是以后的主干了,得好好维护;“源头”的C++版代码以后就不要了。
而NetBeans Clank的做法则是尽量不要人工干预机械翻译出来的结果,以便在未来“源头”发生了更新后能及时重新翻译。
原文作者:;如有侵权,请联系管理员删除。