如何将makefile转换为JSON编译数据库



假设我们的项目有make文件(而不是cmake/premake/ninja等),可以为gcc和clang工作。我们想从中生成JSON编译数据库,将其输入到clang-modernize工具中。怎么做这样的事?(clang基础设施中是否有解析器,或者是否有类似make CC='cc_args.py gcc' CXX='cc_args.py g++'或其他工具的脚本)?

我对它没有任何个人经验,但Bear似乎是你的场景的目标。(这是从叮当声现代化网站链接而来的。)

Make有一个通常不需要的功能,即将编译器命令行发送到其标准输出,但在这种情况下,您可以将其与shell(和jq)管道一起使用。使用GNU Make on Bash:

make --always-make --dry-run 
| grep -wE 'gcc|g++' 
| grep -w '-c' 
| jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$").string[1:]}]' 
> compile_commands.json

在Windows上,cmd.exe语法类似:

  • 使用^而不是进行行延续
  • 仅使用"分隔的字符串
  • 使用"""对以"分隔的字符串中的"进行转义
  • 使用^^"分隔的字符串中的^进行转义

但是,这不能处理makefile使用-C参数在子目录中递归调用自己(或其他makefile)的情况。

也许有人也和我一样。我有一个macOS,Bear正在生成一个空的compile_commands.json文件。他们在自述文件中承认了这个问题,并建议使用扫描构建作为解决方法。不幸的是,我仍然得到一个空文件。我发现的两种替代解决方案是:

  • compiledb,它在我的案例中起作用
  • clang++编译器的-MJ选项加上sed的组合,在这里详细介绍,它看起来足够简单,很容易转换为make目标。这个我没有测试过

如果您没有安装Bear/compiledb的权限,或者您不能从源代码构建Bear,那么Web应用程序可能会有所帮助。

我在https://texttoolkit.com/compilation-database-generator,其从接通输出生成compile_commands.json

由于我还没有足够的分数来对答案做出反应,我想在@Tanz87的答案下发表评论,他建议试运行make,并使用grepjq来格式化输出。

我的CMake检测到/usr/bin/c++是默认编译器(我在WSL2,Ubuntu 21.10上),因此grep -wE 'gcc|g++'命令不起作用,就像@Hi-Anger一样,我的compile_commands.json最终使用了[]

修复方法是还对c++进行grep,因此整个命令变为:

make --always-make --dry-run 
| grep -wE 'gcc|g++|c++' 
| grep -w '-c' 
| jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$").string[1:]}]' 
> compile_commands.json

编辑:也就是说,虽然解决方案似乎确实生成了一个compile_commands.json文件,但在构建目录中运行clang-tidy会从clang后端为编译数据库中的每个文件打印一个错误:

error: no such file or directory: '&&' [clang-diagnostic-error]

compile_commands.json文件中的每个command条目如下所示:

"cd /build/src && /usr/bin/c++  -I/src/src -std=gnu++2a -o CMakeFiles/project.dir/subdir/subsubdir/sourcefile.cpp.o -c /project/src/subdir/subsubdir/sourcefile.cpp

此项目使用CMake生成生成生成系统文件。不幸的是,目前还没有解决方案。

编辑:编辑:从compile_commands.json中的每个command中删除cd /build/src &&可以修复该特定错误。使用sed:

sed 's|cd.*.&&||g' compile_commands.json

编辑:编辑:作为一个oneliner,它需要&&标记的一些额外斜杠:

make --always-make --dry-run
| grep -wE 'gcc|g++|c++'
| grep -w '-c'
| sed 's|cd.*.&&||g'
| jq -nR '[inputs|{directory:".", command:., file: match(" [^ ]+$").string[1:]}]' 
> compile_commands.json

我把这个编译器包装器放在一起,您可能需要处理提取文件名参数的if子句。它只检测.c文件

我这样调用它:make ... CC="cc_wrapper $CC"

或:make ... CC="cc_wrapper gcc"

#! /bin/bash
# Writing to a common output file, so buffer for atomic output
if file=($(printf "%sn" "$@" | grep '.c$'))
then JSON="$(jq -nR '[inputs|{directory:$pwd, arguments:[$ARGS.positional[]], file:.}]' --arg pwd "$PWD" --args -- "$@" <<< "${file}")"
# c2rust transpile "$file.compile_commands.json" will still look for a file called compile_commands.json so we have to provide it
mkdir -p "$file.c2rs"
echo "$JSON" > "$file.c2rs/compile_commands.json"
fi
exec "$@"

我发现,对于一些make项目,compiledb能够拦截make输出并生成有效的compile_commands.json文件。但是,对于某些项目,它会生成一个空的compile_commands.json文件。对我来说总是有效的解决方案如下。

make clean;make VERBOSE=y all &> make_output.txt
compiledb --parse make_output.txt

这始终会生成有效的compile_commands.json文件,即使对于compiledb无法直接截取make输出的make项目也是如此
希望这能有所帮助。

最新更新