31

I am trying to set environment variables using project file (ex. .vcxproj)

I looked at property functions but it didn't seem to have such function.

I know there is a way to retrieve environment variable but couldn't find how to set it.

I feel like there should be way to set environment variables in project file.

asgoth
  • 35,552
  • 12
  • 89
  • 98
in His Steps
  • 3,075
  • 6
  • 30
  • 38

4 Answers4

38

The coded task can be put right at the project file since MSBuild v4.0. Like this:

<UsingTask
  TaskName="SetEnvironmentVariableTask"
  TaskFactory="CodeTaskFactory"
  AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll">

  <ParameterGroup>
    <Name ParameterType="System.String" Required="true" />
    <Value ParameterType="System.String" Required="true" />
  </ParameterGroup>

  <Task>
    <Using Namespace="System" />
    <Code Type="Fragment" Language="cs">
      <![CDATA[
        Environment.SetEnvironmentVariable(Name, Value);
      ]]>
    </Code>
  </Task>

</UsingTask>

Note that in MSBuild 14+, the AssemblyFile reference should be just:

AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll"

The SetEnvironmentVariableTask can be then used from the target:

<Target Name="SampleTarget" BeforeTargets="Build">
  <SetEnvironmentVariableTask Name="TEST_ENV_VAR" Value="$(MSBuildProjectName)" />
</Target>

That's much handier than authoring a separate .DLL for small MSBuild task(s).

ogggre
  • 2,204
  • 1
  • 23
  • 19
  • Try as I might, I could not get this to work with Visual Studio 2012. However, I was able to find a solution [here](http://stackoverflow.com/questions/5886938/cannot-reference-dependency-assemblies-in-t4-template-when-using-transformonbuil/20688714#20688714). – Matt Davis Dec 19 '13 at 18:10
  • This seems to work only within the context of a given target; when I tried to read the env var from another target it wasn't there anymore – gschuager Mar 28 '14 at 16:01
  • 2
    Try Environment.SetEnvironmentVariable(Name, Value, EnvironmentVariableTarget.Machine) or Environment.SetEnvironmentVariable(Name, Value, EnvironmentVariableTarget.User). The default is Environment.SetEnvironmentVariable(Name, Value, EnvironmentVariableTarget.Process) – herskinduk Oct 30 '14 at 18:11
  • 1
    My experience was that although I could set it the environment variable I had to close/reopen VS2013 to have see the change. – herskinduk Oct 30 '14 at 18:13
  • 3
    Note that in MSBuild 14, the `AssemblyFile` reference should be just: `AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">` – minghan Jan 13 '16 at 18:33
  • The answer is to first invent the universe I see. This is *so* much more convenient than running an application from shell, and providing `SET Key="Value"` before starting the process. – Dagrooms Nov 05 '19 at 00:45
  • 2
    If using msbuild on .NET core, you must use [RoslynCodeTaskFactory](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-roslyncodetaskfactory) instead. It's available since MSBuild 15.8. Some tasks may work simply by replacing `CodeTaskFactory` with `RoslynCodeTaskFactory`. – Michael Lowman Mar 26 '20 at 14:50
10

In the more recent versions of MS Build (since 2016), you can simply do this:

<Target Name="MyEnvironmentVariables">
    <Exec Command="A command that needs environment variables" EnvironmentVariables="HANDY_ENV_VAR_1=500;HANDY_ENV_VAR_2=A Useful Value"  />
</Target>

Make sure to separate the variables with a semicolon. You don't need a trailing semicolon though.

Paul F. Wood
  • 1,453
  • 16
  • 19
7

If you are only using the variable in the context of MSBuild...

...then you can use the standard MSBuild variables (aka properties) instead of trying to set an environment variable. You can set properties when running msbuild using the /property switch (aka /p switch) (more here), as shown in the example below, which sets the PublishProfile property to the value Debug and the VisualStudioVersion property to the value 15.0

   msbuild  Project.csproj /p:PublishProfile=Debug /p:VisualStudioVersion=15.0

Find a list of the standard MSBuild's variables/properties at this question

You could also define arbitrary properties in the csproj file itself, using the <PropertyGroup> element. More on that here

If you do need to set a true environment variable...

...well, it's not an out-of-box thing. You can write a custom task and then leverage it in the project file. Here's a link to an MSDN thread that outlines how to do this: How to set envrionment variables in MSBuild file? This example creates a new C# class SetEnvVar which inherits from the Task class (Microsoft.Build.Utilities.Task), in the MSBuildTasks namespace.

   //...
   using Microsoft.Build.Framework;
   using Microsoft.Build.Utilities;

   namespace MSBuildTasks
   {
       public class SetEnvVar : Task
       {
           //...            
           public override bool Execute()
           {
               Environment.SetEnvironmentVariable(_variable, _value);
               return true;
           } 

... and then the task is invoked by this part of the csproj file:

      <UsingTask 
         TaskName="MSBuildTasks.SetEnvVar"
         AssemblyFile="$(RootDir)Tools\Bin\MSBuildTasks.dll"/>
Nate Anderson
  • 18,334
  • 18
  • 100
  • 135
Nick Nieslanik
  • 4,388
  • 23
  • 21
3

You might consider writing the environment variables to a text file (.cmd) as a sequence of SET XXX=$(XXX) lines. Then execute the .cmd in the command window.

e.g. Define an ItemGroup with all the SET commands, then use Task 'WriteLinesToFile' to write each item to a line in the .cmd text file.

<ItemGroup>
  <BuildEnvItem Include="REM This file is auto generated. Do not edit" />
  <BuildEnvItem Include="SET TEST_ENV_VAR=$(TEST_ENV_VAR)" />
  <!-- add more as needed -->
<ItemGroup>

<Target Name="DefineBuildEnvironmentVariables">
    <WriteLinesToFile File="Build_env.cmd" Lines="@(BuildEnvItem)" Overwrite="true" Encoding="Unicode"/>
</Target>

This is may be useful in the situation where there is an existing .cmd that is using msbuild. The initial .cmd uses msbuild to generate the Build_env.cmd, and then calls Build_env.cmd before proceeding.