MSBuild Command Line arguments length exceeds 8191 characters - msbuild

I are using MSBUILD.exe to perform build for the application. as part of this I pass the required variables as command line arguments for MSBUILD.exe.
I have 2 files. service.xml and MyService.proj. Below line is present in service.xml file.
<installCommand name="MyService" cmd="msbuild.exe "MyService.proj" /p:{vairables}">
initially the length of the command was small and everything was fine as I was able to build my project but as the project size increased the number of parameters also increased, and now I am at a stage where the command line is displaying an error (Input line is too Long).
Upon some searching I found out that command line cannot be more than 8191 characters.
Can any one Suggest any alternatives for this.

What's installCommand?
MSBuild engine merges all system, user and process variables as well as parameters and properties into one big pool, so every property that you pass via {vairables} can be set first separatly. Keep in mind that properties pass via command line are global properties so environment variable equivalent will not override corresponding project property unless it has Condition=" '$(Foo)' == '' " on it.
http://msdn.microsoft.com/en-us/library/ms171458.aspx

You can set variables in an xml file, and pass the xml file-name to the msbuild script.
Here is a simple example that uses the MSBuildCommunityTasks.
Parameters.xml (contents below)
<?xml version="1.0" encoding="utf-8"?>
<parameters>
<setParameter name="LineNumber1" value="PeanutsAreCool" />
<setParameter name="LineNumber2" value="" />
</parameters>
MyMsbuildDef.proj (contents below)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapped">
<Import Project="$(MSBuildExtensionsPath32)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<PropertyGroup>
<!-- Always declare some kind of "base directory" and then work off of that in the majority of cases -->
<WorkingCheckout>.</WorkingCheckout>
</PropertyGroup>
<Target Name="AllTargetsWrapped">
<CallTarget Targets="ReadXmlPeekValue" />
<CallTarget Targets="WriteXmlPeekValue" />
</Target>
<Target Name="ReadXmlPeekValue">
<!-- you do not need a namespace for this example, but I left it in for future reference -->
<XmlPeek Namespaces="<Namespace Prefix='peanutNamespace' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>"
XmlInputPath=".\Parameters.xml"
Query="/parameters/setParameter[#name='LineNumber1']/#value">
<Output TaskParameter="Result" ItemName="Peeked" />
</XmlPeek>
<Message Text="#(Peeked)"/>
<XmlPeek Namespaces="<Namespace Prefix='peanutNamespace' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>"
XmlInputPath=".\Parameters.xml"
Query="/parameters/setParameter[#name='LineNumber1']/#value">
<Output TaskParameter="Result" PropertyName="PeekedSingle" />
</XmlPeek>
<Message Text="PeekedSingle = $(PeekedSingle) "/>
</Target>
<Target Name="WriteXmlPeekValue" Condition=" '$(PeekedSingle)' != '' ">
<XmlPoke Namespaces="<Namespace Prefix='msb' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/>"
XmlInputPath=".\Parameters.xml"
Query="/parameters/setParameter[#name='LineNumber2']/#value"
Value="$(PeekedSingle)" />
</Target>
</Project>
MyBatFile.bat (contents below)
set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v4.0.30319
call %msBuildDir%\msbuild /target:AllTargetsWrapped "MyMsbuildDef.proj" /p:Configuration=Debug;FavoriteFood=Popeyes /l:FileLogger,Microsoft.Build.Engine;logfile=ZZZZZAllTargetsWrapped.log
set msBuildDir=

Related

<When> element below <Choose> element is unknown — why?

I manually edited my MSBuild file to execute a few commands only when a certain condition is met:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<Target Name="Rebuild" DependsOnTargets="Clean">
<Choose>
<When Condition="'$(MSBuildProjectDirectory)'!=''" >
<RemoveDir Directories="$(MSBuildProjectDirectory)\intermediate" />
<RemoveDir Directories="$(MSBuildProjectDirectory)\$(BaseIntermediateOutputPath)" />
<RemoveDir Directories="$(MSBuildProjectDirectory)\$(BaseOutputPath)" />
<Message Text="Artifact files have been deleted." />
</When>
</Choose>
</Target>
</Project>
Yet, Visual Studio 2019 denies to load the .vcxproj file. There error message is:
<When> element below <Choose> elements is unknown.
What did I do wrong here?
According to https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditional-constructs?view=vs-2019 I was convinced that everything I did was by the book.
See Choose element (MSBuild) and note the parent elements. Choose cannot be used within a Target.
However, $(MSBuildProjectDirectory) will always be set with a value and <When Condition="'$(MSBuildProjectDirectory)' != ''" > will always be true.

Get a property value out of an MSBuild Task

My scenario is as follows.
I have a .targets file that does some stuff, for example,
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Some UsingTask here -->
<PropertyGroup>
<CustomProperty1>MyFirstValue</CustomProperty1>
<CustomProperty2>MySecondValue</CustomProperty2>
<Target Name="AfterResolveReferences">
<MSBuild Projects="MyProjectLocation\MyProject.csproj" Properties="CustomProperty1=$(CustomProperty1);CustomProperty2=$(CustomProperty2)" Targets="Rebuild" />
<Message Text="CodeGenerated = $(CodeGenerated)" />
</Target>
<Target Name="CodeGeneratedSpecificStuff" Condition="$(CodeGenerated) == 'true'">
<!-- Stuff happens -->
</Target>
<!-- More targets and bits and pieces -->
</Project>
And MyProject.csproj looks like this:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<!-- Some property groups with some properties -->
<!-- Also Compile includes etc as expected in a .csproj file -->
<Target Name="BeforeBuild" DependsOnTargets="GenerateCode">
<ItemGroup Condition="$(CodeGenerated) == 'true'">
<Compile Include="MyGeneratedCode.cs" />
</ItemGroup>
</Target>
<Target Name="GenerateCode">
<MyCodeGenerationCustomTask MyOperationalProperty="$(CustomProperty1)">
<Output TaskParameter="CodeGenerated" PropertyName="CodeGenerated" />
</MyCodeGenerationCustomTask>
</Target>
</Project>
I am trying to bubble the "CodeGenerated" property that comes from my MyCodeGenerationCustomTask in the .csproj file to get out into my .targets file.
So far I've tried:
Passing in the "CodeGenerated" property as empty at the MSBuild call in the .targets file.
Specifying an "Output" element under the MSBuild task (it doesn't work as the MSBuild task doesn't have a "CodeGenerated" property...)
I have also tried setting "Outputs" on both the "BeforeBuild" target and on the "Project" element in the .csproj file to "$(CodeGenerated)" (MSBuild didn't complain about the existence of the Outputs tag on the Project element, so I thought I might as well give it a go), but the value does not bubble up to the .targets file. In the .targets file I did also change the MSBuild task to look more like this:
<MSBuild Projects="MyProjectLocation\MyProject.csproj" Properties="CustomProperty1=$(CustomProperty1);CustomProperty2=$(CustomProperty2)" Targets="Rebuild">
<Output TaskParameter="TargetOutputs" PropertyName="CodeGenerated" />
</MSBuild>
But as I would expect this just contained the list of generated files from the MSBuild task.
Important to note is the code generation is working fine, and the CodeGenerated property within the .csproj file functions correctly and conditionally includes the cs file for compilation.
Is this possible? Am I just hitting my head against a brick wall? Or am I missing some MSBuild magic?
I really want to avoid checking the specific .cs location at every level above for its existence.

Compiler Additional Options computed in a custom Target

I have a msbuild custom Target and a Task computing a Value.
The Task will output the Value as Property.
This Property I would like to uses as Additional Option to the Compiler call.
But the Property is empty when used as Additional Option.
My *.targets File looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="GetBranchName_TASK" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<sPath ParameterType="System.String" Required="true" />
<sBranchName ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
... some Code ...
]]>
</Code>
</Task>
</UsingTask>
<Target Name="GetBranchName_TARGET">
<GetBranchName_TASK sPath="$(MSBuildThisFileDirectory)">
<Output PropertyName="BranchName" TaskParameter="sBranchName" />
</GetBranchName_TASK>
<Message Importance="High" Text="BranchName = $(BranchName)" />
</Target>
<PropertyGroup>
<BuildDependsOn>
GetBranchName_TARGET;
$(BuildDependsOn);
</BuildDependsOn>
</PropertyGroup>
</Project>
My *.props File is like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Configuration">
... some Properties here ...
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="IRSGetBranchName.targets" />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalOptions>/DBRANCHNAME=$(BranchName) /DMORE=BAR</AdditionalOptions>
<ClCompile>
<ItemDefinitionGroup>
</Project>
This .props File then is imported into several .vcxproj
The Value printed as Message in my GetBranchName_TARGET is correct as expected (showing the correct TFS-Branch Name).
But when looking at Detailed Build Output, the Value seems empty:
1>ClCompile
1> ..\FOO.cpp
1> AdditionalOptions = /DBRANCHNAME= /DMORE=BAR
I tried for hours but found no solution and I really hope someone help whats wrong here ...
a) Is the Property BranchName not available globally? I tried to print the Property from other custom Targets and it worked well!
b) Or is the ClCompile.AdditionalOptions evaluated/build before my Target is excuted? In this case how can I re-evaluate?
c) ...
I'am very thankful for any Input.
You should be familiar with the msbuild evaluation process, as described here:
When the MSBuild engine begins to process a build file, it is evaluated in a top-down fashion in a multi-pass manner. These passes are described in order in the following list:
Load all environment and global properties, and toolset properties. In Microsoft Visual Studio 2010, for example, C++ defines several properties in the MSBuild 4.0 toolset.
Evaluate properties and process imports as encountered
Evaluate item definitions
Evaluate items
Evaluate using tasks
Start build and reading targets
So, in your case, the ItemDefinitionGroup for ClCompile has been evaluated before the GetBranchName_TARGET has been executed. So, it is empty by design.
In order to achieve the desired behavior, you should Add the following:
<Target Name="GetBranchName_TARGET">
<GetBranchName_TASK sPath="$(MSBuildThisFileDirectory)">
<Output PropertyName="BranchName" TaskParameter="sBranchName" />
</GetBranchName_TASK>
<Message Importance="High" Text="BranchName = $(BranchName)" />
<ItemGroup>
<ClCompile>
<AdditionalOptions>/DBRANCHNAME=$(BranchName) /DMORE=BAR</AdditionalOptions>
</ClCompile>
</ItemGroup>
</Target>
You can use a Condition attribute in the ClCompile in order to include only your sources, for example. Actually, what you are looking for is the feature to modify item metadata after it was declared.

MSBuild MSBuildCommunityTasks Task Time

I have a MSBuild project and I want the current date to be added to a zip file that I am creating.
I am using the MSBuildCommunityTasks.
<!-- Import the CommunityTasks Helpper -->
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
On the website http://msbuildtasks.tigris.org/ I can see a task called time. I have not been able to find doc on how to use Time.
In msbuild 4 you can now
$([Namespace.Type]::Method(..parameters…))
$([Namespace.Type]::Property)
$([Namespace.Type]::set_Property(value))
so I am using
$([System.DateTime]::Now.ToString(`yyyy.MMdd`))
those ticks around the format are backticks not '
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<!-- Include MSBuild tasks here -->
<ItemGroup>
<DefaultExclude Include="****" />
</ItemGroup>
<Target Name="Deploy" >
<Time Format="yyyy-MM-dd">
<Output TaskParameter="FormattedTime" PropertyName="buildDate" />
</Time>
<Message Text="Deploying ...."></Message>
<Copy SourceFiles="#(DeploymentFiles)" DestinationFolder="C:\CCNET\$(buildDate)\bin\" />
</Target>
</Project>
Maslow's answer is correct (I can't comment on it or I would); I would only add to it that you have to be careful when implicitly calling System.DateTime.Parse.
A parsed string value like $([System.DateTime]::Parse("1970-01-01T00:00:00.0000000Z") doesn't seem to end up with a Kind of DateTimeKind.Utc.
But you can use nested property functions to make it work; like this (to get the Unix timestamp):
$([System.DateTime]::UtcNow.Subtract($([System.DateTime]::Parse("1970-01-01T00:00:00.0000000Z").ToUniversalTime())).TotalSeconds.ToString("F0"))

Updating Assembly information with MSBuild failing

All
i am trying to automatically update the assembly information of a project using AssemblyInfo task, before build however the target appears to do nothing (no failure/error) just no update/creation
Below is the build.proj file I am using (obviously some contents altered)
Can anyone help?
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\AssemblyInfoTask\Microsoft.VersionNumber.targets"/>
<PropertyGroup>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision>
</PropertyGroup>
<PropertyGroup>
<BuildDir>C:\svn\Infrastructure</BuildDir>
</PropertyGroup>
<ItemGroup>
<SolutionsToBuild Include="Infrastructure.sln"/>
</ItemGroup>
<Target Name="Build" DependsOnTargets="ChangeDataAccessAssemblyInfo">
<RemoveDir Directories="$(BuildDir)\Builds" Condition="Exists('$(BuildDir)\Builds')" />
<MSBuild Projects="#(SolutionsToBuild)" Properties="Configuration=Debug" Targets="Rebuild" />
</Target>
<ItemGroup>
<TestAssemblies Include="Build\Logging\Logging.UnitTests.dll" />
</ItemGroup>
<!--<UsingTask TaskName="NUnit" AssemblyFile="$(teamcity_dotnet_nunitlauncher_msbuild_task)" />
<Target Name="Test" DependsOnTargets="Build">
<NUnit NUnitVersion="NUnit-2.4.6" Assemblies="#(TestAssemblies)" />
</Target>-->
<Target Name="ChangeDataAccessAssemblyInfo" >
<Message Text="Writing ChangeDataAccessAssemblyInfo file for 1"/>
<Message Text="Will update $(BuildDir)\DataAccess\My Project\AssemblyInfo.vb" />
<AssemblyInfo CodeLanguage="VB"
OutputFile="$(BuildDir)\DataAccess\My Project\AssemblyInfo_new.vb"
AssemblyTitle="Data Access Layer"
AssemblyDescription="Message1"
AssemblyCompany="http://somewebiste"
AssemblyProduct="the project"
AssemblyCopyright="Copyright notice"
ComVisible="true"
CLSCompliant="true"
Guid="hjhjhkoi-9898989"
AssemblyVersion="$(Major).$(Minor).1.1"
AssemblyFileVersion="$(Major).$(Minor).5.7"
Condition="$(Revision) != '0' "
ContinueOnError="false" />
<Message Text="Updated Assembly File Info"
ContinueOnError="false"/>
</Target>
</Project>
I think you are missing the specification of the AssemblyInfoFiles attribute on your AssemblyInfo task. Here's how it looks on a project I'm working on...
<Target Name="AfterGet">
<Message Text="In After Get"/>
<CreateItem Include="$(SolutionRoot)\Source\SomeProject\My Project\AssemblyInfo.vb">
<Output ItemName="AssemblyInfoFiles" TaskParameter="Include"/>
</CreateItem>
<Attrib Files="#(AssemblyInfoFiles)"
ReadOnly="false"/>
<AssemblyInfo AssemblyInfoFiles="#(AssemblyInfoFiles)"
AssemblyDescription="$(LabelName)">
</AssemblyInfo>
</Target>
What we're doing is first using to create a property that contains the name of the file we'll be updating. We have to do this via createItem because when we start the build the file doesn't exist (and that is when MSBuild evaluates the and definitions in your build file.
We then take the readonly bit off the file.
Finally we invoke the AssemblyInfo task passing it the file(s) to update and a custom assembly name that we want to give it (in this case we put the TFS build label into the Assembly Description field so that we can easily tell which team build the assembly came from.