cezarypiatek / MappingGenerator

:arrows_counterclockwise: "AutoMapper" like, Roslyn based, code fix provider that allows to generate mapping code in design time.
https://marketplace.visualstudio.com/items?itemName=54748ff9-45fc-43c2-8ec5-cf7912bc3b84.mappinggenerator
MIT License
1.03k stars 120 forks source link

Mapping not generated on build with grpc service #110

Closed jorisdebock closed 4 years ago

jorisdebock commented 4 years ago

When using the GrpcService project, the mappings are not generated on build. When using the extension it works as expected

  public virtual HelloReply MapHello(Hello hello)
        {
            return new HelloReply();
        }

I think this is because the Grpc objects are also generated.

The targetType is marked as ErrorType Kind = ErrorType

Example repository https://github.com/wazowsk1/GrpcService1/blob/master/GrpcService1/Services/GreeterService.cs

cezarypiatek commented 4 years ago

Hi, This is a problem with SmartCodeGenerator rather than MappingGenerator plugin. Please take a look at https://github.com/cezarypiatek/SmartCodeGenerator/issues/1 Unfortunately, at the moment there is no solution for that. I'm waiting for a help from guys who work on https://github.com/daveaglick/Buildalyzer

cezarypiatek commented 4 years ago

Duplicates #104

jorisdebock commented 4 years ago

@cezarypiatek but this is not a project reference problem, there are no project references in this example that are used in the generation part.

I have been playing around a bit and I see two things go wrong.

  1. The grpc/proto code generation happens after the smartcodegenerator msbuild step.

when I modify the engine target conditions to

 <Target Name="GenerateFilesWithSmartCodeGenerator" AfterTargets="ProtoCompile" BeforeTargets="CoreCompile" Condition="'$(SmartGeneratorProcessing)' != 'true'">

it will run after the grpc/proto compilation step

  1. The generated code from grpc/proto in the /obj folder is unknown in the msbuild step of the smartcodegenerator and causing no member to be found for mapping.

I am not sure how the roslyn workspace etc work in regard to the /obj folder files. Is this also caused by the issues you mentioned or should I open an issue for this on the smartcodegenerator repository?

cezarypiatek commented 4 years ago

Thanks for the due diligence investigation. I need to verify how roslyn workspace is created and updated with generated code.

Can you try to put proto contracts in a separate project and add it as a reference to the project where you are using MappingGenerator?

jorisdebock commented 4 years ago

I have put the proto contracts in a separate netcoreapp3.1 project

But the result is the same the generated code still only resides in the /obj folder of the separate project. tested with and without the modification of the msbuild step order

var methodSymbol = context.SemanticModel.GetDeclaredSymbol(methodDeclaration);

ReturnType.Kind: ErrorType

In branch: seperate-project-for-protos (this branch is without the msbuild step modification) https://github.com/wazowsk1/GrpcService1/tree/seperate-project-for-protos

jorisdebock commented 4 years ago

I have found the following post on stackoverflow https://stackoverflow.com/questions/42738463/how-to-find-the-obj-directory-for-c-sharp-projects-using-roslyn-msbuildworkspa which pointed me to the following direction.

The /obj intermediate files are not loaded into the project when calling OpenProject on the workspace and there seems to be no option to load these.

So in the Engine, I have added the files manually to the project with the following code.

var files = Directory.GetFiles(options.OutputPath, "*.cs");

            MSBuildLocator.RegisterInstance(selectedMsBuildInstance);
            using (var workspace = MsBuildHelper.CreateMsBuildWorkspace(progressReporter))
            {
                var project = await workspace.OpenProjectAsync(options.ProjectPath, progressReporter);
                foreach (var file in files)
                {
                    var textDocument = project.AddDocument(file, File.ReadAllText(file), filePath: file);
                    project = textDocument.Project;
                }

                var generatorAssemblySearchPaths = options.GetGeneratorPluginsSearchPaths(progressReporter);
                var generator = new CompilationGenerator(generatorAssemblySearchPaths, options.OutputPath, progressReporter);
                await generator.Process(project);
            }

The results are that the type is now known to roslyn and the mapping generator is working as expected. (tested with 1 project including the proto files)

        public virtual HelloReply MapHello(Hello hello)
        {
            return new HelloReply()
            {
                Message = hello.Message
            };
        }

@cezarypiatek what do you think of this possible solution?

cezarypiatek commented 4 years ago

Those files that you are loading manually probably comes from the previous compilation so this won't work after build clean. The presents of the file in the obj directory doesn't mean that it should be included in the compilation, dynamically generated files should be additionally added to the Compile collection of the MSBuild.

jorisdebock commented 4 years ago

It should work even after a clean, since the generated files are generated before the smartcodegenerator target.

The generated files are indeed added to the Compile collection of msbuild, but this collection is not known to MSBuildWorkspace. Instead of doing a GetFiles() passing the Compile collection to the smartcodegenerator is also possible.

cezarypiatek commented 4 years ago

Sorry for long response time and no specific action so far but right now I'm in the middle of moving into the new apartment and it will take the next two weeks.

Back to the problem, I will try to verify once again why those files are not included in the compilation and if there is no option for that in MsBuildWorkspace then I will expose some option to enable including additional files.

jorisdebock commented 4 years ago

no problem, look at it when you have time.

In my fork of the SmartCodeEngine you can see my modifications which are so far working for me. I am not sure if this is the best solution, but the only one I found so far. It's not yet tested with multiple projects where the proto contracts are in another project than the mapping code. https://github.com/wazowsk1/SmartCodeGenerator/tree/add-msbuild-compile-paths-to-msbuildworkspace-documents

cezarypiatek commented 4 years ago

A new version of SmartCodeGenerator.Engine has been released. Please update the package and verify if it works correctly.

jorisdebock commented 4 years ago
        public virtual HelloReply MapHello(Hello hello)
        {
            return new HelloReply()
            {
                Message = hello.Message
            };
        }

yes works :)