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%")]
50 changes: 40 additions & 10 deletions Fody/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
public class Configuration
{
public bool UseProject;
public bool UseFileVersion;
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)
{
throw new WeavingException($"Unable to parse '{attr.Value}' as a boolean, please use true or false.");
}
UseProject = ConvertAndThrowIfNotBoolean(attr.Value);
}

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

attr = config.Attribute("ChangeString");
if (!string.IsNullOrWhiteSpace(attr?.Value))
if (HasValue(attr))
{
ChangeString = attr.Value;
}

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

private static bool HasValue(XAttribute attr)
{
return !string.IsNullOrWhiteSpace(attr?.Value);
}

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

[Obsolete("Use ReplaceTokens with Version. Will be removed in 2.0")]
public string ReplaceTokens(string template, ModuleDefinition moduleDefinition, Repository repo, string changestring)
{
var assemblyVersion = moduleDefinition.Assembly.Name.Version;
return ReplaceTokens(template, moduleDefinition.Assembly.Name.Version, repo, changestring);
}

public string ReplaceTokens(string template, System.Version version, Repository repo, string changestring)
{
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
87 changes: 65 additions & 22 deletions Fody/ModuleWeaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@
using Version = System.Version;
using Fody.PeImage;
using Fody.VersionResources;
using Mono.Collections.Generic;
using System.Collections.Generic;

public class ModuleWeaver
{
#region public props
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
public XElement Config { get; set; }
public Action<string> LogInfo { get; set; }
public Action<string> LogWarning { get; set; }
Expand All @@ -18,12 +24,22 @@ public class ModuleWeaver
public string ProjectDirectoryPath { get; set; }
public string AddinDirectoryPath { get; set; }
public string AssemblyFilePath { get; set; }
// ReSharper restore MemberCanBePrivate.Global
// ReSharper restore AutoPropertyCanBeMadeGetOnly.Global
// ReSharper restore UnusedAutoPropertyAccessor.Global
#endregion

private static bool isPathSet;
private readonly FormatStringTokenResolver formatStringTokenResolver;
private string assemblyInfoVersion;
private Version assemblyVersion;
private Version versionToUse;
private bool dotGitDirExists;

private Configuration _config;

private const string InfoAttributeName = nameof(System.Reflection.AssemblyInformationalVersionAttribute);
private const string FileAttributeName = nameof(System.Reflection.AssemblyFileVersionAttribute);

public ModuleWeaver()
{
LogInfo = s => { };
Expand All @@ -35,14 +51,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 +78,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, InfoAttributeName);
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 +97,32 @@ 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 static 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, FileAttributeName);
if (afvAttribute == null)
{
throw new WeavingException("AssemblyFileVersion attribute could not be found.");
}

var assemblyFileVersionString = (string) afvAttribute.ConstructorArguments[0].Value;
VerifyStartsWithVersion(assemblyFileVersionString);
return Version.Parse(assemblyFileVersionString);
}

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

public void AfterWeaving()
Expand All @@ -161,22 +198,22 @@ public void AfterWeaving()
var reader = new VersionResourceReader(versionStream);
var versions = reader.Read();

if (versions.FixedFileInfo == null)
{
throw new WeavingException("versions.FixedFileInfo == null");
}

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 @@ -189,7 +226,13 @@ public void AfterWeaving()
}
catch (Exception ex)
{
throw new WeavingException($"Failed to update the assembly information. {ex.Message}");
throw new WeavingException("Failed to update the assembly information. {ex.Message}", ex);
}
}

private static void SetTableValue(IDictionary<string, string> dict, string key, string value)
{
if (dict.ContainsKey(key))
dict[key] = value;
}
}
5 changes: 5 additions & 0 deletions Fody/WeavingException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,9 @@ public WeavingException(string message)
{

}

/// <inheritdoc />
public WeavingException(string message, Exception innerException) : base(message, innerException)
{
}
}
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Extracts the git information from disk, combines it with the assembly version, a
So if your assembly version is `1.0.0.0`, the working branch is `master` and the last commit is `759e9ddb53271dfa9335a3b27e452749a9b22280` then the following attribute will be added to the assembly.

```c#
[assembly: AssemblyInformationalVersion("1.0.0.0 Head:'master' Sha:759e9ddb53271dfa9335a3b27e452749a9b22280")]
[assembly: AssemblyInformationalVersion("1.0.0.0 Head:'master' Sha:759e9ddb53271dfa9335a3b27e452749a9b22280")]
```


Expand All @@ -52,6 +52,8 @@ The tokens are:
- `%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
- `%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
Expand All @@ -78,7 +80,6 @@ Define the string used to indicate that the code was built from a non clean repo
<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`).
Expand All @@ -89,6 +90,24 @@ Define if you want to start Stamp to start searching for the Git repository in t
<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`*

```xml
<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`*
```xml
<Stamp UseFileVersion='true' />
```

## Icon

Expand Down
Loading