Correct way to use MSBuild Task that generates code with Intellisense

Viewed 1439

Context

For some project, we've been attempting to do code generation directly from Visual Studio. The basic idea is that you can create a bunch of (.foo) files, and that each (.foo) file corresponds to a single generated (.foo.cs) file.

Basically the process works as follows:

  1. During build, all .foo files are collected.
  2. Some transformations take place. Note that some of the transformations require information from all the .foo files.
  3. For each .foo file, a .foo.cs file is emitted with a partial class.

Normally I'd create a single file generator (custom tool) for this - however, in this case I'd like to know that the filename is always .foo.cs, and there doesn't seem to be a way this can be controlled from a single file generator. Also, the fact that building requires information from multiple files made me opt for an alternative approach.

Therefore, I created an MS Build Task, and registered it in the csproj:

<UsingTask AssemblyFile="C:\Projects\FooTool\bin\Debug\FooBuildTool.dll"
           TaskName="FooBuildTool.FooTool" />
<Target Name="Foo" BeforeTargets="BeforeBuild" 
        Condition="'$(MSBuildProjectExtension)' == '.csproj'">
    <FooTool Language="C#" ProjectFolder="$(ProjectDir)" 
             ProjectName="$(ProjectName)" Sources="@(Foo)">
        <Output ItemName="FooCompile" TaskParameter="ComputedSources" />
    </FooTool>
    <ItemGroup>
        <Compile Include="@(FooCompile->'%(Outputs)')" />
    </ItemGroup>
</Target>

This works fine in the sense that code generation works and this generates all the required .cs files.

Note that if Intellisense doesn't work until I click 'build' - that's fine.

Situation

The problem now is that Intellisense is broken for the .foo.cs files, and I'm looking for a way to fix this.

I've attempted to fix this by changing the .csproj file even more:

<Compile Include="Test.foo.cs">
  <AutoGen>True</AutoGen>
  <DesignTime>True</DesignTime>
  <DependentUpon>Test.foo</DependentUpon>
</Compile>

...

<Foo Include="Test.foo" Generator="Foo" LastGenOutput="Test.foo.cs" />

This appears to work, in the sense that files now show up in the solution explorer of Visual Studio.

However, I don't know how to create this automatically, and couldn't find any documentation nor an example about it. I'm guessing it has to do with building a proper VSTemplate?

Question

What's the right way to fix this issue? E.g. how can I ensure that generated files show up in Visual Studio, so that Intellisense can pick them up?

1 Answers

I had similar problem: I have task that generates some .cs file based on a template. The project is targeting dotnet core 3.1

First I wrote

<Target Name="GenerateEnvFiles" BeforeTargets="BeforeBuild;BeforeClean">

The build worked, but intellisense did not.

I was checking different sources and found an article on msdn where they say:

Design-time IntelliSense To get IntelliSense support in Visual Studio before a build has generated an output assembly, the following conditions must be met:

There must be a target named Compile.

Although setting BeforeTargets=...;Compile didn't work, setting CoreCompile instead worked for me and now both build and intellisense work fine.

I assume the intellisense analyzer has its own workflow where other targets are used other than BeforeBuild. But I didn't find any details about which exactly targets it uses.(For example from the msdn article above I would think BeforeTargets="Compile" should work, while it doesn't)

Related