dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.37k stars 9.99k forks source link

How to debug a compiler preprocessor? #295

Closed kevinkuszyk closed 7 years ago

kevinkuszyk commented 9 years ago

As suggested by @davidfowl in issue #272:

You can follow the same pattern as the RazorView preprocessor https://github.com/aspnet/Mvc/blob/dev/samples/MvcSample.Web/Compiler/Preprocess/RazorPreCompilation.cs. In the target project, the file needs to go into compiler/preprocess/*.cs.

I now have a preprocessor running in a proof of concept app, and have some questions:

  1. Is it possible to debug a preprocessor? I have a breakpoint in but it's not getting hit.
  2. I've noticed the preprocessor seems to be cached. Changes I make to it don't take effect unless I close and re-open Visual Studio. I assume this is by design for performance reasons. Can this be disabled to make the development experience better, or is there another workaround?
wwwlicious commented 9 years ago

I tried the same thing too, I got the debugger to work by the following

public class MVCRouteCompilation : MVCRouteCompilerModule {
    public MVCRouteCompilation(IServiceProvider provider)
        : base(provider)
    {
    }
}
public class MVCRouteCompilerModule : ICompileModule
{
    private readonly IServiceProvider _appProvider;

    public MVCRouteCompilerModule(IServiceProvider provider)
    {
        Debugger.Launch();
        _appProvider = provider;
    }

    public void BeforeCompile(IBeforeCompileContext context)
    {
        var applicationEnvironment = _appProvider.GetRequiredService<IApplicationEnvironment>();
        var projectResolver = _appProvider.GetRequiredService<IProjectResolver>();

        // 1. How to find all public types inheriting from Controller
        //      change class to partial and add {ControllerName}.Generated.cs nested class
        //      if no default constructor, add one to generated class
        //      CodeAnalysis should run for public classes and methods to trigger above
        // 2. Get all public methods returning types inheriting from actionresult
    }

    public void AfterCompile(IAfterCompileContext context)
    {
    }
}

[Edit] Should add that this can leave your project permanently building but you just have to kill msbuild process. Also I ended up grabbed files like the ICompileModule out of the asp.net vnext repo and copying directly into my project,

Had no luck setting up a vsix project from the Roslyn Templates though as I couldn't reference the library containing the precompilation class as it in turn had references to system.web vnext (in order find the controller types).

Also struggling with finding any decent examples of rosyln codegen to mark controller classes as partial and generate action method stubs ala t4mvc. Plenty of examples of diagnostics and refactoring which could look for action link strings in razor views and suggest refactoring to the stubs but to me that seems like 'nice to have' rather than core functionality.

davidfowl commented 9 years ago

Preprocessors aren't first class in tooling yet so this will be a bit painful for now as it runs in the tooling process, not in the runtime process (when running in visual studio).

wwwlicious commented 9 years ago

so I'm learning.. :)

On point 2 of Kevin's post, is it possible to invalidate whatever caching is going on for the preprocessor between running builds? Getting RSI killing the klr.exe process!

davidfowl commented 9 years ago

Make a change to project.json to recompile the preprocessor itself.

kevinkuszyk commented 9 years ago

Thanks both.

@davidfowl - can you advise what change to put in the project.json?

@wwwlicious - I am trying to build a replacement for T4MVC using Roslyn. It looks like we are trying to solve similar problems. When I have some working code I'll push it here

wwwlicious commented 9 years ago

@kevinkuszyk i"ve commited some code to a repo fork and would be interested in your thoughts on direction. it doesnt do much beyond creating controller partial classes but as ive no exp with roslyn im a bit like a gorilla with a gun at the moment, but you have to start somewhere right?

kevinkuszyk commented 9 years ago

@wwwlicious - it looks like you are taking a similar approach to me. I'm by no means an expert on Roslyn though so I could't comment on whether this is the correct approach.

I have lots of red underlines and namespace collisions at the moment which I will try to fix this afternoon, so I can push an update.

Antaris commented 9 years ago

A possible suggestion for compile modules would be to implement them as abstract class in a reference class library, and add a subclass of it in your compiler/preprocess folder of your target library/project. Doing it this way will enable you to design unit tests using your abstract types, which you can mock or provide a fake, with a Roslyn compilation created to test.

kevinkuszyk commented 9 years ago

Thanks @Antaris, but the problem is that I wanted to hit a breakpoint in my compile module. The issue is because the compile module is run in the framework, Visual Studio doesn't hit the breakpoint.

Antaris commented 9 years ago

@kevinkuszyk You can add a Debugger.Launch() in your compile module and this will be triggered at compile time.