首先说明一下需求详情:把项目里面所有的源码拷贝一份,然后给所有的会让编译不过的token加一个前缀。
然后不要问我为什么有这个需求…
最后看看都有哪些办法来完成这件事情以及适用情况:
纯手工
纯手工的方式是指人肉去读代码,然后在整个工程搜索需要重命名的关键字,然后统一或者个别地去替换。过程中其实会发现很多蛋疼的地方,因为其实你没有办法统一替换,比如你代码中有包含情况的token:Abc,bc.
适用范围:微型工程.
优雅程度:*
IDE + refactor
很多IDE都提供refactor的功能,相比纯手工智能了不少,能进行不同程度的类型推导,这个得看IDE具体实现方式了,如果工程项目不大,人力也比较充足,这个一般也能在老板能理解的时间范围内完成-_-。
适用范围:微型工程,中型工程
优雅程度:**
脚本 + 正则
如果你对正则表达式比较熟悉,用脚本结合正则表达式也能够比较快速地完成任务。首先你得用正则匹配出你需要进行替换的token,并且生成一个旧的token和新token的map,然后把所有源代码文件copy一份,再用一个正则把所有该token字符串形式前后不能有:字母,下划线,数字的group全部匹配出来(根据大多数程序语言的语法,这样可以规避掉Abc(bc)的问题),最后在之前声称map中查找然后替换掉。这里看起来正则比较简单,但实际情况下得处理各种case还是很蛋疼的,比如注释,比如字符串。
根据我的经验,这里需要替换的token在Objective-C这门语言中,如果不考虑c++超集的情况,那么有下面几种需要匹配到的token:
-
类
- 协议
- oc形式的枚举
- c形式的枚举
- c函数
- 非static全局变量
- block
适用范围:中型工程,大型工程
优雅程度:***
编译器前端+脚本+正则
这里以Objective-C代码为例:
- 在这里http://clang.llvm.org/get_started.html,下载一份clang的源码进行编译
- 利用clang tools中的python bindings来parse所有需要重命名的源码文件,需要一定的脚本开发量
- 根据parse到的类型和token来生成替换的map
- 同前一种方式
这里搜索需要替换token的工作从正则换成了编译器前端,主要会有两个方面的提升,一是减少了很多对于注释,或者字符串或者某些格式的token的特定的正则的匹配写法,对于正则不熟悉的人能省去很多工作量,二是在性能上应该会有提升(未验证,因为不是很重要,除非你是几千万行代码的巨型工程)
走到这一步,有心的工友应该已经意识到了,其实这里已经不仅是一个重命名的工具了,在引入编译器前端以后,可以做很多事情,包括语言翻译器,源码检查等等,我以前用clang+python做过一个oc到lua的翻译器,在大部分情况下都可以工作良好,只需要很少的人工干预,但由于clang本身是用c++开发的,python binding相对来说包含的信息比较少,所以有些东西做起来比较受限制,详细的可以搜索clang的官网查询如何利用插件的方式使用clang。
适用范围:中型工程,大型工程
优雅程度:****
编译器前端+AST+代码生成
如果你能够拿到由编译器前端生成的包含完整信息的抽象语法树(AST),那么理论上你是可以通过这个AST完整地生成你的源代码,那么重命名就非常简单了,你只需要搜索这棵树,替换其中某些节点的名字,再还原成源代码,就可以了。
这种方式只有一个弊端,因为编译器前端生成AST以后,已经没有注释信息了,所以把AST再生成源代码的时候生成的源代码的注释也就全都没有了。
而且大部分情况如果不是做代码翻译器或者编译器(好吧,其实代码翻译和编译器一个意思),前一种方式已经非常够用了,这里继续研究下去水还是很深的。
适用范围:中型工程,大型工程
优雅程度:*****
写在后面
以上优雅度大于3星的方式其实都还有不少死角,虽然能满足编译通过的需求,但在运行的时候会有不少问题,比如所有动态加载的东西几乎都没有办法覆盖到,比如对预编译宏也没有办法处理,当然这个需求一般也不会有人遇到,写出来纯做记录,如果有人遇到相同的需求也可以参考,有其他更好的方式欢迎探讨。