I have spent the better part of a few hours trying to find a way to auto-increment versions in a .NETCoreApp 1.1 (Visual Studio 2017).
I know the the AssemblyInfo.cs is being created dynamically in the folder: obj/Debug/netcoreapp1.1/
It does not accept the old method of:
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.*")]
If I set the project to package I can set versions there but this seems to be used to build the AssemblyInfo.cs file.
My question is, has anyone figured out how to control version in .NET Core (or .NETStandard for that matter) projects.
Add <Deterministic>False</Deterministic> inside a <PropertyGroup> section of .csproj
The workaround to make AssemblyVersion * working is described in “Confusing error message for wildcard in [AssemblyVersion] on .Net Core #22660”
Wildcards are only allowed if the build is not deterministic, which
is the default for .Net Core projects.
Adding <Deterministic>False</Deterministic> to csproj fixes the
issue.
The reasons why .Net Core Developers consider Deterministic Builds beneficial described in http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html
and Compilers should be deterministic: same inputs generate same outputs #372
However if you are using TeamCity, TFS or other CI/CD tool, it's probably better to keep the version number controlled and incremented by them and pass to build as a parameter (as it was suggested in other answers) , e.g.
msbuild /t:build /p:Version=YourVersionNumber /p:AssemblyVersion=YourVersionNumber
Package number for NuGet packages
msbuild /t:pack /p:Version=YourVersionNumber
If you're using Visual Studio Team Services/TFS or some other CI build process to have versioning built-in, you can utilize msbuild's Condition attribute, for example:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">0.0.1-local</Version>
<Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(BUILD_BUILDNUMBER)</Version>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
</ItemGroup>
</Project>
This will tell the .NET Core compiler to use whatever is in the BUILD_BUILDNUMBER environment variable if it's present, or fallback to 0.0.1-local if you're doing a build on your local machine.
I have been looking for a version incrementer for a .NET Core app in VS2017 using the csproj configuration format.
I found a project called dotnet bump that worked for the project.json format but struggled to find a solution for the .csproj format. The writer of dotnet bump actually came up with the solution for the .csproj format and it is called MSBump.
There is a project on GitHub for it at:
https://github.com/BalassaMarton/MSBump
where you can see the code and it's available on NuGet too. Just search for MSBump on Nuget.
You can use a MSBuild property function to set the version suffix based on current date:
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<VersionSuffix>pre$([System.DateTime]::UtcNow.ToString(yyyyMMdd-HHmm))</VersionSuffix>
</PropertyGroup>
This will output a package with a name like: PackageName.1.0.0-pre20180807-1711.nupkg.
More details about MSBuild property functions: https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions
The Version is formed from the combination of VersionPrefix and VersionSuffix, or if VersionSuffix is blank, VersionPrefix only.
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
</PropertyGroup>
I came up with a solution that worked almost the same as old AssemblyVersion attribute with star (*) - AssemblyVersion("1.0.*")
Values for AssemblyVersion and AssemblyFileVersion is in MSBuild project .csproj file (not in AssemblyInfo.cs) as property FileVersion (generates AssemblyFileVersionAttribute) and AssemblyVersion (generates AssemblyVersionAttribute).
In MSBuild process we use our custom MSBuild task to generate version numbers and then we override values of these FileVersion and AssemblyVersion properties with new values from task.
So first, we create our custom MSBuild task GetCurrentBuildVersion:
public class GetCurrentBuildVersion : Task
{
[Output]
public string Version { get; set; }
public string BaseVersion { get; set; }
public override bool Execute()
{
var originalVersion = System.Version.Parse(this.BaseVersion ?? "1.0.0");
this.Version = GetCurrentBuildVersionString(originalVersion);
return true;
}
private static string GetCurrentBuildVersionString(Version baseVersion)
{
DateTime d = DateTime.Now;
return new Version(baseVersion.Major, baseVersion.Minor,
(DateTime.Today - new DateTime(2000, 1, 1)).Days,
((int)new TimeSpan(d.Hour, d.Minute, d.Second).TotalSeconds) / 2).ToString();
}
}
Task class inherit from Microsoft.Build.Utilities.Task class from Microsoft.Build.Utilities.Core NuGet package.
It takes BaseVersion property (optional) on input and returns generated version in Version output property. The logic to get version numbers is same as .NET automatic versioning (Build number is days count since 1/1/2000 and Revision is half seconds since midnight).
To build this MSBuild task, we use .NET Standard 1.3 class library project type with this class.
.csproj file can looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<AssemblyName>DC.Build.Tasks</AssemblyName>
<RootNamespace>DC.Build.Tasks</RootNamespace>
<PackageId>DC.Build.Tasks</PackageId>
<AssemblyTitle>DC.Build.Tasks</AssemblyTitle>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.1012" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.1.1012" />
</ItemGroup>
</Project>
This task project is also available in my GitHub holajan/DC.Build.Tasks
Now we setup MSBuild to use this task and set FileVersion and AssemblyVersion properties.
In .csproj file, it looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<UsingTask TaskName="GetCurrentBuildVersion" AssemblyFile="$(MSBuildThisFileFullPath)\..\..\DC.Build.Tasks.dll" />
<PropertyGroup>
...
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
</PropertyGroup>
...
<Target Name="BeforeBuildActionsProject1" BeforeTargets="BeforeBuild">
<GetCurrentBuildVersion BaseVersion="$(FileVersion)">
<Output TaskParameter="Version" PropertyName="FileVersion" />
</GetCurrentBuildVersion>
<PropertyGroup>
<AssemblyVersion>$(FileVersion)</AssemblyVersion>
</PropertyGroup>
</Target>
</Project>
Important things here:
Mentioned UsingTask imports GetCurrentBuildVersion task from DC.Build.Tasks.dll. It assumes that this dll file is located on parent directory from your .csproj file.
Our BeforeBuildActionsProject1 Target that calls task must have unique name per project in case we have more projects in the solution which calls GetCurrentBuildVersion task.
The advantage of this solution is that it works not only from builds on build server, but also in manual builds from dotnet build or Visual Studio.
I accepted the above answer because #Gigi is correct (as of now) but I was annoyed and came up with the following PowerShell Scripts.
First I have the script in my solution folder (UpdateBuildVersion.ps1):
#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"
#Read csproj (XML)
$xml = [xml](Get-Content $path)
#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion
#Split the Version Numbers
$avMajor, $avMinor, $avBuild = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")
#Increment Revision
$avBuild = [Convert]::ToInt32($avBuild,10)+1
$fvBuild = [Convert]::ToInt32($fvBuild,10)+1
#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"
#Save csproj (XML)
$xml.Save($path)
I added this to csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AssemblyVersion>0.0.1</AssemblyVersion>
<FileVersion>0.0.1</FileVersion>
<PreBuildEvent>powershell.exe –NonInteractive –ExecutionPolicy Unrestricted -command "& {$(SolutionDir)UpdateBuildVersion.ps1}"</PreBuildEvent>
</PropertyGroup>
</Project>
Even through its set to be a PreBuildEvent, the fact is the version numbers do not get updated until AFTER the file has been loaded into memory so the version number will not reflect until the next build. In fact, you could change it to a PostBuildEvent and it would have the same effect.
I also created the following two scripts:
(UpdateMinorVersion.ps1)
#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"
#Read csproj (XML)
$xml = [xml](Get-Content $path)
#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion
#Split the Version Numbers
$avMajor, $avMinor, $avBuild = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")
#Increment Minor Version - Will reset all sub nodes
$avMinor = [Convert]::ToInt32($avMinor,10)+1
$fvMinor = [Convert]::ToInt32($fvMinor,10)+1
$avBuild = 0
$fvBuild = 0
#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"
#Save csproj (XML)
$xml.Save($path)
(UpdateMajorVersion.ps1)
#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"
#Read csproj (XML)
$xml = [xml](Get-Content $path)
#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion
#Split the Version Numbers
$avMajor, $avMinor, $avBuild = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")
#Increment Major Version - Will reset all sub nodes
$avMajor = [Convert]::ToInt32($avMajor,10)+1
$fvMajor = [Convert]::ToInt32($fvMajor,10)+1
$avMinor = 0
$fvMinor = 0
$avBuild = 0
$fvBuild = 0
#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"
#Save csproj (XML)
$xml.Save($path)
You could do it like below, within the csproj file. I didn't figure out the math. I found that somewhere else on Stack Overflow, but this works and will give you something similiar to 1.0.* for version.
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<FileVersion>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays).$([System.Math]::Floor($([MSBuild]::Divide($([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds), 1.32))))</FileVersion>
<Version>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays)</Version>
</PropertyGroup>
These values are now set in the .csproj file:
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyVersion>1.0.6.0</AssemblyVersion>
<FileVersion>1.0.6.0</FileVersion>
<Version>1.0.1</Version>
</PropertyGroup>
These are the same values you see if you go in the Package tab in the project settings. While I don't think you can use * to autoincrement the version, what you can do is introduce a post-processing step that replaces the versions for you (e.g. as part of your continuous integration).
has anyone figured out how to control version in .NET Core (or .NETStandard for that matter) projects.
Use:
dotnet build /p:AssemblyVersion=1.2.3.4
I found this question trying to solve this problem in the context of a CI build. I wanted to set the assembly version to the CI build number.
To summaries all up above: your can revert to old AssemblyInfo.cs behavior with this:
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>false</Deterministic>
But this approach is not recommended, because turning off GenerateAssemblyInfo can lead to problems with infra, for example.
More selective approach:
<Deterministic>false</Deterministic>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<AssemblyVersion>1.2.*</AssemblyVersion>
and you don't need AssemblyInfo.cs any more.
I made a simple CLI tool for setting .csproj .NET Core version strings here. You can combine it with tools like GitVersion for automatic version bumping during CI builds, if that's what you're after.
Thanks to #joelsand for pointing me in the right direction.
I had to change his answer slightly as when the DevOps Build ran, I got the following exception
The specified version string does not conform to the recommended format - major.minor.build.revision
I had to add the $(BUILD_BUILDNUMBER) at the end of major.minor.build section. To de-duplicate the actual version, I also use a version-prefix:
<PropertyGroup>
<VersionPrefix>1.0.3</VersionPrefix>
<Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">$(VersionPrefix)-local</Version>
<Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(VersionPrefix)-$(BUILD_BUILDNUMBER)</Version>
</PropertyGroup>
What worked for me was to define Patch and Revision using a PropertyGroup, then you can just use this variables for version (and prefix if needed). Version numbers must be short numbers so I use YearMonth for Patch, and MinutesOfDay for Revision. Add this lines to your csproj file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VersionMajor>0</VersionMajor>
<VersionMinor>9</VersionMinor>
<VersionPatch Condition="'$(VersionPatch)' == ''">$([System.DateTime]::UtcNow.ToString("yyMM"))</VersionPatch>
<VersionRevision Condition="'$(VersionRevision)' == ''">$([System.DateTime]::UtcNow.TimeOfDay.TotalMinutes.ToString("0"))</VersionRevision>
</PropertyGroup>
<PropertyGroup>
<OutputType>...</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Title>Software Title</Title>
<Description>...</Description>
<Authors>...</Authors>
<Version>$(VersionMajor).$(VersionMinor).$(VersionPatch).$(VersionRevision)</Version>
</PropertyGroup>
....
</Project>
It can be achive in a generic way making use of Directory.build.props file. More info here: https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019
Just add a file with this name in the project folder and place there these lines.
I came across here searching for a solution for shared projects. In my case, I solved it adding a Version.build.props file in my shared project with the structure shown above, and just one new line at any csproj file for projects using my shared code:
<!-- Shared project import -->
<Import Project="..\Shared\Shared.projitems" Label="Shared" />
<!-- Version number generator -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Version.Build.props', '$(MSBuildThisFileDirectory)../Shared/'))" />
I'll left this code here just in case someone needs it.
*Solution tested for .Net5 but should works for earlier versions.
<PropertyGroup>
<SecondsSinceEpoch>$([System.DateTime]::UtcNow.Subtract($([System.DateTime]::MinValue)).TotalSeconds)</SecondsSinceEpoch>
<Revision>$([System.Math]::Truncate($([System.Decimal]::Remainder($(SecondsSinceEpoch), 100000))))</Revision>
<Version>1.7.0.$(Revision)</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
</PropertyGroup>
My take on setting a decent value via .csproj. Unfortunately if your next rebuild is an interval of 100000 seconds later it will be the same value. Better than MSBump making every Build a Rebuild though.
Can use TotalMinutes, TotalDays, etc. if slow or automated builds.
To enable versioning of your .NET Core / .NET Whatever project based on your GIT setup, using the tags/describe functionality of GIT.
I have been using a Prebuild.targets.xml file which is located in the root folder for the project and included in the csproj file like:
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="PreBuild.targets.xml" />
...
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
Use the "GenerateAssembyInfo" tag to disable automatic assembly info generation.
Then the Prebuild.targets.xml will generate a CommonAssemblyInfo.cs file where you can include the version tags you want based on your GIT version
NOTE: I have found the Prebuilds.targets.xml somewhere else, so haven't bothered cleaning it up .)
The Prebuild.targets.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask
TaskName="GetVersion"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<VersionString ParameterType="System.String" Required="true" />
<Version ParameterType="System.String" Output="true" />
<Commit ParameterType="System.String" Output="true" />
<VersionSuffix ParameterType="System.String" Output="true" />
</ParameterGroup>
<Task>
<!--<Reference Include="" />-->
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Text.RegularExpressions" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var match = Regex.Match(VersionString, #"^(?<major>\d+)\.(?<minor>\d+)(\.?(?<patch>\d+))?-(?<revision>\d+)-(?<commit>[a-z0-9-]+)$");
int major, minor, patch, revision;
Int32.TryParse(match.Groups["major"].Value, out major);
Int32.TryParse(match.Groups["minor"].Value, out minor);
Int32.TryParse(match.Groups["patch"].Value, out patch);
Int32.TryParse(match.Groups["revision"].Value, out revision);
_Version = new Version(major, minor, patch, revision).ToString();
_Commit = match.Groups["commit"].Value;
]]>
</Code>
</Task>
</UsingTask>
<UsingTask
TaskName="GitExistsInPath"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<Exists ParameterType="System.Boolean" Output="true" />
</ParameterGroup>
<Task>
<!--<Reference Include="" />-->
<Using Namespace="System"/>
<Using Namespace="System.IO"/>
<Using Namespace="System.Text.RegularExpressions" />
<Code Type="Fragment" Language="cs">
<![CDATA[
var values = Environment.GetEnvironmentVariable("PATH");
foreach (var path in values.Split(';')) {
var exeFullPath = Path.Combine(path, "git.exe");
if (File.Exists(exeFullPath)) {
Exists = true;
return true;
}
var cmdFullPath = Path.Combine(path, "git.cmd");
if (File.Exists(cmdFullPath)) {
Exists = true;
return true;
}
}
Exists = false;
]]>
</Code>
</Task>
</UsingTask>
<Target Name="CreateCommonVersionInfo" BeforeTargets="CoreCompile">
<Message Importance="high" Text="CreateCommonVersionInfo" />
<GitExistsInPath>
<Output TaskParameter="Exists" PropertyName="GitExists"/>
</GitExistsInPath>
<Message Importance="High" Text="git not found!" Condition="!$(GitExists)"/>
<Exec Command="git describe --tags --long --dirty > $(ProjectDir)version.txt" Outputs="$(ProjectDir)version.txt" WorkingDirectory="$(SolutionDir)" IgnoreExitCode="true" Condition="$(GitExists)">
<Output TaskParameter="ExitCode" PropertyName="ExitCode" />
</Exec>
<Message Importance="high" Text="Calling git failed with exit code $(ExitCode)" Condition="$(GitExists) And '$(ExitCode)'!='0'" />
<ReadLinesFromFile File="$(ProjectDir)version.txt" Condition="$(GitExists) And '$(ExitCode)'=='0'">
<Output TaskParameter="Lines" ItemName="OutputLines"/>
</ReadLinesFromFile>
<Message Importance="High" Text="Tags: #(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'"/>
<Delete Condition="Exists('$(ProjectDir)version.txt')" Files="$(ProjectDir)version.txt"/>
<GetVersion VersionString="#(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'">
<Output TaskParameter="Version" PropertyName="VersionString"/>
<Output TaskParameter="Commit" PropertyName="Commit"/>
</GetVersion>
<PropertyGroup>
<VersionString Condition="'$(VersionString)'==''">0.0.0.0</VersionString>
</PropertyGroup>
<Message Importance="High" Text="Creating CommonVersionInfo.cs with version $(VersionString) $(Commit)" />
<WriteLinesToFile Overwrite="true" File="$(ProjectDir)CommonAssemblyInfo.cs" Encoding="UTF-8" Lines='using System.Reflection%3B
// full version: $(VersionString)-$(Commit)
[assembly: AssemblyVersion("$(VersionString)")]
[assembly: AssemblyInformationalVersion("$(VersionString)")]
[assembly: AssemblyFileVersion("$(VersionString)")]' />
</Target>
</Project>
EDIT: If you are building using MSBUILD the
$(SolutionDir)
Might cause you trouble, use
$(ProjectDir)
instead
As an alternative, you can try fixed major number with a suffix based on current date:
<PropertyGroup>
<VersionPrefix>1</VersionPrefix>
<VersionSuffix>$([System.DateTime]::UtcNow.ToString(yyMM)).$([System.DateTime]::UtcNow.ToString(ddHH)).$([System.DateTime]::UtcNow.ToString(mmss))</VersionSuffix>
<Version Condition=" '$(VersionSuffix)' == '' ">$(VersionPrefix).0.0.1</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
</PropertyGroup>
We can use special parameter for dotnet publish -- version-suffix 1.2.3
For file version:
<AssemblyVersion Condition=" '$(VersionSuffix)' == '' ">0.0.1.0</AssemblyVersion>
<AssemblyVersion Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>
For version:
<Version Condition=" '$(VersionSuffix)' == '' ">0.0.1</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>
https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21
--version-suffix <VERSION_SUFFIX> Defines the value for the $(VersionSuffix) property in the project.
I think this Answer from #joelsand is the correct answer for setting version number for dotnet core running on VSTS
To add more information for this answer,
BUILD_BUILDNUMBER is actually a predefined variable.
It turns out there are 2 versions of predefined variable.
One is build.xxxx, the other is BUILD_XXXX.
You can only use Environment Variable Name in cproj.
My OSS project "RelaxVersioner" can full automatic insert with the attributes and constatnt literals on git repository only NuGet package installed without any tool-depended operation.
Example for applied information:
sing System.Reflection;
[assembly: AssemblyVersion("1.0.21")]
[assembly: AssemblyFileVersion("2020.12.20.33529")]
[assembly: AssemblyInformationalVersion("1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a")]
[assembly: AssemblyMetadata("Date","Sun, 20 Dec 2020 09:37:39 GMT")]
[assembly: AssemblyMetadata("Branch","master")]
[assembly: AssemblyMetadata("Tags","")]
[assembly: AssemblyMetadata("Author","Kouji Matsui <k#kekyo.net>")]
[assembly: AssemblyMetadata("Committer","Kouji Matsui <k#kekyo.net>")]
[assembly: AssemblyMetadata("Message","Merge branch 'devel'")]
[assembly: AssemblyMetadata("Build","")]
[assembly: AssemblyMetadata("Generated","Sun, 20 Dec 2020 09:37:43 GMT")]
[assembly: AssemblyMetadata("Platform","AnyCPU")]
[assembly: AssemblyMetadata("BuildOn","Unix")]
[assembly: AssemblyMetadata("SdkVersion","5.0.101")]
namespace YourApp
{
internal static class ThisAssembly
{
public const string AssemblyVersion = "1.0.21";
public const string AssemblyFileVersion = "2020.12.20.33529";
public const string AssemblyInformationalVersion = "1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a";
public static class AssemblyMetadata
{
public const string Date = "Sun, 20 Dec 2020 09:37:39 GMT";
public const string Branch = "master";
public const string Tags = "";
public const string Author = "Kouji Matsui <k#kekyo.net>";
public const string Committer = "Kouji Matsui <k#kekyo.net>";
public const string Message = "Merge branch 'devel'";
public const string Build = "";
public const string Generated = "Sun, 20 Dec 2020 09:37:43 GMT";
public const string Platform = "AnyCPU";
public const string BuildOn = "Unix";
public const string SdkVersion = "5.0.101";
}
}
}
Another alternative with dates, based on Antonio Rodríguez's answer, to avoid repetitions in the numbers
Version Patch: (Year in 2 digits)+(Day of Year)
VersionRevision: Total number of seconds in the Day
<PropertyGroup>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<VersionPatch Condition="'$(VersionPatch)' == ''">$([System.DateTime]::UtcNow.ToString("yy"))$([System.DateTime]::UtcNow.DayOfYear.ToString("0"))</VersionPatch>
<VersionRevision Condition="'$(VersionRevision)' == ''">$([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds.ToString("0"))</VersionRevision>
</PropertyGroup>
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Version>$(VersionMajor).$(VersionMinor).$(VersionPatch).$(VersionRevision)</Version>
</PropertyGroup>
Related
I'm currently working on my first .NET 6 cross-plattform project in VisualStduio 2022.
I want to change my build output (Base output path) to: ..\..\..\bin\.
I know that this only works for windows.
Is there a way to adjust this path so it works for both windows and unix?
I tried this and it only works for windows:
<PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
<IsLinux Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
</PropertyGroup>
<PropertyGroup Condition="'$(IsWindows)'=='true'">
<BaseOutputPath>..\..\..\bin\</BaseOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(IsLinux)'=='true'">
<BaseOutputPath>../../../bin/</BaseOutputPath>
</PropertyGroup>
In my project.cs I would use:
System.IO.Directory.GetParent(System.Environment.CurrentDirectory).Parent.FullName;
but not sure how to use it in the xml
MSBuild v15 and later has a NormalizeDirectory function that will ensure the correct directory separator for the current OS. (See "MSBuild property functions".) Your code can be changed to the following:
<PropertyGroup>
<BaseOutputPath>$([MSBuild]::NormalizeDirectory('..\..\..\bin\'))</BaseOutputPath>
</PropertyGroup>
In ItemGroups, MSBuild handles mapping paths based on the OS. In the following example the Include paths are equivalent, interchangeable, and work on both Windows and Unix.
<ItemGroup>
<Windows Include="..\..\..\bin\*.*" />
<Unix Include="../../../bin/*.*" />
</ItemGroup>
If for other reasons you need to know the current OS is Unix, use the IsOSUnixLike function.
<Message Text="Hello Windows" Condition="!$([MSBuild]::IsOSUnixLike())" />
<Message Text="Hello *nix" Condition="$([MSBuild]::IsOSUnixLike())" />
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?
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>
I have an MSBuild file like this:
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<Param1>Hello world</Param1>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<Param1>Goodbye world</Param1>
</PropertyGroup>
</Project>
I am working on an external application and I need to be able to find out what the configured value of Param1 is. I need a way to correctly evaluate the MSBuild file so that any conditions are applied and then the correct parameter returned to the calling application.
Being able to do something like this would be great:
>MSBuild /p:Configuration=Release MyBuild.proj -extractParam:Param1
>Goodbye World
Any ideas? Is this possible with C# instead?
You can make the project output the value, then parse it using scripting/C#/....
Add this target to your project:
<Target Name="OutputParam1" AfterTargets="Build">
<Message Text="Param1 = $(Param1)"/>
</Target>
it will be invoked automatically after the Build target.
Then on the commandline:
>MSBuild /p:Configuration=Release MyBuild.proj /fl
where /fl cause the file msbuild.log to be generated, which will contain amongst others a line
Param1 = Goodbye world
because of the Message task. Now use e.g. powershell to output the matching part:
>powershell -command "$a = Select-String -Path msbuild.log -Pattern 'Param1 = (.+)'; $a.Matches[0].Groups[1].Value"
>Goodbye world
You can add a target which prints the param value:
<Target Name="ExtractParam1" >
<Message Text="$(Param1)" Importance="high" />
</Target>
The switches /v:m /nologo makes the output print just the value:
msbuild /p:Configuration=Release MyBuild.proj /t:ExtractParam1 /v:m /nologo
I'm new to Antlr. I want to use Antlr in C# and I've done all things described in https://github.com/sharwell/antlr4cs but when I build my Project nothing happens.
Part of my Project File:
<ItemGroup>
<Antlr4 Include="Model\ScriptGrammar\ScriptGrammar.g4">
<Generator>MSBuild:Compile</Generator>
<CustomToolNamespace>Model.ScriptGrammar</CustomToolNamespace>
</Antlr4>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<!-- Folder containing Antlr4BuildTasks.dll -->
<Antlr4BuildTaskPath>..\..\External\Antlr</Antlr4BuildTaskPath>
<!-- Path to the ANTLR Tool itself. -->
<Antlr4ToolPath>..\..\External\Antlr\antlr4-csharp-4.0.1-SNAPSHOT-complete.jar</Antlr4ToolPath>
</PropertyGroup>
<Import Project="..\..\External\Antlr\Antlr4.targets" />
<PropertyGroup>
<PostBuildEvent>$(ProjectDir)\copyExternals.bat</PostBuildEvent>
</PropertyGroup>
My test grammar file:
grammar ScriptGrammar;
options
{
language=CSharp_v4_5;
}
r : 'hello' ID ; // match keyword hello followed by an identifier
ID : [a-z]+ ; // match lower-case identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines, \r (Windows)
I'm using Visual Studio 2012 Professional
Remove the following lines from the project file:
<PropertyGroup>
<!-- Folder containing Antlr4BuildTasks.dll -->
<Antlr4BuildTaskPath>..\..\External\Antlr</Antlr4BuildTaskPath>
<!-- Path to the ANTLR Tool itself. -->
<Antlr4ToolPath>..\..\External\Antlr\antlr4-csharp-4.0.1-SNAPSHOT-complete.jar</Antlr4ToolPath>
</PropertyGroup>
<Import Project="..\..\External\Antlr\Antlr4.targets" />
Use the NuGet package manager to install the Antlr4 package described here:
https://www.nuget.org/packages/Antlr4
Remove the following lines from your grammar file. The language is set by the build tool.
options
{
language=CSharp_v4_5;
}