Search code examples
.netmsbuildcsproj

MSBuild - csproj - Use combinations to exclude folder from build


In order to allow building our projects in Visual Studio and a Linux DevContainer side-by-side, I changed the build configuration of our project as follows:

Directory.Build.props

<Project>
  <PropertyGroup Condition=" '$(OS.StartsWith(`Windows`))' == 'true' ">
    <MSBUildProjectExtensionsPath>$(MSBuildThisFileDirectory)$(MSBuildProjectName)\obj-windows\</MSBUildProjectExtensionsPath>
    <BaseOutputPath>$(MSBuildThisFileDirectory)$(MSBuildProjectName)\bin-windows\</BaseOutputPath>
    <NoWarn>MSB3539</NoWarn>    <!-- Temporary Suppress warning about BaseIntermediateOutputPath -->
  </PropertyGroup>
  <PropertyGroup Condition=" '$(OS.StartsWith(`Windows`))' != 'true' ">
    <MSBUildProjectExtensionsPath>$(MSBuildThisFileDirectory)$(MSBuildProjectName)/obj-linux/</MSBUildProjectExtensionsPath>
    <BaseOutputPath>$(MSBuildThisFileDirectory)$(MSBuildProjectName)/bin-linux/</BaseOutputPath>
    <NoWarn>MSB3539</NoWarn>    <!-- Temporary Suppress warning about BaseIntermediateOutputPath -->
  </PropertyGroup>
</Project>

This redirects bin files to bin-windows or bin-linux respectively. Same for the obj folder.

The problem with this is that this includes these folders in the .NET compilation

enter image description here

Which causes a recursive tree if you'd do alternating builds on windows/linux

enter image description here

I can solve this by adding the following snippet to my csproj file

<ItemGroup>
    <Compile Remove="bin-linux\**" />
    <Compile Remove="bin-windows\**" />
    <Compile Remove="obj-linux\**" />
    <Compile Remove="obj-windows\**" />
    <Content Remove="bin-linux\**" />
    <Content Remove="bin-windows\**" />
    <Content Remove="obj-linux\**" />
    <Content Remove="obj-windows\**" />
    <EmbeddedResource Remove="bin-linux\**" />
    <EmbeddedResource Remove="bin-windows\**" />
    <EmbeddedResource Remove="obj-linux\**" />
    <EmbeddedResource Remove="obj-windows\**" />
    <None Remove="bin-linux\**" />
    <None Remove="bin-windows\**" />
    <None Remove="obj-linux\**" />
    <None Remove="obj-windows\**" />
</ItemGroup>

However I would like to write it like this instead

<ItemGroup>
    <Compile Remove="**\(bin|obj)-(linux|windows)\**" />
    <Content Remove="**\(bin|obj)-(linux|windows)\**" />
    <EmbeddedResource Remove="**\(bin|obj)-(linux|windows)\**" />
    <None Remove="**\(bin|obj)-(linux|windows)\**" />
</ItemGroup>

But this doesn't seem to work. Is there a way to do this?

I created a repository here


Solution

  • Regular expressions are not supported to select files, only globbing patterns.

    You can concatenate these globbing patterns via a semicolon.

    <ItemGroup>
      <Compile Remove="bin-linux\**;bin-windows\**;obj-linux\**;obj-windows\**" />
      <Content Remove="bin-linux\**;bin-windows\**;obj-linux\**;obj-windows\**" />
      <EmbeddedResource Remove="bin-linux\**;bin-windows\**;obj-linux\**;obj-windows\**" />
      <None Remove="bin-linux\**;bin-windows\**;obj-linux\**;obj-windows\**" />
    </ItemGroup>
    

    Or in case your project doesn't have any other folders with a bin- or obj- prefix, then you might consider below one, where any folder starting with bin- and obj- gets excluded.

    <ItemGroup>
      <Compile Remove="bin-*\**;obj-*\**" />
      <Content Remove="bin-*\**;obj-*\**" />
      <EmbeddedResource Remove="bin-*\**;obj-*\**" />
      <None Remove="bin-*\**;obj-*\**" />
    </ItemGroup>