How to modify code before compilation?

前端 未结 3 872
一整个雨季
一整个雨季 2021-01-30 05:22

Using Roslyn, I would like to modify my C# code before the actual compilation. For now, I will just need something like:

[MyAnotatedMethod]
public void MyMethod(         


        
3条回答
  •  春和景丽
    2021-01-30 05:57

    Here is a quick and dirty way of doing what you want. It's based on one of the above comments, which points to SebbyLive. It is just a proof of concept, I wouldn't try to use it in production.

    The basic idea is that you change the compiler of the project that you want to modify. And this changed compiler will do the code injection. So you'd need to write a new compiler (AopCompiler.exe) and set it as the build tool in your project.

    Setting the AopCompiler.exe as the build tool is easy, in you project file, you'd need to add the following two lines:

    $(SolutionDir)AopCompiler\bin\Debug
    AopCompiler.exe
    

    The AopCompiler should be a simple console application. This is doing the code modification and the compilation too. If you don't want to modify the source code, just build it, then the easiest way is to call the csc.exe yourself:

    static void Main(string[] args)
    {
      var p = Process.Start(@"C:\Program Files (x86)\MSBuild\14.0\Bin\csc.exe", 
                string.Join(" ", args));
      p.WaitForExit();
    }
    

    So if you set this up so far you'd have a normal build process, without the aspect weaving.

    At this point, if you check out what is in the args, you'll see that there is a file path to an .RSP file, which contains all the command line parameters for the csc.exe. Naturally, these parameters contain all the the .CS file names too. So you could parse this .RSP file and find all the .CS files, that are part of the compilation.

    With the C# files in hand, the rewriting can be done with Roslyn. There are many tutorials on CSharpSyntaxRewriter, for example here, and here. You'd need to write your custom CSharpSyntaxRewriter, which checks for the given attribute, and then add the logging to the beginning of the found methods. Adding logging to the end of each method is a bit trickier, because there can be multiple exit points. To find those, you can use control flow analysis. The built-in Roslyn control flow analysis can give you exactly what you are after, the ExitPoints property holds the set of statements inside a region that jump to locations outside the region.

    To get the semantic model (and then do the CFG analysis) you can do something like this:

    public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
    {
        var semanticModel = _compilation.GetSemanticModel(node.SyntaxTree);
        // semanticModel.AnalyzeControlFlow(node.Block)
        return node;
    }
    

    Finally, to process each of the input files, your AopCompiler, you just simply have to call your rewriter's Visit method on the root of the tree. That will produce the modified tree, which you can write out to a file. (Either you can modify the original file, or write the result to a new one, and change the .RSP file accordingly.)

    Sorry for not providing a full working solution, but I hope, this is enough to get you started.

提交回复
热议问题