msbuild, how to set environment variables? - visual-c++

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.

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).

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.

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"/>

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.

Related

How to use NLog to write to different file according to parameter

I want to be able to write to logs to a different log file according to one of my log parameters.
Lets say, bankId. Each day and bank will write to their own file like
2019-11-11-bank11.log
2019-11-12-bank11.log
and so on.
How can i achieve that programmatic with some kind of a pattern for the log file.
I don't want to create a new version of my app every time I have a new bankid.
You could set the context in one of the context classes and render it with a layout renderer.
e.g. use ${mdlc} and MappedDiagnosticsLogicalContext:
Config:
<target name="file" xsi:type="File"
fileName="${basedir}/${mdlc:bankid}.log" .. />
Code:
using (NLog.MappedDiagnosticsLogicalContext.SetScoped("bankid", "bank11"))
{
logger.Info("myLogEvent");
}
See also all context layout renderers
Sounds like you have an application-wide setting. For that you should either use:
NLog Config Variables - ${var:bankid}
Global Diagnostic Context - ${gdc:bankid}
I prefer to use the GDC has it has a minimum number of surprises, when modifying at runtime.
You can do this in NLog.config:
<target name="file" xsi:type="File"
fileName="${basedir}/${shortdate}-bank${gdc:bankid:whenEmpty=0}.log" .. />
Then at application startup then you can do this:
NLog.GlobalDiagnosticsContext.Set("bankid","11");
See also: https://github.com/nlog/nlog/wiki/Gdc-Layout-Renderer
You can also extract a setting from config-file:
app.config - https://github.com/NLog/NLog/wiki/AppSetting-Layout-Renderer
appsettings.json - https://github.com/NLog/NLog/wiki/ConfigSetting-Layout-Renderer

Xamarin.iOS versioning during build

I've been trying to get an automatic versioning system going for builds (mainly due to external crash analytics picking up each build as the same until I change the version manually). The format is simple, I take the CFBundleShortVersionString from the Info.plist, and append the current date and time (in yyyyMMddmmss format) as subversion.
The task I've put together for this:
<Project>
<Target Name="BeforeBuild">
<XmlPeek XmlInputPath="$(ProjectDir)Info.plist" Query="//dict/key[. = 'CFBundleShortVersionString']/following-sibling::string[1]">
<Output TaskParameter="Result" ItemName="VersionNumber" />
</XmlPeek>
<PropertyGroup>
<BuildNumber>$([System.DateTime]::Now.ToString(yyyyMMddmmss))</BuildNumber>
</PropertyGroup>
<XmlPoke XmlInputPath="$(ProjectDir)Info.plist" Query="//dict/key[. = 'CFBundleVersion']/following-sibling::string[1]" Value="$(VersionNumber).$(BuildNumber)" />
</Target>
</Project>
However it fails with the following error:
Target BeforeBuild:
[...]/[...].csproj(1069,5): error MSB3733: Input file "[...]/Info.plist" cannot be opened. For security reasons DTD is prohibited in this XML document. To enable DTD processing set the DtdProcessing property on XmlReaderSettings to Parse and pass the settings into XmlReader.Create method.
Done building target "BeforeBuild" in project "[...].csproj" -- FAILED.
What am I doing wrong? There's not much info about this error, at least not much that I could find and would help fixing it.

Use tstamp property in tasks in a project

Can I use tstamp property BuildDate in a nant task to an xecutable task in cruise control net as given below? if that is possible, Is my usage correct?
<tstamp property="BuildDate" pattern="dd-mmm-yy" verbose="true" />
<exec executable="C:\WINDOWS\system32\cmd.exe">
<buildArgs>/C rename "D:\Disk Images\Disk1" ICE_$(BuildDate)"</buildArgs>
<buildTimeoutSeconds>10</buildTimeoutSeconds>
</exec>
Taking a first look everything looks fine so far... except for this: Use curly braces when accessing the property. So it's Disk1" ICE_${BuildDate}" instead of Disk1" ICE_$(BuildDate)".
UPDATE: Wait a minute... You're trying to pass the property back from NAnt to CCNET? No, that won't work. You may use the BuildDate property inside NAnt only.
A cumbersome way to achieve this would be to write your values into a xml file using nant, and then use the modificationReader task.

CruiseControl.NET: How to access modifications in MSBuild task?

I'd like to do some action based on modified files. I have such project configuration
<project name="MyProject">
<sourcecontrol type="vsts" autoGetSource="true">
...
</sourcecontrol>
<tasks>
<msbuild>
...
</msbuild>
</tasks>
Is there any way how to put or access the modifications comes from source control in that configured MSBuild task? I cannot see any integration property for this, but I can see modifications in CCNET build log
<cruisecontrol project="MyProject">
<request source="ScheduledTrunk" buildCondition="ForceBuild">...</request>
<modifications>
<modification type="merge">
<filename>MyFile.cs</filename>
<project>$/MyProject/Trunk/Source/</project>
<date>2010-02-23 02:27:40</date>
<user>domain\user</user>
<comment>Some comment</comment>
<changeNumber>79367</changeNumber>
<version>79367</version>
</modification>
</modifications>
<integrationProperties>
...
</integrationProperties>
<build date="..." buildtime="..." buildcondition="...">
<msbuild
startTime="02/23/2010 11:55:52"
elapsedTime="00:00:51" success="true"
>
...
</msbuild>
</cruisecontrol>
Thanks for suggestion!
BTW do you know why the common CCNet documentation pages are down for several days already? And what is the primary discussion forum for CCNET?
You are looking for the Mofification Writer Task. This tasks writes modification details to an XML file, which can easily be evaluated from an MSBuild task.

Include build output as attachment

We have a CruiseControl.NET Server 1.5.0.4401 running. One project uses an external compiler, which is included via exec-task. This compiler is configured to write its output into a textfile that is located in the artifact directory. The filename is keil_Revision-x.txt (x is the revision number) whereas the configuration for this file looks like %ccnetartifactdirectory%\keil_%ccnetlabel%.txt.
We want this output file to be attached to the e-mail report CC is sending for each build. The configuration of the email-publisher is (a bit shortened) the following:
<email from="buildmaster#xxx.yy" mailhost="zzz" mailport="25" includeDetails="TRUE" useSSL="FALSE">
<users>
<!-- Here are some users -->
</users>
<groups>
<!-- Here are some groups -->
</groups>
<converters>
<!-- LDAP converter-->
</converters>
<modifierNotificationTypes>
<!-- Several notification types -->
</modifierNotificationTypes>
<subjectSettings>
<!-- Here are some subject settings -->
</subjectSettings>
<attachments>
<file>${CCNetArtifactDirectory}\keil_${CCNetLabel}.txt</file>
</attachments>
</email>
The only problem is, that the files are not attached. The debug output on the cruise control console doesn't contain any error message. The artifact directory contains all files, only they are not attached. Where is the failure?
Are Integration Properties like CCNetLabel available inside CCNET configuration? I venture to doubt that. Up to CCNET 1.4.4 SP1 they weren't. So please check the attachment node in Your project configuration to see if CCNetLabel has been resolved properly.
You need to define preprocessor constants that can replace the integration properties like this:
<cruisecontrol xmlns:cb="urn:ccnet.config.builder">
<cb:define project.artifact.dir="C:\foodir" />
<project name="foo">
<artifactDirectory>$(project.artifact.dir)</artifactDirectory>
...
<publishers>
...
<email
from="buildmaster#xxx.yy"
mailhost="zzz"
mailport="25"
includeDetails="TRUE"
useSSL="FALSE">
....
<attachments>
<file>$(project.artifact.dir)\keil.txt</file>
</attachments>
</email>
</publishers>
</project>
</cruisecontrol>
You need to instruct your compiler to write the results to a file whose name is predictable by CCNET configuration. Since configuration has no access to the label it must not be part of the filename. If you want to keep the result files from being overwritten by each build, add an executable task that triggers a simple batch file whose purpose is to copy %ccnetartifactdirectory%\keil.txt to %ccnetartifactdirectory%\keil_%ccnetlabel%.txt.
Otherwise the answer to this question might help here as well.

Resources