I need to change only the revision number of an AssemblyInfo.cs file. The version number is in the format Major.Minor.Build.Revision e.g. 1.4.6.0.
Currently I change the version with the FileUpdate task (from the MSBuild Community Tasks Project) and the following regex:
<FileUpdate Files="#(AssemblyResult)"
Regex='(\[\s*assembly:\s*AssemblyVersion\(\s*"[^\.]+\.[^\.]+)\.([^\.]+)(\.)([^\.]+)("\)\s*\])'
ReplacementText='[assembly: AssemblyVersion("$(AssemblyMajorNumber).$(AssemblyMinorNumber).$(AssemblyBuildNumber).$(Revision)")]' />
Now I need to update only the revision number and leave major,minor and build unchanged.
So, is there any task to do this? Or can it be done with a regex? What would be the regular expression then?
How about this:
<FileUpdate Files="Properties/AssemblyInfo.cs"
Regex="(\d+)\.(\d+)\.(\d+)\.(\d+)"
ReplacementText="$1.$2.$3.$(Revision)" />
I use the following target to do this:
<Target Name="UpdateAssemblyInfoVersion" DependsOnTargets="GetRevision">
<CreateItem Include="**\AssemblyInfo.vb">
<Output TaskParameter="Include" ItemName="AssemblyFiles"/>
</CreateItem>
<Time>
<Output TaskParameter="Year" PropertyName="Year" />
</Time>
<FileUpdate Files="#(AssemblyFiles)"
Multiline="true"
Singleline="false"
Regex="(AssemblyVersion|AssemblyFileVersionAttribute|AssemblyFileVersion)\("([0-9]+\.[0-9]+\.[0-9]+)(\.[0-9]+)?"\)"
ReplacementText="$1("$2.$(Revision)")" />
<FileUpdate Files="#(AssemblyFiles)"
Multiline="true"
Singleline="false"
Regex="AssemblyCompany\(".*"\)"
ReplacementText="AssemblyCompany("My Company")" />
<FileUpdate Files="#(AssemblyFiles)"
Multiline="true"
Singleline="false"
Regex="AssemblyCopyright\(".*"\)"
ReplacementText="AssemblyCopyright("Copyright © 2009-$(Year) My Company")" />
</Target>
This replaces the revision (4th number) in any of the AssemblyInfo files (in multiple projects). It looks at the AssemblyVersion AssemblyFileVersionAttribute and AssemblyFileVersion tags, and uses the $(Revision) MSBuild property for the number (I have another target called GetRevision that gets this from SVN and sets the property, so this one depends on that target). The regex replacement handles version numbers that have either 3 or 4 digits (I had a bunch with 3 only, for whatever reason).
It also sets/overwrites the Company and Copyright information, and sets it to "My Company". For copyright, I was lazy and made it so it always uses the current year so I don't have to remember to update it every year (so it says eg "Copyright (c) 2009-2010 My Company").
This target requires the MSBuild Community tasks extension.
As a matter of policy, everything checked into SVN has .0 as the last number, and only the CI server changes this value when it's doing a build. This lets us quickly tell the difference between developer-created builds (which are never allowed to go to customers) and "official" builds created by the CI server.
Related
I want to define a Timestamp property and use it to drive the value of several other properties, as the intent is shown below:
<PropertyGroup>
<TrapHouseBuildToolsPath>..\TrapHouse.Build.NetStandard\bin\$(Configuration)\netstandard2.0\publish\TrapHouse.Build.NetStandard.dll</TrapHouseBuildToolsPath>
<Timestamp>$([System.DateTime]::Now.ToUniversalTime()))</Timestamp>
<DeploymentEnvironment>LOCALDEV</DeploymentEnvironment>
<TrapHouseVersion>$(DeploymentEnvironment)$([System.String]::Format("{0:yyyyMMddHHmmss}", Timestamp)</TrapHouseVersion>
</PropertyGroup>
In one of my tasks I spit out the value of TrapHouseVersion, which is not what I expect:
4.5.0-LOCALDEV$([System.String]::Format("{0:yyyyMMddHHmmss}", Timestamp)
It seems pretty clear that I am not getting the syntax right, but I have had difficulty finding good resources for this sort of thing.
What do I need to do to get the TrapHouseVersion to evaluate properly?
First, you are missing a closing brace at the end, but also know that the property isn't persisted as DateTime but properties are only stored as strings, so you can't use String.Format later on (except for parsing $(Timestamp) to a DateTime again):
<Project DefaultTargets="PrintVersion">
<PropertyGroup>
<Timestamp>$([System.String]::Format("{0:yyyyMMddHHmmss}", $([System.DateTime]::Now.ToUniversalTime())))</Timestamp>
<DeploymentEnvironment>LOCALDEV</DeploymentEnvironment>
<TrapHouseVersion>$(DeploymentEnvironment)$(Timestamp)</TrapHouseVersion>
</PropertyGroup>
<Target Name="PrintVersion">
<Message Importance="high" Text="TrapHouseVersion: $(TrapHouseVersion)" />
</Target>
</Project>
When run:
$ dotnet msbuild
Microsoft (R) Build Engine version 15.8.169+g1ccb72aefa for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.
TrapHouseVersion: LOCALDEV20181012171759
I'm a competent user of GNU make on Unix, tasked with writing a build system using MSBuild on Windows 7. (Cygwin is not an option)
The MSBuild documentation on partial incremental builds states that:
MSBuild attempts to find a 1-to-1 mapping between the values of [the Inputs and Outputs attributes].
[...]
1-to-1 mappings are typically produced by item transformations.
If 1-to-1 mappings are typically produced by item transformations, are there any other ways in which they may they be produced?
The page contains the following example target:
<Target Name="Backup" Inputs="#(Compile)"
Outputs="#(Compile->'$(BackupFolder)%(Identity).bak')">
<Copy SourceFiles="#(Compile)" DestinationFiles=
"#(Compile->'$(BackupFolder)%(Identity).bak')" />
</Target>
It bothers me that the transformation from the #(Compile) Item to the set of .bak files is duplicated. It appears once in the Target's Outputs attribute, and once in the Copy Task's DestinationFiles attribute.
I'd like to be able to specify the transformation just once, preferably declaring an Item for the backup files. I might want to use the same Item in other Tasks and Targets. For example:
<ItemGroup>
<BackupFiles Include="#(Compile->'$(BackupFolder)%(Identity).bak')" />
</ItemGroup>
<Target Name="Backup" Inputs="#(Compile)" Outputs="#(BackupFiles)">
<Copy SourceFiles="#(Compile)" DestinationFiles="#(BackupFiles)" />
</Target>
Then I could use #(BackupFiles) as the Outputs of the Target, the DestinationFiles of the Copy, and potentially elsewhere too. However, if I do this then partial incremental building stops happening. If all outputs are up to date then the target is correctly skipped, but if some outputs are out of date then the target is run for all inputs. It appears that the 1-to-1 mapping between #(Compile) and #(BackupFiles) is not identified when the target is run.
My questions are:
Can MSBuild be instructed that a 1-to-1 mapping exists between a Target's Inputs and Outputs attributes when it has failed to spot one itself, such as #(Compile) and #(BackupFiles) above?
Is there an equivalent to make's $^, $< and $# values. That is; the inputs and outputs of the current target? In particular I'd like to use something like $# as the DestinationFiles of the Copy task. That is; "Whatever you identified as the output; use that".
PS: I have the "Using MSBuild and Team Foundation Build" book (2nd edition), if anybody wishes to refer to it.
I want to write a number in a text file using WriteLinesToFile but the task is putting a line feed at the end which causes me trouble when i want to read or combine in other places
Example:
<WriteLinesToFile File="$(TextFile)" Lines="#(BuildNumber)" Overwrite="true"/>
UPDATE as the user comment below:
The problem that I had was that I was using a very simple command in Property to read the content of a file $([System.IO.File]::ReadAllText("$(TextFile)")) and I really want to use this one but it also included the line feed from WriteLinesToFiles. I ended up using similar solution like yours using ReadLinesFromFile.
There is a slight dissconnect between the title and the description. I would have liked to post this "answer" as an edit, but do not have enough reputation points :)
Do you have a problem with the newline at the end of a file, or do you have a problem ignoring that newline? Could you please clarify?
One way how I suppose you could ignore that newline follows.
This small snippet of code writes a build number to a file, then reads it out and then increments the number read by 1.
<Target Name="Messing_around">
<PropertyGroup>
<StartBuildNumber>1</StartBuildNumber>
</PropertyGroup>
<ItemGroup>
<AlsoStartBuildNumber Include="1"/>
</ItemGroup>
<!-- Use a property
<WriteLinesToFile File="$(ProjectDir)test.txt" Lines="$(StartBuildNumber)" Overwrite="true"/>
-->
<WriteLinesToFile File="$(ProjectDir)test.txt" Lines="#(AlsoStartBuildNumber)" Overwrite="true"/>
<ReadLinesFromFile File="$(ProjectDir)test.txt">
<Output
TaskParameter="Lines"
ItemName="BuildNumberInFile"/>
</ReadLinesFromFile>
<PropertyGroup>
<OldBuildNumber>#(BuildNumberInFile)</OldBuildNumber>
<NewBuildNumber>$([MSBuild]::Add($(OldBuildNumber), 1))</NewBuildNumber>
</PropertyGroup>
<Message Importance="high" Text="Stored build number: #(BuildNumberInFile)"/>
<Message Importance="high" Text="New build number: $(NewBuildNumber)"/>
</Target>
And this is what I see
Output:
1>Build started xx/xx/xxxx xx:xx:xx.
1>Messing_around:
1> Stored build number: 1
1> New build number: 2
1>
1>Build succeeded.
If you attempting to read, in an MSBuild Task, a single line containing only a number from a file with a trailing line feed, then you should not have a problem.
As a side note: With the little information at hand I'd assume that BuildNumber is an Item in an ItemGroup. If you have only one build number to deal with, perhaps Property may have been an option. But then, again, I haven't been tinkering with MSBuild for too long. So, I am open to feedback on the Item vs Property issue.
I'm trying to combine all javascript files in a project during the build process, but it just isn't working for me. Here's what I have:
<Target Name="CombineJS">
<CreateItem Include=".\**\*.js">
<Output TaskParameter="Include" ItemName="jsFilesToCombine" />
</CreateItem>
<ReadLinesFromFile File="#(jsFilesToCombine)">
<Output TaskParameter="Lines" ItemName="jsLines" />
</ReadLinesFromFile>
<WriteLinesToFile File="all.js" Lines="#(jsLines)" Overwrite="true" />
</Target>
MSBuild is throwing an error on the ReadLinesFromFile line saying there's an invalid value for the "File" parameter. (No error when there's only one file to combine)
So, two questions:
What am I doing wrong?
Is there a better way to combine files within an MSBuild task? I ask this question because I know that my current process removes all tabs and blank lines, which for me isn't that big of a deal, but still kind of annoying.
Change line 6 to:
<ReadLinesFromFile File="%(jsFilesToCombine.FullPath)">
The # operator is used when the input is ItemGroup which is essentially a semicolon-delimited list of strings.
The % operator is for expanding ItemGroups into strings (properties).
The ReadLinesFromFileTask you are using to read the files takes a single file as input to the File property (MSDN). You can't use this task to read lines from multiple files at once. You may however be able to use batching to run the task several times for each file.
I am trying to setup some properties that I use multiple times in my MSBuild script. I have the following property section:
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn); MyAfterBuild </BuildDependsOn>
<SubstitutionsFilePath>$(ProjectDir)app.config.substitutions.xml </SubstitutionsFilePath>
<AppConfig>$(TargetPath).config</AppConfig>
<HostConfig>$(TargetDir)$(TargetName).vshost.exe.config</HostConfig>
</PropertyGroup>
When I run this I get the following error:
The expression "#(TargetPath).config" cannot be used in this context. Item lists cannot be concatenated with other strings where an item list is expected. Use a semicolon to separate multiple item lists.
I don't understand this error, as the use of the $(BuildDependsOn) and $(ProjectDir) work fine. And I know the $(TargetXXX) values generate properly as when I put them directly into the Tasks section below, they work fine.
The reason for this problem is that TargetDir is defined as an item list, not a property; presumably to cater to the scenario where your outputs are distributed amongst several output directories?
I came up against this same problem and managed to work around it by using the $(OutDir) property instead of $(TargetDir).
(The OutDir property is defined in Microsoft.Common.Targets (lines 100-102) as a normalised version of the OutputPath defined in your project file.)
First try running your build with the /v:diag option, which will output a lot more information and give you a clue as to what part of the build is failing.
A clue might be in the Microsoft.Common.targets file (located in %SystemRoot%\Microsoft.NET\Framework\v2.0.50727) in the PrepareForBuild target:
<!--
These CreateProperty calls are required because TargetDir and TargetPath are defined
to contain an item list. We want that item list to be expanded so that it can be used
as a regular property value and not as an item-list-with-transform.
-->
<CreateProperty Value="$(TargetDir)">
<Output TaskParameter="Value" PropertyName="TargetDir" />
</CreateProperty>
<CreateProperty Value="$(TargetPath)">
<Output TaskParameter="Value" PropertyName="TargetPath" />
</CreateProperty>
To me this looks like a bug, you can report it at https://connect.microsoft.com/feedback/Search.aspx?SiteID=210.