Skip to content

Commit

Permalink
first version of generator tool + msbuild task
Browse files Browse the repository at this point in the history
  • Loading branch information
gasparnagy committed Nov 24, 2009
1 parent 9be167e commit 8a83666
Show file tree
Hide file tree
Showing 14 changed files with 521 additions and 8 deletions.
97 changes: 97 additions & 0 deletions Generator/BatchGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using TechTalk.SpecFlow.Generator.Configuration;

namespace TechTalk.SpecFlow.Generator
{
public class BatchGenerator
{
private readonly TextWriter traceWriter;
private readonly bool verboseOutput;

public BatchGenerator(TextWriter traceWriter, bool verboseOutput)
{
this.traceWriter = traceWriter;
this.verboseOutput = verboseOutput;
}

public void ProcessProject(SpecFlowProject specFlowProject, bool forceGenerate)
{
if (verboseOutput)
traceWriter.WriteLine("Processing project: " + specFlowProject);

SpecFlowGenerator generator = new SpecFlowGenerator(specFlowProject);

foreach (var featureFile in specFlowProject.FeatureFiles)
{
string featureFileFullPath = featureFile.GetFullPath(specFlowProject);

string codeFileFullPath = featureFileFullPath + ".cs";

bool needsProcessing = true;

if (!forceGenerate && IsUpToDate(generator, featureFileFullPath, codeFileFullPath))
{
needsProcessing = false;
}

if (verboseOutput || needsProcessing)
{
traceWriter.WriteLine("Processing file: {0}", featureFileFullPath);
if (!needsProcessing)
traceWriter.WriteLine(" up-to-date");
}

if (needsProcessing)
{
GenerateFile(generator, featureFile, codeFileFullPath);
}
}
}

protected virtual void GenerateFile(SpecFlowGenerator generator, SpecFlowFeatureFile featureFile, string codeFileFullPath)
{
using (var writer = GetWriter(codeFileFullPath))
{
generator.GenerateCSharpTestFile(featureFile, writer);
}
}

protected virtual StreamWriter GetWriter(string codeFileFullPath)
{
return new StreamWriter(codeFileFullPath, false, Encoding.UTF8);
}

private static bool IsUpToDate(SpecFlowGenerator generator, string featureFileFullPath, string codeFileFullPath)
{
// check existance of the target file
if (!File.Exists(codeFileFullPath))
return false;

// check modification time of the target file
try
{
var featureFileModificationTime = File.GetLastWriteTime(featureFileFullPath);
var codeFileModificationTime = File.GetLastWriteTime(codeFileFullPath);

if (featureFileModificationTime > codeFileModificationTime)
return false;
}
catch (Exception ex)
{
Debug.WriteLine(ex);
return false;
}

// check tools version
var codeFileVersion = generator.GetGeneratedFileSpecFlowVersion(codeFileFullPath);
if (codeFileVersion == null || codeFileVersion != generator.GetCurrentSpecFlowVersion())
return false;

return true;
}
}
}
File renamed without changes.
9 changes: 7 additions & 2 deletions Generator/Configuration/MsBuildProjectReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ public static class MsBuildProjectReader
public static SpecFlowProject LoadSpecFlowProjectFromMsBuild(string projectFile)
{
projectFile = Path.GetFullPath(projectFile);
Project project = new Project();
project.Load(projectFile, ProjectLoadSettings.IgnoreMissingImports);

Project project = Engine.GlobalEngine.GetLoadedProject(projectFile);
if (project == null)
{
project = new Project();
project.Load(projectFile, ProjectLoadSettings.IgnoreMissingImports);
}

string projectFolder = Path.GetDirectoryName(projectFile);

Expand Down
1 change: 1 addition & 0 deletions Generator/TechTalk.SpecFlow.Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
<Compile Include="..\VersionInfo.cs">
<Link>VersionInfo.cs</Link>
</Compile>
<Compile Include="BatchGenerator.cs" />
<Compile Include="Configuration\GeneratorConfiguration.cs" />
<Compile Include="Configuration\GeneratorConfigurationReader.cs" />
<Compile Include="Configuration\MsBuildProjectReader.cs" />
Expand Down
12 changes: 6 additions & 6 deletions TechTalk.SpecFlow.sln
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevenvSetupCustomAction", "
EndProject
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "SpecFlowSetup", "Installer\SpecFlowSetup\SpecFlowSetup.vdproj", "{F6740296-282C-4A0F-941E-A8FD1B1DAC2D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechTalk.SpecFlow.GeneratorTools", "GeneratorTools\TechTalk.SpecFlow.GeneratorTools.csproj", "{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechTalk.SpecFlow.Generator", "Generator\TechTalk.SpecFlow.Generator.csproj", "{453D8014-B6CD-4E86-80A8-D59F59092334}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TechTalk.SpecFlow.Tools", "Tools\TechTalk.SpecFlow.Tools.csproj", "{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -73,14 +73,14 @@ Global
{F6740296-282C-4A0F-941E-A8FD1B1DAC2D}.Debug|Any CPU.ActiveCfg = Debug
{F6740296-282C-4A0F-941E-A8FD1B1DAC2D}.Release|Any CPU.ActiveCfg = Release
{F6740296-282C-4A0F-941E-A8FD1B1DAC2D}.Release|Any CPU.Build.0 = Release
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Release|Any CPU.Build.0 = Release|Any CPU
{453D8014-B6CD-4E86-80A8-D59F59092334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{453D8014-B6CD-4E86-80A8-D59F59092334}.Debug|Any CPU.Build.0 = Debug|Any CPU
{453D8014-B6CD-4E86-80A8-D59F59092334}.Release|Any CPU.ActiveCfg = Release|Any CPU
{453D8014-B6CD-4E86-80A8-D59F59092334}.Release|Any CPU.Build.0 = Release|Any CPU
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{87BE7FE6-C3DE-4409-ABF6-FA5B60AF3DE1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
File renamed without changes.
65 changes: 65 additions & 0 deletions Tools/MsBuild/GeneratorTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Build.Framework;
using TechTalk.SpecFlow.Generator;
using TechTalk.SpecFlow.Generator.Configuration;

namespace TechTalk.SpecFlow.Tools.MsBuild
{
internal class MsBuildBatchGenerator : BatchGenerator
{
private readonly GeneratorTaskBase task;

public MsBuildBatchGenerator(TextWriter traceWriter, bool verboseOutput, GeneratorTaskBase task) : base(traceWriter, verboseOutput)
{
this.task = task;
}

private GeneratorTaskBase.OutputFile outputFile = null;

protected override StreamWriter GetWriter(string codeFileFullPath)
{
outputFile = task.PrepareOutputFile(codeFileFullPath);

return base.GetWriter(outputFile.FilePathForWriting);
}

protected override void GenerateFile(SpecFlowGenerator generator, SpecFlowFeatureFile featureFile, string codeFileFullPath)
{
try
{
base.GenerateFile(generator, featureFile, codeFileFullPath);
outputFile.Done(task.Errors);
}
catch(Exception ex)
{
task.RecordException(ex);
}
finally
{
outputFile = null;
}
}
}

public class GeneratorTask : GeneratorTaskBase
{
public bool VerboseOutput { get; set; }
public bool ForceGeneration { get; set; }

[Required]
public string ProjectPath { get; set; }

protected override void DoExecute()
{
SpecFlowProject specFlowProject = MsBuildProjectReader.LoadSpecFlowProjectFromMsBuild(ProjectPath);

BatchGenerator batchGenerator = new MsBuildBatchGenerator(GetMessageWriter(), VerboseOutput, this);
batchGenerator.ProcessProject(specFlowProject, ForceGeneration);
}
}
}
148 changes: 148 additions & 0 deletions Tools/MsBuild/GeneratorTaskBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.IO;
using System.Linq;

namespace TechTalk.SpecFlow.Tools.MsBuild
{
public abstract class GeneratorTaskBase : TaskBase
{
public bool BuildServerMode { get; set; }
public bool OverwriteReadOnlyFiles { get; set; }

public class OutputFile
{
public string FilePath { get; private set; }

public virtual string FilePathForWriting
{
get { return FilePath; }
}

public OutputFile(string filePath)
{
FilePath = filePath;
}

public virtual void Done(CompilerErrorCollection result)
{
// nop;
}
}

public class VerifyDifferenceOutputFile : OutputFile
{
public string TempFilePath { get; private set; }

public override string FilePathForWriting
{
get { return TempFilePath; }
}

public VerifyDifferenceOutputFile(string filePath) : base(filePath)
{
TempFilePath = Path.Combine(Path.GetTempPath(), "tmp" + Path.GetFileName(filePath));
}

public override void Done(CompilerErrorCollection result)
{
Compare(TempFilePath, FilePath, result);
}

private void Compare(string path1, string path2, CompilerErrorCollection result)
{
if (FileCompare(path1, path2))
return;

string message = String.Format("Error during file generation. The target file '{0}' is read-only, but different from the transformation result. This problem can be a sign of an inconsistent source code package. Compile and check-in the current version of the file from the development environment or remove the read-only flag from the generation result. To compile a solution that contains messaging project on a build server, you can also exclude the messaging project from the build-server solution or set the <OverwriteReadOnlyFiles> msbuild project parameter to 'true' in the messaging project file.", Path.GetFullPath(path2));
result.Add(new CompilerError(String.Empty, 0, 0, null, message));
}
}

public OutputFile PrepareOutputFile(string outputFilePath)
{
if (OverwriteReadOnlyFiles)
{
RemoveReadOnly(outputFilePath);
}

bool isReadOnly = IsReadOnly(outputFilePath);
if (isReadOnly && BuildServerMode)
return new VerifyDifferenceOutputFile(outputFilePath);

return new OutputFile(outputFilePath);
}

// 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.
private 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);
}

private bool IsReadOnly(string path)
{
try
{
FileInfo fileInfo = new FileInfo(path);
if (!fileInfo.Exists)
return false;
return fileInfo.IsReadOnly;

}
catch (Exception ex)
{
Debug.WriteLine(ex, "IsReadOnly");

// if there is an exception, we let the generation to discover the real problem
return false;
}
}

private void RemoveReadOnly(string path)
{
FileInfo fileInfo = new FileInfo(path);
if (fileInfo.Exists && fileInfo.IsReadOnly)
fileInfo.IsReadOnly = false;
}
}
}
Loading

0 comments on commit 8a83666

Please sign in to comment.