#!/usr/bin/env python3
"""
将 Keil MDK 工程 (.uvprojx) 转换为 clangd 配置文件 (.clangd)。
用法:
python3 keil2clangd.py # 默认搜索当前目录
python3 keil2clangd.py /path/to/project.uvprojx # 指定工程文件
python3 keil2clangd.py -o /path/to/.clangd # 指定输出路径
生成内容:
- target: arm-none-eabi
- std: c11
- 所有 <IncludePath> → -I 路径 (自动过滤不存在目录)
- 所有 <Define> → -D 宏定义
- Keil/ARMCC 兼容宏 (__packed, __weak, __irq, __inline 等)
"""
import re
import os
import sys
import argparse
from pathlib import Path
# Keil/ARMCC 特有关键字 → clang/GCC 兼容映射
ARMCC_COMPAT_DEFINES = {
"__CC_ARM": "",
"__GNUC__": "",
"__weak": "__attribute__((weak))",
"__packed": "__attribute__((packed))",
"__irq": "",
"__inline": "inline",
"__align": "",
"__at": "",
"__ramfunc": "",
}
def parse_uvprojx(uvprojx_path: str) -> tuple[set[str], set[str]]:
"""解析 Keil uvprojx 文件,返回 (includes, defines)"""
with open(uvprojx_path, "r", encoding="utf-8", errors="ignore") as f:
content = f.read()
project_dir = os.path.dirname(os.path.abspath(uvprojx_path))
# 提取 IncludePath
includes = set()
for m in re.finditer(r"<IncludePath>([^<]+)", content):
for p in m.group(1).split(";"):
p = p.strip()
if not p:
continue
abs_p = os.path.normpath(os.path.join(project_dir, p.replace("\\", "/")))
if os.path.isdir(abs_p):
includes.add(abs_p)
# 提取 Define
defines = set()
for m in re.finditer(r"<Define>([^<]+)", content):
for d in m.group(1).split(","):
d = d.strip()
if d:
defines.add(d)
return includes, defines
def generate_clangd(includes: set[str], defines: set[str], output_path: str):
"""生成 .clangd YAML 文件"""
lines = []
lines.append("CompileFlags:")
lines.append(" Add:")
# 编译目标
lines.append(" - -std=c11")
lines.append(" - -fno-ms-compatibility")
# Keil 原始宏定义
for d in sorted(defines):
lines.append(f" - -D{d}")
# ARMCC 兼容宏
for macro, gcc_val in ARMCC_COMPAT_DEFINES.items():
if gcc_val:
lines.append(f" - -D{macro}={gcc_val}")
else:
lines.append(f" - -D{macro}")
# Include 路径
for p in sorted(includes):
lines.append(f" - -I{p}")
content = "\n".join(lines) + "\n"
with open(output_path, "w") as f:
f.write(content)
return content
def find_all_uvprojx(search_dir: str) -> list[str]:
"""搜索目录下所有 .uvprojx 文件"""
results = []
for root, dirs, files in os.walk(search_dir):
for f in files:
if f.endswith(".uvprojx"):
results.append(os.path.join(root, f))
return results
def pick_uvprojx(uvprojx_list: list[str]) -> str | None:
"""多个工程文件时,优先选择文件名含 'app' 的(大小写不敏感)"""
if not uvprojx_list:
return None
if len(uvprojx_list) == 1:
return uvprojx_list[0]
# 优先匹配文件名含 "app" 的
app_matches = [p for p in uvprojx_list
if "app" in os.path.basename(p).lower()]
if app_matches:
return app_matches[0]
# 没有 "app" 匹配则返回第一个
return uvprojx_list[0]
def main():
parser = argparse.ArgumentParser(
description="Keil uvprojx → clangd .clangd 转换器",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
示例:
%(prog)s # 自动搜索 uvprojx,输出到其所在目录的上级
%(prog)s /work/project.uvprojx # 指定 uvprojx
%(prog)s project.uvprojx -o /work/mcu/.clangd # 指定输出
""",
)
parser.add_argument(
"uvprojx", nargs="?", default=None,
help="Keil 工程文件路径,不指定则自动搜索当前目录"
)
parser.add_argument(
"-o", "--output", default=None,
help="输出 .clangd 路径,默认输出到 uvprojx 所在目录向上两级"
)
args = parser.parse_args()
# 查找 uvprojx
uvprojx_path = args.uvprojx
if uvprojx_path is None:
uvprojx_list = find_all_uvprojx(os.getcwd())
if not uvprojx_list:
print("错误: 未找到 .uvprojx 文件,请指定路径", file=sys.stderr)
sys.exit(1)
uvprojx_path = pick_uvprojx(uvprojx_list)
if len(uvprojx_list) > 1:
print(f"找到 {len(uvprojx_list)} 个工程文件,默认选择: {os.path.basename(uvprojx_path)}")
for p in uvprojx_list:
mark = " ← 选中" if p == uvprojx_path else ""
print(f" {p}{mark}")
else:
print(f"自动找到: {uvprojx_path}")
uvprojx_path = os.path.abspath(uvprojx_path)
if not os.path.isfile(uvprojx_path):
print(f"错误: 文件不存在 {uvprojx_path}", file=sys.stderr)
sys.exit(1)
# 确定输出路径: uvprojx 向上两级 (project/mdk/ → mcu/)
if args.output:
output_path = os.path.abspath(args.output)
else:
# project/mdk/uvprojx → 向上两级 → mcu/
output_path = os.path.join(
os.path.dirname(os.path.dirname(os.path.dirname(uvprojx_path))),
".clangd"
)
# 解析
includes, defines = parse_uvprojx(uvprojx_path)
# 生成
generate_clangd(includes, defines, output_path)
print(f"输入: {uvprojx_path}")
print(f"输出: {output_path}")
print(f"Include: {len(includes)} 个路径")
print(f"Define: {len(defines)} 个宏 (+ {len(ARMCC_COMPAT_DEFINES)} 个兼容宏)")
print("完成!")
if __name__ == "__main__":
main()
keil工程 适配VSCODE clangd插件跳转 生成.clangd文件跳转脚本
最新推荐文章于 2026-06-22 23:41:02 发布

1万+

被折叠的 条评论
为什么被折叠?



