简介
Microsoft.CodeAnalysis是一个开源的.NET编译器平台,可以作为一个NuGet软件包安装到项目中,也可以以dll的形式安装。本人想利用Microsoft.CodeAnalysis实现对c#语言在python环境下的解析,主要涉及到了利用Pythonnet库实现Microsoft.CodeAnalysis的调用
问题描述
在一个python项目里,需要用到c#源码的抽象语法树(AST),但是却遇到了不小的麻烦,陆陆续续解决了有好几天,涉及到的环节颇多,特此记录一下
配置步骤:
1、安装.NET SDK:.NET官方安装说明
建议直接使用官方的安装脚本dotnet-install.sh并运行,检查是否安装成功。
sudo chmod +x ./dotnet-install.sh
./dotnet-install.sh --version latest
dotnet --info
2、 安装pythonnet
pip install pythonnet
这里有个坑,由于后续会 import clr
所以我在第一次安装的时候直接pip install clr
但其实这样是不正确的,利用clr.AddReference()
调用dll的时候会报错:AttributeError: module 'clr' has no attribute 'AddReference'
。原因是直接pip安装的clr包同名但功能不同。
3、安装gcc
conda install -c conda-forge gcc
如果太慢可以换源
4、下载Microsoft.CodeAnalysis.dll
不熟悉dotnet工作机制的可能会在这一步困扰很久,当时我没有在dotnet的根目录下找到Microsoft.CodeAnalysis.dll,就想了以下一种比较容易的方式
1.利用dotnet新建一个最简单的项目
dotnet new console -o MyApp -f net6.0
cd sample1
dotnet run
2.在本项目中
dotnet add package Microsoft.CodeAnalysis.CSharp --version 4.5.0
3.进入项目文件夹
cd MyApp/bin/Debug/net6.0/
就可以看到Microsoft.CodeAnalysis.dll和Microsoft.CodeAnalysis.CSharp.dll啦,将其复制到你的python项目路径下,或者添加至sys.path中。
5、在python中利用clr调用dll
需要找一下自己的dotnet根路径和所调用的runtime配置文件路径,一般都如下所示
#待解析源码
source_code = """
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, world!");
}
}
}
"""
from clr_loader import get_coreclr
from pythonnet import set_runtime
rt =get_coreclr(dotnet_root='/usr/share/dotnet/',
runtime_config="/usr/share/dotnet/shared/Microsoft.AspNetCore.App/6.0.14/Microsoft.AspNetCore.App.runtimeconfig.json")
clr.AddReference('Microsoft.CodeAnalysis') #这一步我曾经使用dll绝对路径的方式,可以通过编译,但是在后面的运行过程中还是无法获得Microsoft.CodeAnalysis的命名空间,关键在于将dll放至python路径
clr.AddReference("Microsoft.CodeAnalysis.CSharp")
from Microsoft.CodeAnalysis.CSharp import CSharpSyntaxTree
from Microsoft.CodeAnalysis import CSharp
tree = CSharpSyntaxTree.ParseText(source_code)
顺利实现。后续可以用实现树的遍历操作:
class_definition_nodes = [node for node in tree.GetRoot().DescendantNodes() if isinstance(node, CSharp.Syntax.ClassDeclarationSyntax)]
for class_node in class_definition_nodes:
print(class_node.Identifier.ValueText)
可能c#还有其他的解析方法,比如用tree-sitter等库,欢迎有相似问题的找我讨论