msbuild
msbuild copied to clipboard
Build fails when referenced project contains generated code
Issue Description
When building a project that contains reference to another project which includes dynamically generated code, the build fails.
Steps to Reproduce
GeneratedCodeBuildFail.zip Command: msbuild GeneratedCodeBuildFail
We are using a tool that generates c# classes from swagger json. I've replaced this task with simply copy task inside Generator.csproj as it is much simpler and seems to have the same effect. Please, manually delete 'Generated.cs' file from the Generator project before every build attempt as second build will succeed. This is not much of an issue when during local development, but it becomes much bigger issue on our build servers as build definitions needs to be run twice for the build to succeed.
Expected Behavior
The build finishes without errors on first try.
Actual Behavior
Solution needs to be built twice for the build to actually succeed.
Versions & Configurations
MSBuild 16.6.0.22303 Microsoft Visual Studio Professional 2019 Version 16.6.0 on Windows 10 64bit
It looks like you are generating .cs files but not including them as Compile
items during the build run. Then only a second build will pick it up when evaluating the default glob patterns.
You can adapt your target like this to fix this:
<Target Name="GenerateSources" BeforeTargets="BeforeBuild">
<Copy SourceFiles="@(FileName)" DestinationFiles="@(FileName->'Generated.cs')">
<Output TaskParameter="DestinationFiles" ItemName="GeneratedCodeFiles" />
</Copy>
<ItemGroup>
<Compile Include="@(GeneratedCodeFiles)" Exclude="@(Compile)" />
</ItemGroup>
</Target>
I was under the impression that everything inside the project is auto-included when using SDK style project. That isn't true?
@horato so yes and no: The auto-import works during the so-called 'static evaluation' - so a project is loaded and all the stuff that's not inside a <Target>
element is processed.
This include any top-level PropertyGroup
and ItemGroup
elements. The SDK also contains such elements for you that do the "auto-import": it's basically a fancy <Compile Include="**/*.cs" />
(source)
So in this step all the glob patterns are evaluated. If a file wasn't on disk when the build started, it will not be picked up.
If any target then creates files on disk it also needs to create appropriate items since static evaluation is already over.
I've done something similar to this solution, and it's mostly working but there is one thing I'm having trouble with .. the intellisense for the generated code in VS says it can't find the types or namespaces of the generated code. IS there a way to make that work too?
Here is the csproj section that I had to setup to get msbuild to generate the code and build it in one go. But it would still be nice to have VS not show this visually as types that can't be found (even tho a compile all builds fine).
Any thoughts on what more to do?
<PropertyGroup>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(ProjectDir)\Impl\**\*.cs" />
<Compile Include="$(ProjectDir)\Interfaces\**\*.cs" />
</ItemGroup>
<Target Name="GenerateProto" BeforeTargets="BeforeBuild">
<Exec Command="call py "$(ProjectDir)..\console-common\_build\csharp_prebuild.py" -out $(ProjectDir)\$(BaseIntermediateOutputPath)">
</Exec>
<ItemGroup>
<Compile Include="$(BaseIntermediateOutputPath)\dir1\*.cs"/>
<Compile Include="$(BaseIntermediateOutputPath)\dir2\Disconnect.g.cs" />
</ItemGroup>
</Target>
In my case I have items included before they have been generated (ItemGroup under Project) (also generating to 'obj' directory), but it randomly does not see them. It looks like msbuild does not pick up the new files (if files added to XsdSchema in my case), but does for old files even after clean/rebuild.
<Project>
<XsdSchema Include="Schemas\*.xsd">
<DesignTime>true</DesignTime>
<Generator>MSBuild:RunGenerationTool</Generator>
<!--<Visible>false</Visible>-->
</XsdSchema>
<Target ... />
<ItemGroup>
<Compile Include="@(XsdSchema-> '$(GeneratedItemsFolderPath)%(Identity).generated.cs')">
<AutoGen>true</AutoGen>
<Visible>false</Visible>
</Compile>
<Compile Include="@(XsdSchema-> '$(GeneratedItemsFolderPath)%(Identity).partial.generated.cs')">
<AutoGen>true</AutoGen>
<Visible>false</Visible>
</Compile>
</ItemGroup>
</Project>
Could it be relative to https://developercommunity.visualstudio.com/t/items-included-as-wildcard-not-refreshed-in-the-pr/213285 ?
Are you writing files to disk via a source generator? If so, my understanding is that Microsoft advises against this and the behaviour is undefined 🙂
It looks like you are generating .cs files but not including them as
Compile
items during the build run. Then only a second build will pick it up when evaluating the default glob patterns.You can adapt your target like this to fix this:
<Target Name="GenerateSources" BeforeTargets="BeforeBuild"> <Copy SourceFiles="@(FileName)" DestinationFiles="@(FileName->'Generated.cs')"> <Output TaskParameter="DestinationFiles" ItemName="GeneratedCodeFiles" /> </Copy> <ItemGroup> <Compile Include="@(GeneratedCodeFiles)" Exclude="@(Compile)" /> </ItemGroup> </Target>
After converting an old project to the SDK Style, I had the same issue, and the solution above worked perfectly. Thanks!
Not a bug then.