How to change the code before compilation?

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

[MyAnotatedMethod] public void MyMethod() { // method-body } 

And based on the annotation, I would like to introduce some code at the beginning of the method and at the end of the method.

I know PostSharp, but that’s not what I would like.

Can this be done with Roslin? And if so, could you give me an example?

+43
c # roslyn
Oct 24 '15 at 8:21
source share
3 answers

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

The main idea is that you change the compiler of the project you want to change. And this modified compiler will inject the code. Therefore, you will need to write a new compiler (AopCompiler.exe) and install it as a building tool in your project.

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

 <CscToolPath>$(SolutionDir)AopCompiler\bin\Debug</CscToolPath> <CscToolExe>AopCompiler.exe</CscToolExe> 

AopCompiler should be a simple console application. It also does code modification and compilation. If you do not want to change the source code, just create it, then the easiest way is to call 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 install this so far, you will have a normal build process, without testing.

At this point, if you look at what is in args , you will see that there is a path to the .RSP file that contains all the command line options for csc.exe. Naturally, these options contain all .CS file names. Thus, you can parse this .RSP file and find all .CS files that are part of the compilation.

Using C # files, rewriting can be done using Roslyn. CSharpSyntaxRewriter has many tutorials, like here and here . You will need to write your own CSharpSyntaxRewriter , which checks this attribute, and then adds the entry to the beginning of the methods found. Adding a log to the end of each method is a bit more complicated because there can be multiple exit points. To find them, you can use control flow analysis. Roslyn's built-in flow analysis can give you exactly what you need; the ExitPoints property contains a set of operators within a region that go to locations outside the region.

To get a semantic model (and then do 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 need to call the rewriter Visit method in the root of the tree. This will create a modified tree that you can write to the file. (Or you can change the source file, or write the result to a new one, and change the .RSP file accordingly.)

Sorry for the lack of a complete working solution, but I hope this is enough to get you started.

+16
Nov 01 '15 at 10:49
source share

As I noted in my comment, it is currently unavailable. Although you could put something together using the AOP methods shown here with the Roslyn Scripting API to provide a very flexible solution.

The correct answer at this point in time is that the Roslyn team has an open offer / question to support it. Thanks to the .Net Foundation and Microsoft participating in Open Source, you can read about this feature development here:

https://github.com/dotnet/roslyn/issues/5561

+7
Nov 01 '15 at 10:54
source share

It's a bit messier than using annotations, but blocking the code with preprocessor directives will let you customize how your code compiles based on flags.

http://www.codeproject.com/Articles/304175/Preprocessor-Directives-in-Csharp

The code in #ifMyFlag below will compile if MyFlag is defined

 #define MyFlag public void MyMethod() { #if MyFlag // Flag conditional code #endif // method-body } 
+3
Nov 06 '15 at 20:57
source share



All Articles