diff --git a/src/GitVersionCore/GitVersionCore.csproj b/src/GitVersionCore/GitVersionCore.csproj index ebb4e6db9a..7c90351d5d 100644 --- a/src/GitVersionCore/GitVersionCore.csproj +++ b/src/GitVersionCore/GitVersionCore.csproj @@ -105,5 +105,39 @@ Designer - + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/GitVersionCore/GitVersionInformationResources/AddFormats/GitVersionInformation.h b/src/GitVersionCore/GitVersionInformationResources/AddFormats/GitVersionInformation.h new file mode 100644 index 0000000000..6e959d454d --- /dev/null +++ b/src/GitVersionCore/GitVersionInformationResources/AddFormats/GitVersionInformation.h @@ -0,0 +1 @@ +#define {0} {1} \ No newline at end of file diff --git a/src/GitVersionCore/GitVersionInformationResources/Templates/GitVersionInformation.h b/src/GitVersionCore/GitVersionInformationResources/Templates/GitVersionInformation.h new file mode 100644 index 0000000000..b2cf273d3d --- /dev/null +++ b/src/GitVersionCore/GitVersionInformationResources/Templates/GitVersionInformation.h @@ -0,0 +1,27 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// GitVersion +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +#pragma once + +//------------------------------------------------------------------------------ +//What the values SHOULD look like after the template is applied: +//#define VERSION_FULL MAJORVERSION,MINORVERSION,BUILDVERSION,REVISIONVERSION +//#define VERSION_STRING VERSION_XSTR(MAJORVERSION.MINORVERSION.BUILDVERSION.REVISIONVERSION-REVISIONHASH) VERSION_BLANK "\0" + +//NOTE: The trailing "\0" is VERY important + +//------------------------------------------------------------------------------ + +#define VERSION_STR(x) #x +#define VERSION_XSTR(x) VERSION_STR(x) +#define VERSION_BLANK "" + +#define VERSION_FULL 0,0,0,1 +#define VERSION_STRING VERSION_XSTR(0.0.0.1) VERSION_BLANK "\0" diff --git a/src/GitVersionCore/TemplateManager.cs b/src/GitVersionCore/TemplateManager.cs index 2613dc282d..5bb6a323cb 100644 --- a/src/GitVersionCore/TemplateManager.cs +++ b/src/GitVersionCore/TemplateManager.cs @@ -7,13 +7,14 @@ using GitVersionCore.Extensions; using System.Reflection; - enum TemplateType + public enum TemplateType { VersionAssemblyInfoResources, - GitVersionInformationResources + GitVersionInformationResources, + NativeHeader, } - class TemplateManager + public class TemplateManager { readonly Dictionary templates; readonly Dictionary addFormats; @@ -89,4 +90,4 @@ static IEnumerable GetEmbeddedTemplates(TemplateType templateType, strin } } } -} +} \ No newline at end of file diff --git a/src/GitVersionTask/GenerateGitVersionInformation.cs b/src/GitVersionTask/GenerateGitVersionInformation.cs index 8abaa5a97d..2bf582b557 100644 --- a/src/GitVersionTask/GenerateGitVersionInformation.cs +++ b/src/GitVersionTask/GenerateGitVersionInformation.cs @@ -88,14 +88,17 @@ string GetFileExtension() { switch (Language) { - case "C#": - return "cs"; + case SupportedLanguageConstants.CSHARP: + return SupportedLanguageConstants.FILEEXTENSION_CSHARP; - case "F#": - return "fs"; + case SupportedLanguageConstants.FSHARP: + return SupportedLanguageConstants.FILEEXTENSION_FSHARP; - case "VB": - return "vb"; + case SupportedLanguageConstants.VBDOTNET: + return SupportedLanguageConstants.FILEEXTENSION_VBDOTNET; + + case SupportedLanguageConstants.CPLUSPLUS: + return SupportedLanguageConstants.FILEEXTENSION_CPLUSPLUS; default: throw new Exception($"Unknown language detected: '{Language}'"); diff --git a/src/GitVersionTask/GitVersionTask.csproj b/src/GitVersionTask/GitVersionTask.csproj index 6da556cace..06609ed62a 100644 --- a/src/GitVersionTask/GitVersionTask.csproj +++ b/src/GitVersionTask/GitVersionTask.csproj @@ -92,8 +92,26 @@ - - + + ..\packages\YamlDotNet.3.8.0\lib\net35\YamlDotNet.dll + True + + + + + + + + + + + + + + + + + diff --git a/src/GitVersionTask/NugetAssets/build/GitVersionTask.targets b/src/GitVersionTask/NugetAssets/build/GitVersionTask.targets index 1d50060976..e9bc3b99d9 100644 --- a/src/GitVersionTask/NugetAssets/build/GitVersionTask.targets +++ b/src/GitVersionTask/NugetAssets/build/GitVersionTask.targets @@ -1,5 +1,40 @@ - + + + + True + + + + false + true + + + + $(MSBuildProjectDirectory)\..\ + $(SolutionDir) + $(MSBuildProjectDirectory) + $(SolutionDir)\GitVersionTask.targets + $(SolutionDir)\GitVersionTask.targets + + $(MSBuildProjectDirectory)\obj\$(Configuration)\ + false + + + false + true + + + false + false + true + false + + + false + true + + @@ -9,10 +44,143 @@ false true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(GitVersion_FullSemVer) + $(GitVersion_MajorMinorPatch) + $(GitVersion_NuGetPreReleaseTag) + $(GitVersion_PreReleaseTag) + $(GitVersion_NuGetVersion) + $(GitVersion_FullSemVer) + $(GitVersion_InformationalVersion) + $(GitVersion_AssemblySemVer) + $(GitVersion_AssemblySemFileVer) + + + + + + + + False + + + False + + + False + + - \ No newline at end of file diff --git a/src/GitVersionTask/SupportedLanguageConstants.cs b/src/GitVersionTask/SupportedLanguageConstants.cs new file mode 100644 index 0000000000..4555d82419 --- /dev/null +++ b/src/GitVersionTask/SupportedLanguageConstants.cs @@ -0,0 +1,31 @@ +namespace GitVersionTask +{ + internal static class SupportedLanguageConstants + { + internal const string VBDOTNET = "VB"; + internal const string FSHARP = "F#"; + internal const string CSHARP = "C#"; + internal const string CPLUSPLUS = "C++"; + + internal static readonly string[] SUPPORTED_LANGUAGES = + { + VBDOTNET, + CSHARP, + FSHARP, + CPLUSPLUS, + }; + + internal const string FILEEXTENSION_VBDOTNET = "vb"; + internal const string FILEEXTENSION_CSHARP = "cs"; + internal const string FILEEXTENSION_FSHARP = "fs"; + internal const string FILEEXTENSION_CPLUSPLUS = "h"; + + internal static readonly string[] SUPPORTED_LANGUAGE_FILEEXTENSIONS = + { + FILEEXTENSION_VBDOTNET, + FILEEXTENSION_CSHARP, + FILEEXTENSION_FSHARP, + FILEEXTENSION_CPLUSPLUS, + }; + } +} \ No newline at end of file diff --git a/src/GitVersionTask/UpdateAssemblyInfo.cs b/src/GitVersionTask/UpdateAssemblyInfo.cs index b299fcbc93..abbaf3d777 100644 --- a/src/GitVersionTask/UpdateAssemblyInfo.cs +++ b/src/GitVersionTask/UpdateAssemblyInfo.cs @@ -102,14 +102,14 @@ string GetFileExtension() { switch(Language) { - case "C#": - return "cs"; + case SupportedLanguageConstants.CSHARP: + return SupportedLanguageConstants.FILEEXTENSION_CSHARP; - case "F#": - return "fs"; + case SupportedLanguageConstants.FSHARP: + return SupportedLanguageConstants.FILEEXTENSION_FSHARP; - case "VB": - return "vb"; + case SupportedLanguageConstants.VBDOTNET: + return SupportedLanguageConstants.FILEEXTENSION_VBDOTNET; default: throw new Exception($"Unknown language detected: '{Language}'"); diff --git a/src/GitVersionTask/UpdateNativeVersionHeader.cs b/src/GitVersionTask/UpdateNativeVersionHeader.cs new file mode 100644 index 0000000000..a3c650a754 --- /dev/null +++ b/src/GitVersionTask/UpdateNativeVersionHeader.cs @@ -0,0 +1,310 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace GitVersionTask +{ + using System.IO; + using System.Text.RegularExpressions; + using GitTools; + using GitVersion; + using GitVersion.Helpers; + using Microsoft.Build.Framework; + + public sealed class UpdateNativeVersionHeader : GitVersionTaskBase + { + private TaskLogger logger; + + public UpdateNativeVersionHeader() + { +/* + #if DEBUG + if (!System.Diagnostics.Debugger.IsAttached) + { + System.Diagnostics.Debugger.Launch(); + } +#endif +*/ + + logger = new TaskLogger(this); + Logger.SetLoggers(this.LogDebug, this.LogInfo, this.LogWarning, s => this.LogError(s)); + } + + [Required] + public string SolutionDirectory { get; set; } + + [Required] + public string ProjectFile { get; set; } + + [Required] + public string IntermediateOutputPath { get; set; } + + [Required] + public ITaskItem[] CompileFiles { get; set; } + + //Only "C++" supported at this time + [Required] + public string Language { get; set; } + + [Output] + public string HeaderTempFilePath { get; set; } + + public bool NoFetch { get; set; } + + public override bool Execute() + { + try + { + InnerExecute(); + return true; + } + catch (WarningException errorException) + { + logger.LogWarning(errorException.Message); + return true; + } + catch (Exception exception) + { + logger.LogError("Error occurred: " + exception); + return false; + } + finally + { + Logger.Reset(); + } + } + + void InnerExecute() + { + TempFileTracker.DeleteTempFiles(); + + InvalidFileChecker.CheckForInvalidFiles(CompileFiles, ProjectFile); + + VersionVariables versionVariables; + if (!ExecuteCore.TryGetVersion(SolutionDirectory, out versionVariables, NoFetch, new Authentication())) + { + return; + } + + CreateTempHeaderFile(versionVariables); + } + + void CreateTempHeaderFile(VersionVariables versionVariables) + { + var fileExtension = GetFileExtension(); + var headerFileName = $"GitVersionTaskAssemblyInfo.g.{fileExtension}"; + + if (IntermediateOutputPath == null) + { + headerFileName = $"AssemblyInfo_{Path.GetFileNameWithoutExtension(ProjectFile)}_{Path.GetRandomFileName()}.g.{fileExtension}"; + } + + var workingDirectory = IntermediateOutputPath ?? TempFileTracker.TempPath; + + HeaderTempFilePath = Path.Combine(workingDirectory, headerFileName); + using (var headerFileUpdater = new NativeHeaderFileUpdater(headerFileName, workingDirectory, versionVariables, new FileSystem())) + { + headerFileUpdater.Update(); + headerFileUpdater.CommitChanges(); + } + } + + string GetFileExtension() + { + switch (Language) + { + case SupportedLanguageConstants.CPLUSPLUS: + return SupportedLanguageConstants.FILEEXTENSION_CPLUSPLUS; + default: + throw new Exception($"Unknown language detected: '{Language}'"); + } + } + } + + internal class NativeHeaderFileUpdater : IDisposable + { + readonly List restoreBackupTasks = new List(); + readonly List cleanupBackupTasks = new List(); + + ISet headerFileNames; + string workingDirectory; + VersionVariables variables; + IFileSystem fileSystem; + TemplateManager templateManager; + + + private NativeHeaderFileUpdater() + { + templateManager = new TemplateManager(TemplateType.GitVersionInformationResources); + } + + + public NativeHeaderFileUpdater(ISet headerFileNames, string workingDirectory, VersionVariables variables, IFileSystem fileSystem) + : this() + { + this.headerFileNames = headerFileNames; + this.workingDirectory = workingDirectory; + this.variables = variables; + this.fileSystem = fileSystem; + } + + public NativeHeaderFileUpdater(string headerFileName, string workingDirectory, VersionVariables versionVariables, FileSystem fileSystem) + : this(new HashSet { headerFileName }, workingDirectory, versionVariables, fileSystem) + { + } + + public void Update() + { + Logger.WriteInfo("Updating assembly info files"); + + var headerFiles = GetHeaderFiles(workingDirectory, headerFileNames, fileSystem).ToList(); + Logger.WriteInfo($"Found {headerFiles.Count} files"); + + var assemblyVersion = variables.AssemblySemVer; + var assemblyVersionRegex = new Regex(@"\s*#define VERSION_FULL\s*(.*)"); + var assemblyVersionString = !string.IsNullOrWhiteSpace(assemblyVersion) ? $"\r\n#define VERSION_FULL {assemblyVersion.Replace('.', ',')}\r\n" : "0,0,0,0"; + + var assemblyInfoVersion = variables.InformationalVersion; + var assemblyInfoVersionRegex = new Regex(@"\s*#define VERSION_STRING\s*(.*)"); + var assemblyInfoVersionString = $"\r\n#define VERSION_STRING VERSION_XSTR({assemblyInfoVersion}) VERSION_BLANK \"\\0\"\r\n"; + + foreach (var headerFile in headerFiles) + { + var backupAssemblyInfo = headerFile.FullName + ".bak"; + var localAssemblyInfo = headerFile.FullName; + fileSystem.Copy(headerFile.FullName, backupAssemblyInfo, true); + + restoreBackupTasks.Add(() => + { + if (fileSystem.Exists(localAssemblyInfo)) + { + fileSystem.Delete(localAssemblyInfo); + } + + fileSystem.Move(backupAssemblyInfo, localAssemblyInfo); + }); + + cleanupBackupTasks.Add(() => fileSystem.Delete(backupAssemblyInfo)); + + var originalFileContents = fileSystem.ReadAllText(headerFile.FullName); + var fileContents = originalFileContents; + var appendedAttributes = false; + + if (!string.IsNullOrWhiteSpace(assemblyVersion)) + { + fileContents = ReplaceOrAppend(assemblyVersionRegex, fileContents, assemblyVersionString, headerFile.Extension, ref appendedAttributes); + } + + //if (!string.IsNullOrWhiteSpace(headerFile)) + //{ + // fileContents = ReplaceOrAppend(assemblyFileVersionRegex, fileContents, assemblyFileVersionString, assemblyInfoFile.Extension, ref appendedAttributes); + //} + + fileContents = ReplaceOrAppend(assemblyInfoVersionRegex, fileContents, assemblyInfoVersionString, headerFile.Extension, ref appendedAttributes); + + if (appendedAttributes) + { + // If we appended any attributes, put a new line after them + fileContents += Environment.NewLine; + } + + if (originalFileContents != fileContents) + { + fileSystem.WriteAllText(headerFile.FullName, fileContents); + } + } + } + + internal string ReplaceOrAppend(Regex replaceRegex, string inputString, string replaceString, string fileExtension, ref bool appendedAttributes) + { + if (replaceRegex.IsMatch(inputString)) + { + appendedAttributes = false; + return replaceRegex.Replace(inputString, replaceString); + } + else + { + var assemblyAddFormat = templateManager.GetAddFormatFor(fileExtension); + + appendedAttributes = true; + return inputString + Environment.NewLine + string.Format(assemblyAddFormat, replaceString); + } + } + + internal IEnumerable GetHeaderFiles(string workingDirectory, ISet headerFileNames, IFileSystem fileSystem) + { + if (headerFileNames != null && headerFileNames.Any(x => !string.IsNullOrWhiteSpace(x))) + { + foreach (var headerFileName in headerFileNames) + { + var fullPath = Path.Combine(workingDirectory, headerFileName); + + if (EnsureVersionHeaderFile(fileSystem, fullPath)) + { + yield return new FileInfo(fullPath); + } + } + } + else + { + foreach (var item in fileSystem.DirectoryGetFiles(workingDirectory, "Version.h", SearchOption.AllDirectories)) + { + var assemblyInfoFile = new FileInfo(item); + + if (templateManager.IsSupported(assemblyInfoFile.Extension)) + { + yield return assemblyInfoFile; + } + } + } + } + + internal bool EnsureVersionHeaderFile(IFileSystem fileSystem, string fullPath) + { + if (fileSystem.Exists(fullPath)) + { + return true; + } + + var assemblyInfoSource = templateManager.GetTemplateFor(Path.GetExtension(fullPath)); + + if (!string.IsNullOrWhiteSpace(assemblyInfoSource)) + { + var fileInfo = new FileInfo(fullPath); + + if (!fileSystem.DirectoryExists(fileInfo.Directory.FullName)) + { + fileSystem.CreateDirectory(fileInfo.Directory.FullName); + } + + fileSystem.WriteAllText(fullPath, assemblyInfoSource); + return true; + } + + Logger.WriteWarning($"No version header template available to create source file '{fullPath}'"); + return false; + } + + public virtual void Dispose() + { + foreach (var restoreBackup in restoreBackupTasks) + { + restoreBackup(); + } + + cleanupBackupTasks.Clear(); + restoreBackupTasks.Clear(); + } + + public void CommitChanges() + { + foreach (var cleanupBackupTask in cleanupBackupTasks) + { + cleanupBackupTask(); + } + + cleanupBackupTasks.Clear(); + restoreBackupTasks.Clear(); + } + } +} \ No newline at end of file