From bf34494ae14035e785f36b6a24454eb04be8ea73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6llner?= Date: Wed, 4 Sep 2019 22:29:23 +0200 Subject: [PATCH] Initial skeleton for MSBuild integration --- Capnp.Net.sln | 6 + .../AssemblyAttributes.cs | 4 + .../Capnpc.Csharp.MsBuild.Generation.csproj | 79 +++++++ .../Capnpc.Csharp.MsBuild.Generation.nuspec | 30 +++ .../CodeBehindWriter.cs | 49 +++++ .../FeatureCodeBehindGenerator.cs | 39 ++++ .../FeatureFileCodeBehindGenerator.cs | 78 +++++++ .../FilePathGenerator.cs | 31 +++ .../FileSystemHelper.cs | 204 ++++++++++++++++++ .../GenerateCapnpFileCodeBehindTask.cs | 112 ++++++++++ .../Helper/LogExtensions.cs | 22 ++ .../ICapnpCsharpGenerator.cs | 9 + .../TestFileGeneratorResult.cs | 42 ++++ .../TestGenerationError.cs | 6 + .../TestGeneratorResult.cs | 11 + .../CpsExtension.DesignTime.targets | 15 ++ .../Buildsystem/Rules/FeatureFileType.xaml | 34 +++ .../Buildsystem/Rules/ProjectItemsSchema.xaml | 18 ++ .../Capnpc.Csharp.MsBuild.Generation.props | 77 +++++++ .../Capnpc.Csharp.MsBuild.Generation.targets | 145 +++++++++++++ .../Capnpc.Csharp.MsBuild.Generation.tasks | 3 + .../Capnpc.Csharp.MsBuild.Generation.props | 5 + Licenses/SpecFlow-LICENSE-NewBSD.txt | 31 +++ 23 files changed, 1050 insertions(+) create mode 100644 Capnpc.Csharp.MsBuild.Generation/AssemblyAttributes.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj create mode 100644 Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec create mode 100644 Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/FeatureCodeBehindGenerator.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/FeatureFileCodeBehindGenerator.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/FilePathGenerator.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/TestFileGeneratorResult.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/TestGenerationError.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/TestGeneratorResult.cs create mode 100644 Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/CpsExtension.DesignTime.targets create mode 100644 Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/FeatureFileType.xaml create mode 100644 Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/ProjectItemsSchema.xaml create mode 100644 Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props create mode 100644 Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.targets create mode 100644 Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks create mode 100644 Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props create mode 100644 Licenses/SpecFlow-LICENSE-NewBSD.txt diff --git a/Capnp.Net.sln b/Capnp.Net.sln index 41f5968..8e20bf5 100644 --- a/Capnp.Net.sln +++ b/Capnp.Net.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnp.Net.Runtime.Tests.Cor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "capnpc-csharp.tests", "capnpc-csharp.tests\capnpc-csharp.tests.csproj", "{B77AC567-E232-4072-85C3-8689566BF3D4}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Capnpc.Csharp.MsBuild.Generation", "Capnpc.Csharp.MsBuild.Generation\Capnpc.Csharp.MsBuild.Generation.csproj", "{1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {B77AC567-E232-4072-85C3-8689566BF3D4}.Debug|Any CPU.Build.0 = Debug|Any CPU {B77AC567-E232-4072-85C3-8689566BF3D4}.Release|Any CPU.ActiveCfg = Release|Any CPU {B77AC567-E232-4072-85C3-8689566BF3D4}.Release|Any CPU.Build.0 = Release|Any CPU + {1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1EFC1F20-C7BB-4F44-8BF9-DBB123AACCF4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Capnpc.Csharp.MsBuild.Generation/AssemblyAttributes.cs b/Capnpc.Csharp.MsBuild.Generation/AssemblyAttributes.cs new file mode 100644 index 0000000..ff364dc --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/AssemblyAttributes.cs @@ -0,0 +1,4 @@ + +using System.Runtime.CompilerServices; +using System.Security; + diff --git a/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj new file mode 100644 index 0000000..bb09bb6 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.csproj @@ -0,0 +1,79 @@ + + + netstandard2.0;netcoreapp2.1 + false + $(MSBuildThisFileDirectory)Capnpc.Csharp.MsBuild.Generation.nuspec + true + + + true + true + + true + true + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + + + + + + + + + + + + + All + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Microsoft.Build + + + Microsoft.Build.Framework + + + Microsoft.Build.Utilities.Core + + + + + + + MSBuild:Compile + + + MSBuild:Compile + + + + \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec new file mode 100644 index 0000000..35629ed --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/Capnpc.Csharp.MsBuild.Generation.nuspec @@ -0,0 +1,30 @@ + + + + Capnpc.Csharp.Generation + 1.0.0 + Capnpc.Csharp.MsBuild.Generation + Christian Köllner and contributors + Christian Köllner + Package to enable the .capnp -> .cs file generation during build time + Package to enable the .capnp -> .cs file generation during build time + en-US + https://github.com/c80k/capnproto-dotnetcore + false + MIT + capnproto csharp msbuild + Christian Köllner and contributors + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs b/Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs new file mode 100644 index 0000000..f147ee7 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/CodeBehindWriter.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; +using Microsoft.Build.Utilities; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class CodeBehindWriter + { + public CodeBehindWriter(TaskLoggingHelper log) + { + Log = log; + } + + public TaskLoggingHelper Log { get; } + + public string WriteCodeBehindFile(string outputPath, string featureFile, TestFileGeneratorResult testFileGeneratorResult) //todo needs unit tests + { + //if (string.IsNullOrEmpty(testFileGeneratorResult.Filename)) + //{ + // Log?.LogWithNameTag(Log.LogError, $"{featureFile} has no generated filename"); + // return null; + //} + + //string directoryPath = Path.GetDirectoryName(outputPath) ?? throw new InvalidOperationException(); + //Log?.LogWithNameTag(Log.LogMessage, directoryPath); + + //Log?.LogWithNameTag(Log.LogMessage, $"Writing data to {outputPath}; path = {directoryPath}; generatedFilename = {testFileGeneratorResult.Filename}"); + + //if (File.Exists(outputPath)) + //{ + // if (!FileSystemHelper.FileCompareContent(outputPath, testFileGeneratorResult.GeneratedTestCode)) + // { + // File.WriteAllText(outputPath, testFileGeneratorResult.GeneratedTestCode); + // } + //} + //else + //{ + // if (!Directory.Exists(directoryPath)) + // { + // Directory.CreateDirectory(directoryPath); + // } + + // File.WriteAllText(outputPath, testFileGeneratorResult.GeneratedTestCode); + //} + + return outputPath; + } + } +} diff --git a/Capnpc.Csharp.MsBuild.Generation/FeatureCodeBehindGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/FeatureCodeBehindGenerator.cs new file mode 100644 index 0000000..1f6d631 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/FeatureCodeBehindGenerator.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class FeatureCodeBehindGenerator : IDisposable + { + //private SpecFlowProject _specFlowProject; + //private ITestGenerator _testGenerator; + + public void InitializeProject(string projectPath, string rootNamespace, IEnumerable generatorPlugins) + { + //_specFlowProject = MsBuildProjectReader.LoadSpecFlowProjectFromMsBuild(Path.GetFullPath(projectPath), rootNamespace); + + //var projectSettings = _specFlowProject.ProjectSettings; + + //var testGeneratorFactory = new TestGeneratorFactory(); + + //_testGenerator = testGeneratorFactory.CreateGenerator(projectSettings, generatorPlugins); + } + + + public TestFileGeneratorResult GenerateCodeBehindFile(string featureFile) + { + //var featureFileInput = new FeatureFileInput(featureFile); + //var generatedFeatureFileName = Path.GetFileName(_testGenerator.GetTestFullPath(featureFileInput)); + + //var testGeneratorResult = _testGenerator.GenerateTestFile(featureFileInput, new GenerationSettings()); + + return new TestFileGeneratorResult(null, null); + } + + public void Dispose() + { + //_testGenerator?.Dispose(); + } + } +} \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/FeatureFileCodeBehindGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/FeatureFileCodeBehindGenerator.cs new file mode 100644 index 0000000..eeb369f --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/FeatureFileCodeBehindGenerator.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Build.Utilities; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class FeatureFileCodeBehindGenerator : ICapnpCsharpGenerator + { + private readonly FilePathGenerator _filePathGenerator; + + public FeatureFileCodeBehindGenerator(TaskLoggingHelper log) + { + Log = log ?? throw new ArgumentNullException(nameof(log)); + _filePathGenerator = new FilePathGenerator(); + } + + public TaskLoggingHelper Log { get; } + + public IEnumerable GenerateFilesForProject( + string projectPath, + string rootNamespace, + List CapnpFiles, + List generatorPlugins, + string projectFolder, + string outputPath) + { + using (var featureCodeBehindGenerator = new FeatureCodeBehindGenerator()) + { + featureCodeBehindGenerator.InitializeProject(projectPath, rootNamespace, generatorPlugins); + + var codeBehindWriter = new CodeBehindWriter(null); + + if (CapnpFiles == null) + { + yield break; + } + + foreach (var featureFile in CapnpFiles) + { + var featureFileItemSpec = featureFile; + var generatorResult = featureCodeBehindGenerator.GenerateCodeBehindFile(featureFileItemSpec); + + if (!generatorResult.Success) + { + foreach (var error in generatorResult.Errors) + { + //Log.LogError( + // null, + // null, + // null, + // featureFile, + // error.Line, + // error.LinePosition, + // 0, + // 0, + // error.Message); + } + continue; + } + + var targetFilePath = _filePathGenerator.GenerateFilePath( + projectFolder, + outputPath, + featureFile, + generatorResult.Filename); + + var resultedFile = codeBehindWriter.WriteCodeBehindFile(targetFilePath, featureFile, generatorResult); + + yield return FileSystemHelper.GetRelativePath(resultedFile, projectFolder); + } + } + + } + } +} diff --git a/Capnpc.Csharp.MsBuild.Generation/FilePathGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/FilePathGenerator.cs new file mode 100644 index 0000000..c1e71a1 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/FilePathGenerator.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class FilePathGenerator + { + public string GenerateFilePath(string projectFolder, string relativeOutputPath, string featureFileName, string generatedCodeBehindFileName) + { + if (projectFolder is null) + { + throw new ArgumentNullException(nameof(projectFolder)); + } + + if (featureFileName is null) + { + throw new ArgumentNullException(nameof(featureFileName)); + } + + if (generatedCodeBehindFileName is null) + { + throw new ArgumentNullException(nameof(generatedCodeBehindFileName)); + } + + string featureFileFullPath = Path.GetFullPath(Path.Combine(projectFolder, relativeOutputPath ?? "", featureFileName)); + string featureFileDirPath = Path.GetDirectoryName(featureFileFullPath); + + return Path.Combine(featureFileDirPath, generatedCodeBehindFileName); + } + } +} diff --git a/Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs b/Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs new file mode 100644 index 0000000..14cfd41 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/FileSystemHelper.cs @@ -0,0 +1,204 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public static class FileSystemHelper + { + public static void CopyFileToFolder(string filePath, string folderName) + { + File.Copy(filePath, Path.Combine(folderName, Path.GetFileName(filePath))); + } + + public static string GetRelativePath(string path, string basePath) + { + path = Path.GetFullPath(path); + basePath = Path.GetFullPath(basePath); + if (String.Equals(path, basePath, StringComparison.OrdinalIgnoreCase)) + return "."; // the "this folder" + + if (path.StartsWith(basePath + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) + return path.Substring(basePath.Length + 1); + + //handle different drives + string pathRoot = Path.GetPathRoot(path); + if (!String.Equals(pathRoot, Path.GetPathRoot(basePath), StringComparison.OrdinalIgnoreCase)) + return path; + + //handle ".." pathes + string[] pathParts = path.Substring(pathRoot.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + string[] basePathParts = basePath.Substring(pathRoot.Length).Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + + int commonFolderCount = 0; + while (commonFolderCount < pathParts.Length && commonFolderCount < basePathParts.Length && + String.Equals(pathParts[commonFolderCount], basePathParts[commonFolderCount], StringComparison.OrdinalIgnoreCase)) + commonFolderCount++; + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < basePathParts.Length - commonFolderCount; i++) + { + result.Append(".."); + result.Append(Path.DirectorySeparatorChar); + } + + if (pathParts.Length - commonFolderCount == 0) + return result.ToString().TrimEnd(Path.DirectorySeparatorChar); + + result.Append(String.Join(Path.DirectorySeparatorChar.ToString(), pathParts, commonFolderCount, pathParts.Length - commonFolderCount)); + return result.ToString(); + } + + // This method accepts two strings the represent two files to + // compare. A return value of true indicates that the contents of the files + // are the same. A return value of any other value indicates that the + // files are not the same. + public static bool FileCompare(string filePath1, string filePath2) + { + int file1byte; + int file2byte; + + // Determine if the same file was referenced two times. + if (String.Equals(filePath1, filePath2, StringComparison.CurrentCultureIgnoreCase)) + { + // Return true to indicate that the files are the same. + return true; + } + + // Open the two files. + using (FileStream fs1 = new FileStream(filePath1, FileMode.Open, FileAccess.Read)) + using (FileStream fs2 = new FileStream(filePath2, FileMode.Open, FileAccess.Read)) + { + // Check the file sizes. If they are not the same, the files + // are not the same. + if (fs1.Length != fs2.Length) + { + // Return false to indicate files are different + return false; + } + + // Read and compare a byte from each file until either a + // non-matching set of bytes is found or until the end of + // file1 is reached. + do + { + // Read one byte from each file. + file1byte = fs1.ReadByte(); + file2byte = fs2.ReadByte(); + } while ((file1byte == file2byte) && (file1byte != -1)); + } + + // Return the success of the comparison. "file1byte" is + // equal to "file2byte" at this point only if the files are + // the same. + return ((file1byte - file2byte) == 0); + } + + public static void CopyDirectory(string sourcePath, string destPath, bool cleanDestination = true) + { + if (cleanDestination) + EnsureEmptyFolder(destPath); + else + { + if (!Directory.Exists(destPath)) + { + Directory.CreateDirectory(destPath); + } + } + + foreach (string file in Directory.GetFiles(sourcePath)) + { + var fileName = Path.GetFileName(file); + Debug.Assert(fileName != null); + string dest = Path.Combine(destPath, fileName); + File.Copy(file, dest, true); + File.SetAttributes(dest, File.GetAttributes(dest) & (~FileAttributes.ReadOnly)); + } + + foreach (string folder in Directory.GetDirectories(sourcePath)) + { + var folderName = Path.GetFileName(folder); + Debug.Assert(folderName != null); + string dest = Path.Combine(destPath, folderName); + CopyDirectory(folder, dest); + } + } + + public static void EnsureEmptyFolder(string folderName) + { + folderName = Path.GetFullPath(folderName); + + if (!Directory.Exists(folderName)) + { + Directory.CreateDirectory(folderName); + return; + } + + DeleteFolderContent(folderName); + } + + public static void EnsureFolderOfFile(string filePath) + { + string directory = Path.GetDirectoryName(filePath); + if (directory != null && !Directory.Exists(directory)) + Directory.CreateDirectory(directory); + } + + + private static void Retry(int number, Action action) + { + try + { + action(); + } + catch (UnauthorizedAccessException) + { + var i = number - 1; + + if (i == 0) + throw; + + Retry(i, action); + } + } + + public static void DeleteFolderContent(string folderName) + { + foreach (string file in Directory.GetFiles(folderName)) + { + try + { + Retry(5, () => File.Delete(file)); + } + catch (Exception ex) + { + throw new Exception("Unable to delete file: " + file, ex); + } + } + + foreach (string folder in Directory.GetDirectories(folderName)) + { + try + { + Retry(5, () => Directory.Delete(folder, true)); + } + catch (Exception ex) + { + throw new Exception("Unable to delete folder: " + folder, ex); + } + } + } + + public static void DeleteFolder(string path) + { + if (!Directory.Exists(path)) + return; + + DeleteFolderContent(path); + + Retry(5, () => Directory.Delete(path, true)); + } + } +} diff --git a/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs b/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs new file mode 100644 index 0000000..ee69aa8 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/GenerateCapnpFileCodeBehindTask.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Resources; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class GenerateCapnpFileCodeBehindTask : Task + { + public GenerateCapnpFileCodeBehindTask() + { + CodeBehindGenerator = new FeatureFileCodeBehindGenerator(Log); + } + + public ICapnpCsharpGenerator CodeBehindGenerator { get; set; } + + [Required] + public string ProjectPath { get; set; } + + public string RootNamespace { get; set; } + + public string ProjectFolder => Path.GetDirectoryName(ProjectPath); + public string OutputPath { get; set; } + + public ITaskItem[] CapnpFiles { get; set; } + + public ITaskItem[] GeneratorPlugins { get; set; } + + [Output] + public ITaskItem[] GeneratedFiles { get; private set; } + + public override bool Execute() + { + try + { + try + { + var currentProcess = Process.GetCurrentProcess(); + + Log.LogWithNameTag(Log.LogMessage, $"process: {currentProcess.ProcessName}, pid: {currentProcess.Id}, CD: {Environment.CurrentDirectory}"); + + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + Log.LogWithNameTag(Log.LogMessage, " " + assembly.FullName); + } + } + catch (Exception e) + { + Log.LogWithNameTag(Log.LogMessage, $"Error when dumping process info: {e}"); + } + + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + + var generator = CodeBehindGenerator ?? new FeatureFileCodeBehindGenerator(Log); + + Log.LogWithNameTag(Log.LogMessage, "Starting GenerateFeatureFileCodeBehind"); + + var generatorPlugins = GeneratorPlugins?.Select(gp => gp.ItemSpec).ToList() ?? new List(); + + var capnpFiles = CapnpFiles?.Select(i => i.ItemSpec).ToList() ?? new List(); + + var generatedFiles = generator.GenerateFilesForProject( + ProjectPath, + RootNamespace, + capnpFiles, + generatorPlugins, + ProjectFolder, + OutputPath); + + GeneratedFiles = generatedFiles.Select(file => new TaskItem { ItemSpec = file }).ToArray(); + + return !Log.HasLoggedErrors; + } + catch (Exception e) + { + if (e.InnerException != null) + { + if (e.InnerException is FileLoadException fle) + { + Log?.LogWithNameTag(Log.LogError, $"FileLoadException Filename: {fle.FileName}"); + Log?.LogWithNameTag(Log.LogError, $"FileLoadException FusionLog: {fle.FusionLog}"); + Log?.LogWithNameTag(Log.LogError, $"FileLoadException Message: {fle.Message}"); + } + + Log?.LogWithNameTag(Log.LogError, e.InnerException.ToString()); + } + + Log?.LogWithNameTag(Log.LogError, e.ToString()); + return false; + } + finally + { + AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomain_AssemblyResolve; + } + } + + private System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + Log.LogWithNameTag(Log.LogMessage, args.Name); + + + return null; + } + + } + + +} \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs b/Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs new file mode 100644 index 0000000..1343a80 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/Helper/LogExtensions.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Build.Utilities; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public static class LogExtensions + { + public static void LogWithNameTag( + this TaskLoggingHelper loggingHelper, + Action loggingMethod, + string message, + params object[] messageArgs) + { + string fullMessage = $"[SpecFlow] {message}"; + loggingMethod?.Invoke(fullMessage, messageArgs); + } + } +} diff --git a/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs b/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs new file mode 100644 index 0000000..5368331 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/ICapnpCsharpGenerator.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public interface ICapnpCsharpGenerator + { + IEnumerable GenerateFilesForProject(string projectPath, string rootNamespace, List CapnpFiles, List generatorPlugins, string projectFolder, string outputPath); + } +} \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/TestFileGeneratorResult.cs b/Capnpc.Csharp.MsBuild.Generation/TestFileGeneratorResult.cs new file mode 100644 index 0000000..5eb0160 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/TestFileGeneratorResult.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class TestFileGeneratorResult + { + public TestFileGeneratorResult(TestGeneratorResult generatorResult, string fileName) + { + if (generatorResult == null) + { + throw new ArgumentNullException(nameof(generatorResult)); + } + + Filename = fileName ?? throw new ArgumentNullException(nameof(fileName)); + + Errors = generatorResult.Errors; + IsUpToDate = generatorResult.IsUpToDate; + GeneratedTestCode = generatorResult.GeneratedTestCode; + } + + /// + /// The errors, if any. + /// + public IEnumerable Errors { get; } + + /// + /// The generated file was up-to-date. + /// + public bool IsUpToDate { get; } + + /// + /// The generated test code. + /// + public string GeneratedTestCode { get; } + + public bool Success => Errors == null || !Errors.Any(); + + public string Filename { get; } + } +} \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/TestGenerationError.cs b/Capnpc.Csharp.MsBuild.Generation/TestGenerationError.cs new file mode 100644 index 0000000..1bf834c --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/TestGenerationError.cs @@ -0,0 +1,6 @@ +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class TestGenerationError + { + } +} \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/TestGeneratorResult.cs b/Capnpc.Csharp.MsBuild.Generation/TestGeneratorResult.cs new file mode 100644 index 0000000..35b34e3 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/TestGeneratorResult.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace Capnpc.Csharp.MsBuild.Generation +{ + public class TestGeneratorResult + { + public IEnumerable Errors { get; internal set; } + public bool IsUpToDate { get; internal set; } + public string GeneratedTestCode { get; internal set; } + } +} \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/CpsExtension.DesignTime.targets b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/CpsExtension.DesignTime.targets new file mode 100644 index 0000000..b23690f --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/CpsExtension.DesignTime.targets @@ -0,0 +1,15 @@ + + + + + $(MSBuildThisFileDirectory)Rules\ + + + + + + + File;BrowseObject + + + \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/FeatureFileType.xaml b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/FeatureFileType.xaml new file mode 100644 index 0000000..0864f1e --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/FeatureFileType.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/ProjectItemsSchema.xaml b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/ProjectItemsSchema.xaml new file mode 100644 index 0000000..47300db --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/build/CPS/Buildsystem/Rules/ProjectItemsSchema.xaml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props new file mode 100644 index 0000000..e51a8aa --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.props @@ -0,0 +1,77 @@ + + + + $(MSBuildThisFileDirectory)CPS\Buildsystem\CpsExtension.DesignTime.targets + + + + + + + false + $(CapnpcCsharp_UseHostCompilerIfAvailable) + + + + + false + false + + false + true + false + + <_SpecFlowPropsImported Condition="'$(_SpecFlowPropsImported)'==''">true + + + + + + + false + + + true + $(CapnpcCsharp_EnableDefaultCompileItems) + + $(DefaultItemExcludes);**/*.feature + + + + + %(RelativeDir)%(Filename).capnp(DefaultLanguageSourceExtension) + $(UsingMicrosoftNETSdk) + + + + + + + + + + + + + + + <_CapnpcCsharp_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_CapnpcCsharp_TaskFolder)' == ''">netcoreapp2.0 + <_CapnpcCsharp_TaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' And '$(_CapnpcCsharp_TaskFolder)' == ''">net471 + <_CapnpcCsharp_TaskAssembly Condition=" '$(_CapnpcCsharp_TaskAssembly)' == '' ">..\tasks\$(_CapnpcCsharp_TaskFolder)\Capnpc.Csharp.MsBuild.Generation.dll + + + + + diff --git a/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.targets b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.targets new file mode 100644 index 0000000..a61cde4 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.targets @@ -0,0 +1,145 @@ + + + + + + false + true + + + <_CapnpcCsharp_EnableDefaultCompileItems Condition="'$(CapnpcCsharp_EnableDefaultCompileItems)' == '' And '$(UsingMicrosoftNETSdk)' == 'true'">true + <_CapnpcCsharp_EnableDefaultCompileItems Condition="'$(CapnpcCsharp_EnableDefaultCompileItems)' == 'true' And '$(UsingMicrosoftNETSdk)' == 'true'">true + + + + + BeforeUpdateCapnpFilesInProject; + UpdateCapnpFilesInProject; + IncludeCodeBehindFilesInProject; + AfterUpdateCapnpFilesInProject; + $(BuildDependsOn) + + + CleanCapnpFilesInProject; + $(CleanDependsOn) + + + SwitchToForceGenerate; + $(RebuildDependsOn) + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks new file mode 100644 index 0000000..9e4e977 --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/build/Capnpc.Csharp.MsBuild.Generation.tasks @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props b/Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props new file mode 100644 index 0000000..f3c49ea --- /dev/null +++ b/Capnpc.Csharp.MsBuild.Generation/buildMultiTargeting/Capnpc.Csharp.MsBuild.Generation.props @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Licenses/SpecFlow-LICENSE-NewBSD.txt b/Licenses/SpecFlow-LICENSE-NewBSD.txt new file mode 100644 index 0000000..665e1cd --- /dev/null +++ b/Licenses/SpecFlow-LICENSE-NewBSD.txt @@ -0,0 +1,31 @@ +SpecFlow Licence (New BSD License) + +Copyright (c) 2009, TechTalk + +Disclaimer: + * The initial codebase of Specflow was written by TechTalk employees. + No 3rd party code was included. + * No code of customer projects was used to create this project. + * TechTalk had the full rights to publish the initial codebase. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the SpecFlow project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL TECHTALK OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.