How can I set the LIB environment variable inside a csproj file? - c#

I'm operating on a system where the environment variable LIB is set to "--must-override--". I cannot change the value of the variable on the system itself.
In Visual Studio, the LIB variable is checked during compilation. Because it's set to a junk value, I get a warning in the build:
Invalid search path '--must-override--' specified in 'LIB environment variable' -- 'The system cannot find the path specified.
I'd like to get rid of this warning. To do so, I need to override the value of the LIB environment variable that VS uses, either to NULL or to some value pointing to a real path.
Since I can't change the value of the variable in the environment, I need to do it within the csproj file itself. I've tried setting it in a property group to no avail:
<PropertyGroup>
<Lib></Lib>
</PropertyGroup>
Any ideas on how this variable can be set? Or if it's even possible?

You can mangle it in using the Exec Task, or you can write your own Task to set them - here's the "Let's mangle away with Exec" route:
<PropertyGroup>
<!--
need the CData since this blob is just going to
be embedded in a mini batch file by studio/msbuild
-->
<LibSetter><![CDATA[
set Lib=C:\Foo\Bar\Baz
set AnyOtherEnvVariable=Hello!
]]></LibSetter>
</PropertyGroup>
<Exec Command="$(LibSetter)" />
EDIT:
So I just threw this csproj together with the basics - I've confirmed they do set properly when I run them - I've added in the inline-Task approach as well.
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask
TaskName="EnvVarSet"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<VarName ParameterType="System.String" Required="true"/>
<VarValue ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
Console.WriteLine("Setting var name {0} to {1}...", VarName, VarValue);
System.Environment.SetEnvironmentVariable(VarName, VarValue);
Console.WriteLine("{0}={1}", VarName, VarValue);
]]>
</Code>
</Task>
</UsingTask>
<Target Name="ThingThatNeedsEnvironmentVars">
<CallTarget Targets="FiddleWithEnvironmentVars"/>
<Message Text="LIB environment var is now: $([System.Environment]::GetEnvironmentVariable('LIB'))"/>
</Target>
<Target Name="FiddleWithEnvironmentVars">
<Message Text="LIB environment var is now: $([System.Environment]::GetEnvironmentVariable('LIB'))"/>
<EnvVarSet VarName="LIB" VarValue="C:\temp"/>
<Message Text="LIB environment var is now: $([System.Environment]::GetEnvironmentVariable('LIB'))"/>
</Target>
</Project>

Related

Setting a version number, by setting a PropertyGroup, a project variable in the csproj file

I am trying to set the version number to something I want (read they want me to do).
They want a specific format, and I need to set the version of out programme.
I tried to use a UsingTask to set a PropertyGroup variable. My UsingTask is working.
All this is new to me, I managed to get the code below. Now, how can I set the local project variable?
My initial value is used, next I want to set it programmatically in the UsingTask
This is all done in the *.csproj file
<Project Sdk="Microsoft.NET.Sdk">
<!-- my additional project variable, used below -->
<PropertyGroup Label="UserMacros">
<AssemblyVersion>1.2.3.5</AssemblyVersion>
</PropertyGroup>
<UsingTask TaskName="EnvVarSet"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
// here I want to set the version number to our format
Log.LogMessage(MessageImportance.High, "**** Setting variables");
System.Environment.SetEnvironmentVariable("AssemblyVersion", "1.5.6.7");
var a = Environment.GetEnvironmentVariable("AssemblyVersion");
Log.LogMessage(MessageImportance.High, "**** Value is: " +a);
]]>
</Code>
</Task>
</UsingTask>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<!-- the version number I want to set -->
<Version>$(AssemblyVersion)</Version>
<!-- this does not work -->
<!-- <Version>$([System.Environment]::GetEnvironmentVariable('AssemblyVersion'))</Version>-->
other values....
</PropertyGroup>
//more project....
<Target Name="BeforeBeforeBuild" BeforeTargets="BeforeBuild">
<EnvVarSet />
<Message Text="***** AssemblyVersionis now: $([System.Environment]::GetEnvironmentVariable('AssemblyVersion'))"/>
</Target>
</Project>
You can view the document https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-build.
You can do this by using the dotnct command. Use multiple doctnct commands to pass the version number to each command and build the project and all its dependencies via dotnet build-.
You can use the following command to help you set the version number: dotnet build /p:AssemblyVersion=1.2.3.4
I managed to get what I want.
Here is my example of how to do the classic Build and Revision autoincrement numbers in the assembly version
This is an example, I was asked to do another format, but one can create any format this way
First, add the AssemblyVersion
<PropertyGroup>
<!-- add this line -->
<AssemblyVersion>1.0.0.0</AssemblyVersion>
And add this code, which is similar to "1.2.*" in .Net Framework
<Target Name="BeforeBeforeBuild" BeforeTargets="BeforeBuild">
<SetBuildNumber>
<Output TaskParameter="AssemblyVersion" PropertyName="AssemblyVersion" />
</SetBuildNumber>
</Target>
<UsingTask TaskName="SetBuildNumber" TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<AssemblyVersion ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
Log.LogMessage(MessageImportance.High, "Setting version number");
var now = DateTime.Now;
var secondsSinceMidnightDivivedBy2 = (int)(now - now.Date).TotalSeconds / 2;
var daysSinceJan1st2000 = (int)(now - new DateTime(2000, 1, 1)).TotalDays;
AssemblyVersion = "1.2." +
daysSinceJan1st2000.ToString() + "." +
secondsSinceMidnightDivivedBy2.ToString();
Log.LogMessage(MessageImportance.High, "Version number is: " + AssemblyVersion);
]]>
</Code>
</Task>
</UsingTask>
In the code above, one can set the assembly version to what may be required
The code above give a custom assembly version to the final exe or dll file, but for some reason it does not work, when the installer publishes the project to one file, in the "pre-build event command line":
dotnet publish $(SolutionDir)NCAutomatedReport\NCAutomatedReport.csproj
-p:PublishProfile=FolderProfile -r:win10-x64 -p:PublishSingleFile=true
-p:PublishReadyToRun=false -p:PublishTrimmed=false -c:Release
-o:$(TargetDir)published
This gives the error
The task factory "CodeTaskFactory" is not supported on the .NET Core version of MSBuild.
and
The task factory "CodeTaskFactory" could not be loaded from the assembly
"C:\Program Files\dotnet\sdk\6.0.400\Microsoft.Build.Tasks.Core.dll".
The task factory must return a value for the "TaskType" property.
Now I should look into this - any ideas anyone?

How to set a configuration parameter using service messages in the project file

Description
TeamCity side: I defined a Configuration Parameter named major.minor.patch with an empty value. This can be accessed in TeamCity build steps as %major.minor.patch%
Visual Studio side:
In my .csproj file I added the code below to set major.minor.patch to the current version of my assembly:
<PropertyGroup Condition="'$(TEAMCITY_BUILD_PROPERTIES_FILE)' == ''">
<TeamCityBuild>true</TeamCityBuild>
</PropertyGroup>
<!-- Other stuff... -->
<Target Name="TeamCity" AfterTargets="Build" Condition="'$(TeamCityBuild)' == 'true'">
<GetAssemblyIdentity AssemblyFiles="obj\$(ConfigurationName)\$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="AssemblyItentity"/>
</GetAssemblyIdentity>
<Message Text="##teamcity[setParameter name='major.minor.patch' value='%(AssemblyIdentity.Version)'"/>
Problem
However, this code seems not working since the Parameter is still empty.
I also tried defining my parameter as an environment variable: env.maor.minor.patch, still no chance.
Question
How can I do this?
How to set a Configuration Parameter in TeamCity to my assembly version in my C# project?
Try setting the Importance of the Message to High
Message Text="##teamcity[setParameter name='major.minor.patch' value='%(AssemblyIdentity.Version)'" Importance="high" />

How to manually evaluate msbuild condition?

I'm creating a custom msbuild task that will be processing a configuration from custom XML file. I want to allow to use Condition attribute in that xml file. Syntax of that attribute should be the same as MSBuild Conditions (https://msdn.microsoft.com/en-us/library/7szfhaft.aspx)
How can I evaluate value of that attribute? Is there an existing library that automate that or I'm forced to write my own parser?
So far I was able only to get value of all variables that probably will be necessary to evaluate that conditions (How to access the MSBuild 's properties list when coding a custom task?)
I’m not sure if this will be helpful for you. I was solving the similar problem c++ projects. I was thinking to use Microsoft.Build.BuildEngine.Project class but later changed my mind. Finally I’ve created mine config in msbuild style (including namespace). I’ve enforced importing my config by msbuild (I’ve misused ForceImportAfterCppTargets property). Msbuild evaluated everything for me. Mine injected config (or props/target file) contained target that was injected into build process by overriding some build property (at the project level) in a way my target was called. Mine custom target called mine custom task with passed all necessary properties and items by parameters.
Following content is response on Uriel Jun 12 at 16:26:
Because you've marked question with tag c# I tried to make sample with C# vs 2010.
I made sample really simple. I put task and xml configuration file into one file named my.props. My custom task just prints values of my configuration provided by item. It prints metadata of item.
One think you have to do is to manually modify your .csproj by adding one simple line. After the line where is Microsoft.CSharp.targets imported add import of custom my.props file.
This sample expects your my.props is in the same directory as .csproj.
Diff style change:
+
Content of my.props:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="MyTool" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<Cfg ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
if (Cfg.Length > 0)
{
for (int i = 0; i < Cfg.Length; ++i)
{
ITaskItem item = Cfg[i];
string value1 = item.GetMetadata("Value1");
string value2 = item.GetMetadata("Value2");
Log.LogMessage(MessageImportance.High, "MyTool: {0} - {1}", value1, value2);
}
}
]]>
</Code>
</Task>
</UsingTask>
<ItemDefinitionGroup Condition=" '$(Configuration)' == 'Release' ">
<MyConfig>
<Value1>Hello</Value1>
<Value2>World</Value2>
</MyConfig>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition=" '$(Configuration)' == 'Debug' ">
<MyConfig>
<Value1>Hello</Value1>
<Value2>Debug world</Value2>
</MyConfig>
</ItemDefinitionGroup>
<ItemGroup>
<MyConfig Include="MyCfg" />
</ItemGroup>
<Target Name="AfterBuild">
<MyTool Cfg="#(MyConfig)" />
</Target>
</Project>

How to call second target in Msbuild

I need to call the second target in the msbuild but when I'm calling it in the cmd it shows error my code is give below
MsBuild.csproj
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<alen>123456</alen>
</PropertyGroup>
<Target Name="FirstTarget">
<Message Text="Hello World $(alen)" />
</Target>
<Target Name="SecondTarget">
<Message Text="The second target" />
</Target>
</Project>
The first target called successfully but I cant load the second Target...How it is possible???
Since you have not defined it, the default target is the first target in the file, FirstTarget. To call the second target from the command line you need call it explicitly with /t:SecondTarget. You can use /t:FirstTarget;SecondTarget if you want to run both.
You could also define SecondTarget to always come after first target. Use the AfterTargets attribute like so:
<Target Name="SecondTarget" AfterTargets="FirstTarget">
Now msbuild msbuild.proj would call both targets.
I know this is a really old post, but you can also have one target call other targets.
<Target Name="Build">
<CallTarget Targets="PreBuild"/>
<CallTarget Targets="Main"/>
<CallTarget Targets="AfterBuild"/>
</Target>
Have you tried
%SystemRoot%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe "D:\test_2\MsBuild\MsBuild\BuildScript\MsBuild.csproj" /t:SecondTarget
?
Another option is to define a default target in your build file and than define the order of targets using the DependsOnTargets:
<Project DefaultTargets="DefaultTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<Target Name="DefaultTarget" DependsOnTargets="FirstTarget;SecondTarget">
<Message Text="Executing DefaultTarget" />
</Target>
<!-- your targets -->
</Project>
The targets defined in DependsOnTargets will run before the target itself is running.
Doing it this way, you do not need to set the /t: parameter in your call.

Expand MSBuild Properties containing wildcard into Items

I am trying to write MSBuild script that will perform some action (eg. print its path) on an arbitrary files (specified as a property on a command line) in some predefined directory (F:\Files).
Given the following directory structure
F:\Files\TextFile.txt
F:\Files\Subdir1\ImageFile.bmp
F:\Files\Subdir1\SubSubdir\ImageFile2.bmp
F:\Files\Subdir1\SubSubdir\TextFile2.txt
And MSBuild Script
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="PrintNames" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetDir>F:\Files</TargetDir>
</PropertyGroup>
<ItemGroup>
<Files Include="$(TargetDir)\$(InputFiles)"/>
</ItemGroup>
<Target Name="PrintNames">
<Message Text="Files: #(Files, ', ')" />
</Target>
</Project>
running the script with InputFiles set to "**\*.bmp;**\*.txt" works fine only for bmp files. Txt files are taken from the current working directory, not from "F:\Files"
There are two problems that you have to solve:
$(InputFiles) is specifed as a scalar property, but you want to interprete it as an array
$(InputFiles) contains wildcards you want to expand after you do transformation on the list of patterns in $(InputFiles).
It is easy to solve either of two problems separately, but combination of the two is actually tricky. I have one possible solution, and it works, but the downside is you have to encode '*' characters in your pattern definition.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="PrintNames" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetDir>c:\temp\MyContent</TargetDir>
<InputFilesRelativeEsc>%2A%2A\%2A.bmp;%2A%2A\%2A.txt</InputFilesRelativeEsc>
</PropertyGroup>
<Target Name="PrintNames">
<ItemGroup>
<_TempGroup Include="$(InputFilesRelativeEsc)" />
</ItemGroup>
<CreateItem Include="#(_TempGroup->'$(TargetDir)\%(Identity)')">
<Output TaskParameter="Include" ItemName="_EvaluatedGroup" />
</CreateItem>
<Message Text="_EvaluatedGroup: %(_EvaluatedGroup.FullPath)" />
</Target>
</Project>
It works as follows. Property InputFilesRelativeEsc is a list of relative file patterns. Notice wildcard characters are encoded (%2A is a hex code for asterisk). Since the wildcards encoded, the group _TempGroup does not attempt to search for and extract list of files while you Include this patterns into this group. Now _TempGroup is a group which consists of two elements: **\*.bmp and **\*.txt. Now that you have a real group you can transform it. The only complication is that the normal MSBuild mechanism of running transform does not expand wildcards. You have to use older CreateItem task. The CreateItem task is actually declared deprecated by MSBuild team, but it still works.

Categories