Skip to content
This repository was archived by the owner on Dec 2, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AssemblyToProcess/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
[assembly: AssemblyTitle("AssemblyToProcess")]
[assembly: AssemblyProduct("AssemblyToProcess")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("4.5.6.7")]
//[assembly: AssemblyInformationalVersionAttribute("1.0.0.0/aString")]
1 change: 1 addition & 0 deletions AssemblyToProcessExistingAttribute/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
[assembly: AssemblyTitle("AssemblyToProcessExistingAttribute")]
[assembly: AssemblyProduct("AssemblyToProcessExistingAttribute")]
[assembly: AssemblyVersion("1.0.0")]
[assembly: AssemblyFileVersion("4.5.6.7")]
[assembly: AssemblyInformationalVersionAttribute("%version3%+%branch%.%githash% %haschanges% %utcnow% %now:yyMMdd%")]
52 changes: 41 additions & 11 deletions Fody/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

public class Configuration
{
public bool UseProject;
public bool UseProject = false;
public bool UseFileVersion = false;
public bool OverwriteFileVersion = true;
public string ChangeString = "HasChanges";

public Configuration(XElement config)
Expand All @@ -14,22 +16,50 @@ public Configuration(XElement config)
}

var attr = config.Attribute("UseProjectGit");
if (attr != null)
if (HasValue(attr))
{
try
{
UseProject = Convert.ToBoolean(attr.Value);
}
catch (Exception)
UseProject = ConvertAndThrowIfNotBoolean(attr.Value);
}

attr = config.Attribute("UseFileVersion");
if (HasValue(attr))
{
UseFileVersion = ConvertAndThrowIfNotBoolean(attr.Value);
}

attr = config.Attribute("ChangeString");
if (HasValue(attr))
{
ChangeString = attr.Value;
}

if (UseFileVersion)
OverwriteFileVersion = false;
else
{
attr = config.Attribute("OverwriteFileVersion");
if (HasValue(attr))
{
throw new WeavingException($"Unable to parse '{attr.Value}' as a boolean, please use true or false.");
OverwriteFileVersion = ConvertAndThrowIfNotBoolean(attr.Value);
}
}
}

attr = config.Attribute("ChangeString");
if (!string.IsNullOrWhiteSpace(attr?.Value))
private bool HasValue(XAttribute attr)
{
return !String.IsNullOrWhiteSpace(attr?.Value);
}

private bool ConvertAndThrowIfNotBoolean(string value)
{
try
{
var result = Convert.ToBoolean(value);
return result;
}
catch
{
ChangeString = config.Attribute("ChangeString").Value;
throw new WeavingException($"Unable to parse '{value}' as a boolean; please use 'true' or 'false'.");
}
}
}
13 changes: 6 additions & 7 deletions Fody/FormatStringTokenResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,15 @@ public class FormatStringTokenResolver
static DateTime now = DateTime.Now;
static DateTime utcNow = DateTime.UtcNow;

public string ReplaceTokens(string template, ModuleDefinition moduleDefinition, Repository repo, string changestring)
public string ReplaceTokens(string template, System.Version version, Repository repo, string changestring)
{
var assemblyVersion = moduleDefinition.Assembly.Name.Version;
var branch = repo.Head;

template = template.Replace("%version%", assemblyVersion.ToString());
template = template.Replace("%version1%", assemblyVersion.ToString(1));
template = template.Replace("%version2%", assemblyVersion.ToString(2));
template = template.Replace("%version3%", assemblyVersion.ToString(3));
template = template.Replace("%version4%", assemblyVersion.ToString(4));
template = template.Replace("%version%", version.ToString());
template = template.Replace("%version1%", version.ToString(1));
template = template.Replace("%version2%", version.ToString(2));
template = template.Replace("%version3%", version.ToString(3));
template = template.Replace("%version4%", version.ToString(4));

template = template.Replace("%now%", now.ToShortDateString());
template = template.Replace("%utcnow%", utcNow.ToShortDateString());
Expand Down
73 changes: 52 additions & 21 deletions Fody/ModuleWeaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using Version = System.Version;
using Fody.PeImage;
using Fody.VersionResources;
using Mono.Collections.Generic;
using System.Collections.Generic;

public class ModuleWeaver
{
Expand All @@ -21,9 +23,14 @@ public class ModuleWeaver
static bool isPathSet;
readonly FormatStringTokenResolver formatStringTokenResolver;
string assemblyInfoVersion;
Version assemblyVersion;
Version versionToUse;
bool dotGitDirExists;

Configuration _config;

const string INFO_ATTRIBUTE = "AssemblyInformationalVersionAttribute";
const string FILE_ATTRIBUTE = "AssemblyFileVersionAttribute";

public ModuleWeaver()
{
LogInfo = s => { };
Expand All @@ -35,14 +42,14 @@ public void Execute()
{
SetSearchPath();

var config = new Configuration(Config);
_config = new Configuration(Config);

LogInfo("Starting search for git repository in " + (config.UseProject ? "ProjectDir" : "SolutionDir"));
LogInfo("Starting search for git repository in " + (_config.UseProject ? "ProjectDir" : "SolutionDir"));


var customAttributes = ModuleDefinition.Assembly.CustomAttributes;

var gitDir = Repository.Discover( config.UseProject ? ProjectDirectoryPath : SolutionDirectoryPath );
var gitDir = Repository.Discover( _config.UseProject ? ProjectDirectoryPath : SolutionDirectoryPath );
if (gitDir == null)
{
LogWarning("No .git directory found.");
Expand All @@ -62,13 +69,16 @@ public void Execute()
return;
}

assemblyVersion = ModuleDefinition.Assembly.Name.Version;
if (!_config.UseFileVersion)
versionToUse = ModuleDefinition.Assembly.Name.Version;
else
versionToUse = GetAssemblyFileVersion(customAttributes);

var customAttribute = customAttributes.FirstOrDefault(x => x.AttributeType.Name == "AssemblyInformationalVersionAttribute");
var customAttribute = GetCustomAttribute(customAttributes, INFO_ATTRIBUTE);
if (customAttribute != null)
{
assemblyInfoVersion = (string) customAttribute.ConstructorArguments[0].Value;
assemblyInfoVersion = formatStringTokenResolver.ReplaceTokens(assemblyInfoVersion, ModuleDefinition, repo, config.ChangeString);
assemblyInfoVersion = formatStringTokenResolver.ReplaceTokens(assemblyInfoVersion, versionToUse, repo, _config.ChangeString);
VerifyStartsWithVersion(assemblyInfoVersion);
customAttribute.ConstructorArguments[0] = new CustomAttributeArgument(ModuleDefinition.TypeSystem.String, assemblyInfoVersion);
}
Expand All @@ -78,14 +88,34 @@ public void Execute()
var constructor = ModuleDefinition.ImportReference(versionAttribute.Methods.First(x => x.IsConstructor));
customAttribute = new CustomAttribute(constructor);

assemblyInfoVersion = $"{assemblyVersion} Head:'{repo.Head.FriendlyName}' Sha:{branch.Tip.Sha}{(repo.IsClean() ? "" : " " + config.ChangeString)}";
assemblyInfoVersion = $"{versionToUse} Head:'{repo.Head.FriendlyName}' Sha:{branch.Tip.Sha}{(repo.IsClean() ? "" : " " + _config.ChangeString)}";

customAttribute.ConstructorArguments.Add(new CustomAttributeArgument(ModuleDefinition.TypeSystem.String, assemblyInfoVersion));
customAttributes.Add(customAttribute);
}
}
}

private CustomAttribute GetCustomAttribute(Collection<CustomAttribute> attributes, string attributeName)
{
return attributes.FirstOrDefault(x => x.AttributeType.Name == attributeName);
}

private Version GetAssemblyFileVersion(Collection<CustomAttribute> customAttributes)
{
var afvAttribute = GetCustomAttribute(customAttributes, FILE_ATTRIBUTE);
if (afvAttribute != null)
{
var assemblyFileVersionString = (string)afvAttribute.ConstructorArguments[0].Value;
VerifyStartsWithVersion(assemblyFileVersionString);
return Version.Parse(assemblyFileVersionString);
}
else
{
throw new WeavingException("AssemblyFileVersion attribute could not be found.");
}
}

void VerifyStartsWithVersion(string versionString)
{
var prefix = new string(versionString.TakeWhile(x => char.IsDigit(x) || x == '.').ToArray());
Expand Down Expand Up @@ -137,13 +167,13 @@ static string GetProcessorArchitecture()
TypeDefinition GetVersionAttribute()
{
var msCoreLib = ModuleDefinition.AssemblyResolver.Resolve("mscorlib");
var msCoreAttribute = msCoreLib.MainModule.Types.FirstOrDefault(x => x.Name == "AssemblyInformationalVersionAttribute");
var msCoreAttribute = msCoreLib.MainModule.Types.FirstOrDefault(x => x.Name == INFO_ATTRIBUTE);
if (msCoreAttribute != null)
{
return msCoreAttribute;
}
var systemRuntime = ModuleDefinition.AssemblyResolver.Resolve("System.Runtime");
return systemRuntime.MainModule.Types.First(x => x.Name == "AssemblyInformationalVersionAttribute");
return systemRuntime.MainModule.Types.First(x => x.Name == INFO_ATTRIBUTE);
}

int? GetVerPatchWaitTimeout()
Expand Down Expand Up @@ -187,21 +217,16 @@ public void AfterWeaving()
var versions = reader.Read();

var fixedFileInfo = versions.FixedFileInfo.Value;
fixedFileInfo.FileVersion = assemblyVersion;
fixedFileInfo.ProductVersion = assemblyVersion;
if (_config.OverwriteFileVersion)
fixedFileInfo.FileVersion = versionToUse;
fixedFileInfo.ProductVersion = versionToUse;
versions.FixedFileInfo = fixedFileInfo;

foreach (var stringTable in versions.StringFileInfo)
{
if (stringTable.Values.ContainsKey("FileVersion"))
{
stringTable.Values["FileVersion"] = assemblyVersion.ToString();
}

if (stringTable.Values.ContainsKey("ProductVersion"))
{
stringTable.Values["ProductVersion"] = assemblyInfoVersion;
}
if (_config.OverwriteFileVersion)
SetTableValue(stringTable.Values, "FileVersion", versionToUse.ToString());
SetTableValue(stringTable.Values, "ProductVersion", assemblyInfoVersion);
}

versionStream.Position = 0;
Expand All @@ -217,4 +242,10 @@ public void AfterWeaving()
throw new WeavingException($"Failed to update the assembly information. {ex.Message}");
}
}

private void SetTableValue(Dictionary<string, string> dict, string key, string value)
{
if (dict.ContainsKey(key))
dict[key] = value;
}
}
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,32 +31,52 @@ The tokens are:
- `%version2%` is replaced with the major and minor version (1.0)
- `%version3%` is replaced with the major, minor, and revision version (1.0.0)
- `%version4%` is replaced with the major, minor, revision, and build version (1.0.0.0)
- `%now%` is replaced with the current short date
- `%utcnow%` is replaced with the current utc short date
- `%githash%` is replaced with the SHA1 hash of the branch tip of the repository
- `%shorthash%` is replaced with the first eight characters of %githash%
- `%branch%` is replaced with the branch name of the repository
- `%haschanges%` is replaced with the string defined in the ChangeString attribute in the configuration, see below.
- `%user%` is replaced with the current user name
- `%machinename%` is replaced with the current machine name
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already added these to the readme. Thanks!


> NOTE: if you already have an AssemblyInformationalVersion attribute and it doesn't use replacement tokens, it will not be modified at all.

## Configuration

All config options are attributes of Stamp in FodyWeavers.xml
All config options are attributes of the `Stamp` node in `FodyWeavers.xml`

### ChangeString

Define the string used to indicate that the code was built from a non clean repository.

*Default is `HasChanges`*

<Fody ChangeString="New text" />
<Stamp ChangeString="New text" />

### UseProjectGit

Define if you want to start Stamp to start searching for the Git repository in the ProjectDir (true) or the SolutionDir (false).
Define if you want to start Stamp to start searching for the Git repository in the ProjectDir (`true`) or the SolutionDir (`false`).

*Default is `false`*

<Fody UseProjectGit='true' />
<Stamp UseProjectGit='true' />

### OverwriteFileVersion

By default, Stamp will overwrite the `AssemblyFileVersion` with the `AssemblyVersion`. Setting this to `false` will preserve the existing `AssemblyFileVersion`.

*Default is `true`*

<Stamp OverwriteFileVersion='false' />

### UseFileVersion

By default, Stamp uses the value from `AssemblyVersion` to construct the `AssemblyInformationalVersion`. Set this to `true` to use the `AssemblyFileVersion` instead. **Note:** If this is set to `true`, `OverwriteFileVersion` will be `false` and will ignore any value explicitly set.

*Default is `false`*

<Stamp UseFileVersion='true' />

## Icon

Expand Down
Loading