0

I have a C# project with few dependencies, and a list of build environment properties (dev, staging, prod). I am trying to build the project multiple times per env in a separate folder and with a environment constant same as the env properties - similar to building multiple .NET versions.

The limitaiton i got is that this should work from VS - meaning with no additional parameters when building the csproj.

I got the following solution, but for some reason I get CS0006 Metadata file could not be found error on the dependencies

  <ItemGroup>
  <BuildEnvironment Include="Dev" />
  <BuildEnvironment Include="Staging" />
  <BuildEnvironment Include="Prod" />
</ItemGroup>

<!-- Entry point target that runs automatically -->
<Target Name="BuildAllEnvironments" BeforeTargets="Build">

  <!-- Loop through each environment and build -->
  <MSBuild Projects="$(MSBuildProjectFullPath)" Targets="BuildSingleEnvironment" Properties="BuildEnvironment=%(BuildEnvironment.Identity)" />
</Target>

<!-- Target that builds for a single environment -->
<Target Name="BuildSingleEnvironment">
  
  <PropertyGroup>
    <OutputPath>$(OutputPath)$(BuildEnvironment)\</OutputPath>
    <DefineConstants>$(DefineConstants);BUILD_ENV_$(BuildEnvironment)</DefineConstants>
  </PropertyGroup>

  <!-- Actually build the project for this environment -->
  <MSBuild Projects="$(MSBuildProjectFullPath)" Targets="CoreBuild" Properties="OutputPath=$(OutputPath);DefineConstants=$(DefineConstants);" />
</Target>
1
  • There are reference resolutions performed before the CoreBuild target. You should run the Build target instead. What is different in the compilation of the assembly between the 'Dev', 'Staging', and 'Prod' environments? If the only difference is the output path, then don't have different builds. Just have multiple "packaging" steps. If the compilation is different and producing a different executable, doesn't that invalidate the purpose of different testing environments? Commented Nov 12 at 11:22

1 Answer 1

0

MSBuild is designed to build a project for a given configuration/platform. The standard configurations are Debug and Release. If a project is already up-to-date for the configuration/platform, the project is not built.

When building a project or a solution from within Visual Studio, the Build target is run. You have injected the BuildAllEnvironments target to run before the Build target when the Build target is run. The MSBuild task in the BuildSingleEnvironment target is invoking the CoreBuild target but that's jumping ahead in the target chain. There are reference resolution steps between Build and CoreBuild that are being missed and are probably the cause of the CS0006.

A target is run once and MSBuild task documentation notes:

Unlike using the Exec task to start MSBuild.exe, this task uses the same MSBuild process to build the child projects. The list of already-built targets that can be skipped is shared between the parent and child builds. This task is also faster because no new MSBuild process is created.

An alternative approach that doesn't mess with the standard workings of the project is to create a utlity project with the Microsoft.Build.NoTargets SDK. Something like the following (which is incomplete and not tested):

<Project Sdk="Microsoft.Build.NoTargets">
  <Target Name="BuildAllEnvironments" BeforeTargets="Build">
    <PropertyGroup>
      <TargetProject>MyProject.csproj</TargetProject>
    </PropertyGroup>
    <ItemGroup>
      <BuildEnvironment Include="Dev" />
      <BuildEnvironment Include="Staging" />
      <BuildEnvironment Include="Prod" />
    </ItemGroup>
    ...
    <Exec Command="msbuild $(TargetProject) /p:%(BuildEnvironment) ..." />
  </Target>
</Project>

By using the Exec task, a new MSBuild process is created for each value of the BuildEnvironment ItemGroup.

However, what is being done in the question is a code smell. Presumably, because the environment is passed to DefineConstants, there are C# preprocessor directives testing for the environment and changing the compilation. Generally when you have a series of environments like 'Dev', 'Staging', and 'Prod' the point of it is to deploy the same compiled bits; i.e. what is tested in 'Staging" is exactly the same binaries that passed testing in 'Dev'. That's not possible here.

I can guess at why this is being done but it really doesn't matter. I just want to point out that if it seems like building this way for multiple environments is difficult and unsupported and that you are swimming against the tide -- it's because it is essentially unsupported and you are swimming against the tide. :smiley:

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.