.netmsbuild

How do I late-resolve * in .csproj files?


I have a .csproj file that looks like this:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <LangVersion>12.0</LangVersion>
    <DebugType>portable</DebugType>
  </PropertyGroup>

  <ItemGroup>
    <None Remove="*.dat" />
    <None Include="*.dat" CopyToOutputDirectory="Always" />
  </ItemGroup>

  <Target Name="PrecompileScript" BeforeTargets="BeforeCompile">
    <Exec Command="dotnet run -c $(Configuration) --no-build --project ../DatGenerator/DatGenerator.csproj -directory $(ProjectDir)" />
  </Target>
</Project>

The problem is *.dat is expanded too soon and doesn't actually pick up any files. How do I expand *.dat after the <Exec directive that emits the *.dat runs? No, -directory $(ProjectDir)/bin/$(Configuration)/$(TargetFramework) isn't right. That doesn't work at all; see how there isn't a <OutputType>exe</OutputType>. The copy build outputs built-in functionality needs to work.

Listing each .dat file manually is pretty bad. I need to pick up changes automatically here.


Long discussion in comments; however my previously accepted answer is now observed to be not working in comments. Issue filed on github: https://github.com/dotnet/msbuild/issues/10795


Solution

  • To late-resolve a wildcard, move the item Include inside a target.

    e.g. Change your code to

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <LangVersion>12.0</LangVersion>
        <DebugType>portable</DebugType>
      </PropertyGroup>
    
      <Target Name="PrecompileScript" BeforeTargets="BeforeBuild">
        <Exec Command="dotnet run -c $(Configuration) --no-build --project ../DatGenerator/DatGenerator.csproj -directory $(ProjectDir)" />
        <ItemGroup>
          <None Remove="*.dat" />
          <None Include="*.dat" CopyToOutputDirectory="PreserveNewest" />
        </ItemGroup>
      </Target>
    </Project>
    

    "How MSBuild builds projects" explains the evaluation and execution phases but, briefly:

    Note I updated the answer to change from using BeforeCompile to using BeforeBuild.