Search code examples
c#.netvisual-studiomsbuildcsproj

Build multiple C# project configurations simultaneously with .csproj variables


Imagine having a mathematics library, which provides some functionality optimized for IEEE754 number types. The code might have something along the lines of:

#if F16
    global using Scalar = System.Half;
#elif F32
    global using Scalar = System.Single;
#elif F32
    global using Scalar = System.Double;
#endif

// ...

or

#if F32
    public static float HighlyOptimizedMethod(float a, float b, float c) { ... };
#elif F64
    public static double HighlyOptimizedMethod(double a, double b, double c) { ... };
#endif

I now define those preprocessor constants F16, F32, F64 in my .csproj project file as follows:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <!-- change the following for different precisions. -->
        <!--<Scalar>F16</Scalar>-->
        <!--<Scalar>F32</Scalar>-->
        <Scalar>F64</Scalar>

        <DefineConstants>$(DefineConstants);$(Scalar)</DefineConstants>
    </PropertyGroup>
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>

        <AssemblyName Condition="'$(Scalar)' != ''">mylibrary.$(Scalar)</AssemblyName>
        <AssemblyName Condition="'$(Scalar)' == ''">mylibrary</AssemblyName>
    
        ...
    </PropertyGroup>
</Project>

My question

Is it possible to make VisualStudio/dotnet/MSBuild create the assemblies for all three configurations at the same time when building the project? i.e. would it be possible to run 'compile' and then have the following file list in the bin/ directory?

  • mylibrary.F16.dll
  • mylibrary.F32.dll
  • mylibrary.F64.dll

I tried fiddling around with something like this:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <!-- change the following for different precisions. if all are commented out, every configuration will be built. -->
   
        <!--<Scalar>F16</Scalar>-->
        <!--<Scalar>F32</Scalar>-->
        <Scalar>F64</Scalar>

        <DefineConstants>$(DefineConstants);$(Scalar)</DefineConstants>
    </PropertyGroup>
    <Target Name="BuildOthers" BeforeTargets="CoreBuild" Condition="'$(Scalar)' == ''">
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);Scalar=F16" />
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);Scalar=F32" />
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);Scalar=F64" />
    </Target>
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>

        ...

But it didn't get it to work. May someone be able to help me and/or point me into the right direction?

Thanks.


Solution

  • would it be possible to run 'compile' and then have the following file list in the bin/ directory?

    Yes. You can check the following like this:

    <PropertyGroup>
     <Scalar>''</Scalar>
     <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </PropertyGroup>
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);Scalar=F16" />
        <Copy
           SourceFiles="\obj\Debug\net8.0\mylibrary.F16.dll"
           DestinationFolder="bin\$(Configuration)\Resources"
        />
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);Scalar=F32" />
        <Copy
           SourceFiles="\obj\Debug\net8.0\mylibrary.F32.dll"
           DestinationFolder="bin\$(Configuration)\Resources"
        />
        <MSBuild Projects="$(MSBuildProjectFile)" Properties="Configuration=$(Configuration);Scalar=F64" />
        <Copy
           SourceFiles="\obj\Debug\net8.0\mylibrary.F64.dll"
           DestinationFolder="bin\$(Configuration)\Resources"
        />
    </Target>
    

    But it didn't get it to work(target BuildOthers)

    Based on my test, i found the mylibrary.F12.dll and mylibrary.F32.dll are removed by target IncrementalClean.

    1.delte mylibrary.F12.dll after generating mylibrary.F32.dll remove mylibrary.F12.dll

    2.delte mylibrary.F32.dll after generating mylibrary.F64.dll remove mylibrary.F32.dll

    As we know, IncrementalClean will remove files that were produced in a prior build. In this mode, it is not possible to have the three .dll file in bin folder when run msbuild. To prevent files to be removed during incremental build, you can add <copy> task to copy .dll files to bin folder.

    Also you can try to disable incremental build. For more information, please read doc:--no-incremental

    Besides, as the easiest way to achieve it ,you can try @Jonathan suggestion. Here is a similar ticket you can refer:

    How to create multiple assets (dll) for one project with msbuild depend on Constants value?