如何在Linux + Emacs下进行MFC编程(代码补齐)

最早在 水木的这个贴子 里说了这件事。回头想想,自己是不是真的更适合去搞行为艺术呢?头脑正常一点的程序员应该不会这么干的😄。

既然做了,就说说怎么做的吧,说不定有后来人也想这么干…

首先,先讲点背景。这个补齐要用到clang,也就是苹果公司在召集开发的一个 比gcc更新的一个开源编译器,与llvm相结合,据说很大的一个特点是代码组织 得比gcc更好一些…

它有一个自带的补齐功能( -code-completion-at 的命令行选项)!这个补 齐功能不需要整个C源文件能正确编译才可以正确补齐,因为否则的话就成了鸡 肋了。所以用它来做Linux+Emacs下的MFC编程补齐是完全可行的,我只需要把 MFC相关的一些头文件给弄到Linux底下来。

然后就是如水木上的那个贴子里所说的,不停地修正了clang补齐时报出来的一 些错误。这些错误都会放在一个临时的(*xx*这样命名的)buffer里,一条一条 的修正就好了,最后也没必要全部修完,因为有些东西可能是微软自己的编译器 特有的对C++语言的扩充,clang是处理不了的。

有几个很容易修的错误,形成的原因都是一个:微软的文件名不区分大小写。然 后就经常可以看到 StdAfx.hstdafx.h 这样的写法到处都是,貌似是随 机的😂 对这个错误的话幸好我以前有积累,写过一个 rename-refactory 的脚 本,很快就全部改完了。

哦,我刚才checkout出来我的改动看了一下,我的习惯真是太好了,把自己以前 怎么做的全部写成了一个脚本,然后现在写文章的时候看一下脚本就全想起来了:

#!/bin/bash
set -e
# ./fix-compile.h '(cd ~/src/tools/SmartisanTestFrame/branch_DailyDev/TestFrame; cat /home/bhj/src/tools/SmartisanTestFrame/branch_DailyDev/TestFrame/TestFrameDlg.cpp | /usr/bin/clang -cc1 -fsyntax-only -x c++ -I/home/bhj/.cache/vc/include -I/home/bhj/.cache/vc/atlmfc/include -I/home/bhj/.cache/vc/sdk/include -I/home/bhj/src/tools/SmartisanTestFrame/branch_DailyDev/Public/Smartisan/ -I/usr/include/x86_64-linux-gnu/qt5/QtConcurrent -I/usr/include/x86_64-linux-gnu/qt5/QtCore -I/usr/include/x86_64-linux-gnu/qt5/QtDBus -I/usr/include/x86_64-linux-gnu/qt5/QtGui -I/usr/include/x86_64-linux-gnu/qt5/QtNetwork -I/usr/include/x86_64-linux-gnu/qt5/QtOpenGL -I/usr/include/x86_64-linux-gnu/qt5/QtOpenGLExtensions -I/usr/include/x86_64-linux-gnu/qt5/QtPlatformSupport -I/usr/include/x86_64-linux-gnu/qt5/QtPrintSupport -I/usr/include/x86_64-linux-gnu/qt5/QtSql -I/usr/include/x86_64-linux-gnu/qt5/QtTest -I/usr/include/x86_64-linux-gnu/qt5/QtWidgets -I/usr/include/x86_64-linux-gnu/qt5/QtXml -I/usr/include/x86_64-linux-gnu/qt5 -I/usr/include/c++/4.9 -I/usr/include/x86_64-linux-gnu/c++/4.9 -I/usr/include/c++/4.9/backward -I/usr/lib/gcc/x86_64-linux-gnu/4.9/include -I/usr/local/include -I/usr/lib/gcc/x86_64-linux-gnu/4.9/include-fixed -I/usr/include/x86_64-linux-gnu -I/usr/include -I. -code-completion-at -:711:61 -)'

while true; do
    x=$(bash -c "$@" 2>&1 | tr -d '\r' | grep "fatal error:.*' file not found") || true
    if test "$x"; then
        x=$(echo "$x" | perl -npe "s/.*fatal error: '(.*)' file not found.*/\$1/")
        for y in $x; do ./fix-it.sh "$y"; done
    else
        break
    fi
done

最前面你甚至可以看到我记录了一下这个脚本是怎么用的(因为.bash_history 并不能无限地记录你用过的所有命令,而是有一个可以自己设的上限,但如果你 设得超级大的话会导致你的bash交互启动变慢😊)。这是我的又一个好习惯,防止 自己以后忘了怎么用,包括写博客文章时)。这个脚本基本上就是我不停地在用 clang去补齐,然后把 file not found 的错误全部揪出来,用一个 fix-it.sh 做一个简单粗暴的修正。

fix-it.sh 脚本是这样的:

#!/bin/bash
for x in $(find . -iname $1); do
    ln -s "$(basename "$x")" "$(dirname "$x")"/$1 -v
    exit 0
done
exit -1

我找到一个( -iname 参数,不区分大小写) $1 指定的文件,就用 ln 把这个找到的文件 $x 做一个到目标文件 $1 的软链接。你体会一下😄

最后,把这类文件名不区分大小写造成的错误全修正完之后,就是一个 fix-it.h 文件,里面的内容是这样(再次,简单粗暴)的:

#define __int64 long long
#define __int32 int
#define __int16 short
#define __int8 char
#define __w64
#define __possibly_notnullterminated
#define SORTPP_PASS
#define _M_IX86
#define _WCHAR_T_DEFINED
#define _WIN32
#define __uuidof(a) (*(CLSID*)((a*)0))
#define _inline
#define __thiscall
#define _CRTNOALIAS
#define _CRTRESTRICT
#define DECLSPEC_IMPORT
#define _NTSYSTEM_
#define AFXAPI
#define _DLL
#define _CRTIMP2
#define _CRTIMP
#define _MRTIMP2
#define __stdcall
#define __interface class
#define __pragma(...)
#define __fastcall
#define __forceinline
class type_info;

最后,就是clang和gcc都支持的一个 -include 参数,把这个 fix-it.h 文 件每次都include进来再做补齐,就不会有那么多错误了。这些设置最后会写到 Emacs 的 .dir-locals.el 文件里,知道的就不用多说了😄。

以上行为艺术完成于我在锤子科技工作时,感谢。