Permalink
Browse files

first implementation of dependency checking

  • Loading branch information...
1 parent 853713b commit f88478bbb92c6b43350718c706938dc3df4b3634 Lucas Meijer committed Apr 14, 2012
Showing with 217 additions and 86 deletions.
  1. +20 −0 BuildHistory.cs
  2. +19 −13 DependencyGraph.cs
  3. +36 −0 GenerationRecord.cs
  4. +11 −0 TargetBuildInstructions.cs
  5. +3 −73 Tests/DependencyGraphTests.cs
  6. +46 −0 Tests/SimpleCopyDepGraph.cs
  7. +77 −0 Tests/TargetWithoutSources.cs
  8. +5 −0 bs.csproj
View
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+
+namespace bs
+{
+ internal class BuildHistory
+ {
+ private readonly Dictionary<string, GenerationRecord> _records = new Dictionary<string, GenerationRecord>();
+
+ public void AddRecord(GenerationRecord record)
+ {
+ _records[record.TargetFile] = record;
+ }
+
+ public GenerationRecord FindRecordFor(string targetFile)
+ {
+ return _records[targetFile];
+ }
+ }
+}
View
@@ -7,37 +7,43 @@ namespace bs
{
public class DependencyGraph
{
- readonly Dictionary<string, TargetBuildInstructions> graph = new Dictionary<string, TargetBuildInstructions>();
+ readonly Dictionary<string, TargetBuildInstructions> _graph = new Dictionary<string, TargetBuildInstructions>();
public Action<string, TargetBuildInstructions> GenerateCallback = (s, i) => { };
+ private readonly BuildHistory _buildHistory = new BuildHistory();
public void RequestTarget(string targetFile)
{
- var instructions = graph[targetFile];
-
+ var instructions = _graph[targetFile];
+
if (instructions.SourceFiles.Any(sourceFile => !File.Exists(sourceFile)))
throw new MissingDependencyException();
- if (File.Exists(targetFile))
- return;
+ if (NeedToGenerate(targetFile, instructions))
+ Generate(targetFile, instructions);
+ }
+
+ private bool NeedToGenerate(string targetFile, TargetBuildInstructions instructions)
+ {
+ if (!File.Exists(targetFile))
+ return true;
+
+ var recordOfLastBuild = _buildHistory.FindRecordFor(targetFile);
- Generate(targetFile, instructions);
+ return instructions.SourceFiles.Any(sourceFile => recordOfLastBuild.ModificationTimeOf(sourceFile) != File.GetLastWriteTimeUtc(sourceFile));
}
private void Generate(string targetFile, TargetBuildInstructions instructions)
{
GenerateCallback(targetFile, instructions);
instructions.Action(targetFile, instructions.SourceFiles);
+
+ var record = new GenerationRecord(targetFile, instructions.SourceFiles);
+ _buildHistory.AddRecord(record);
}
public void RegisterTarget(string targetFile, TargetBuildInstructions instructions)
{
- graph.Add(targetFile,instructions);
+ _graph.Add(targetFile,instructions);
}
}
-
- public class TargetBuildInstructions
- {
- public Action<string, IEnumerable<string>> Action;
- public IEnumerable<string> SourceFiles;
- }
}
View
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+namespace bs
+{
+ internal class GenerationRecord
+ {
+ public string TargetFile { get; private set; }
+ public IEnumerable<string> SourceFiles { get; private set; }
+
+ private readonly Dictionary<string, DateTime> _modificationDates = new Dictionary<string, DateTime>();
+
+ public GenerationRecord(string targetFile, IEnumerable<string> sourceFiles)
+ {
+ TargetFile = targetFile;
+ SourceFiles = sourceFiles;
+
+ RecordModificationDate(targetFile);
+ foreach (var sourceFile in sourceFiles)
+ RecordModificationDate(sourceFile);
+ }
+
+ private void RecordModificationDate(string file)
+ {
+ if (!File.Exists(file))
+ throw new ArgumentException("GenerationRecord being generated with inputfile: "+file+" which does not exist.");
+ _modificationDates.Add(file,File.GetLastWriteTimeUtc(file));
+ }
+
+ public DateTime ModificationTimeOf(string file)
+ {
+ return _modificationDates[file];
+ }
+ }
+}
View
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace bs
+{
+ public class TargetBuildInstructions
+ {
+ public Action<string, IEnumerable<string>> Action;
+ public IEnumerable<string> SourceFiles;
+ }
+}
@@ -1,8 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
+using System.IO;
using NUnit.Framework;
namespace bs.Tests
@@ -21,73 +17,7 @@ public void Setup()
Directory.SetCurrentDirectory(dirName);
}
- const string defaultSourceFile = "input.txt";
- const string defaulttargetFile = "output.txt";
-
- [Test]
- public void WillGenerateTargetWithoutSources()
- {
- DependencyGraph depGraph = SetupGraphWithOneTargetWithoutSources();
-
- depGraph.RequestTarget(defaulttargetFile);
- FileAssert.Contains(defaulttargetFile, "Hello");
- }
-
- [Test]
- public void WontGenerateTargetWithoutSourcesTwice()
- {
- DependencyGraph depGraph = SetupGraphWithOneTargetWithoutSources();
-
- depGraph.RequestTarget(defaulttargetFile);
- FileAssert.Contains(defaulttargetFile, "Hello");
- depGraph.GenerateCallback += (target, instructions) => { throw new InvalidOperationException(); };
- depGraph.RequestTarget(defaulttargetFile);
- }
-
- private static DependencyGraph SetupGraphWithOneTargetWithoutSources()
- {
- var depGraph = new DependencyGraph();
- depGraph.RegisterTarget(defaulttargetFile, new TargetBuildInstructions()
- {
- Action = (target,sources) => File.WriteAllText(target, "Hello"),
- SourceFiles = new string[0],
- });
- return depGraph;
- }
-
-
- [Test]
- public void ThrowsIfDependencyDoesNotExist()
- {
- DependencyGraph depGraph = SetupSimpleCopyDepGraph();
- Assert.Throws<MissingDependencyException>(() => depGraph.RequestTarget(defaulttargetFile));
- }
-
- [Test]
- public void RegeneratesWhenSourceChanges()
- {
- DependencyGraph depGraph = SetupSimpleCopyDepGraph();
- Assert.Throws<MissingDependencyException>(() => depGraph.RequestTarget(defaulttargetFile));
-
- File.WriteAllText(defaultSourceFile, "One");
- depGraph.RequestTarget(defaulttargetFile);
- FileAssert.Contains(defaultSourceFile, "One");
-
- File.WriteAllText(defaultSourceFile, "Two");
- depGraph.RequestTarget(defaulttargetFile);
- FileAssert.Contains(defaultSourceFile, "Two");
- }
-
- private static DependencyGraph SetupSimpleCopyDepGraph()
- {
- var depGraph = new DependencyGraph();
-
- depGraph.RegisterTarget(defaulttargetFile, new TargetBuildInstructions()
- {
- Action = (target,sources) => File.Copy(sources.Single(), target, true),
- SourceFiles = new[] { defaultSourceFile }
- });
- return depGraph;
- }
+ protected const string defaultSourceFile = "input.txt";
+ protected const string defaulttargetFile = "output.txt";
}
}
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace bs.Tests
+{
+ class SimpleCopyDepGraph : DependencyGraphTests
+ {
+ [Test]
+ public void ThrowsIfDependencyDoesNotExist()
+ {
+ DependencyGraph depGraph = SetupSimpleCopyDepGraph();
+ Assert.Throws<MissingDependencyException>(() => depGraph.RequestTarget(defaulttargetFile));
+ }
+
+ [Test]
+ public void RegeneratesWhenSourceChanges()
+ {
+ DependencyGraph depGraph = SetupSimpleCopyDepGraph();
+ Assert.Throws<MissingDependencyException>(() => depGraph.RequestTarget(defaulttargetFile));
+
+ File.WriteAllText(defaultSourceFile, "One");
+ depGraph.RequestTarget(defaulttargetFile);
+ FileAssert.Contains(defaulttargetFile, "One");
+
+ File.WriteAllText(defaultSourceFile, "Two");
+ depGraph.RequestTarget(defaulttargetFile);
+ FileAssert.Contains(defaulttargetFile, "Two");
+ }
+
+ private static DependencyGraph SetupSimpleCopyDepGraph()
+ {
+ var depGraph = new DependencyGraph();
+
+ depGraph.RegisterTarget(defaulttargetFile, new TargetBuildInstructions()
+ {
+ Action = (target,sources) => File.Copy(sources.Single(), target, true),
+ SourceFiles = new[] { defaultSourceFile }
+ });
+ return depGraph;
+ }
+ }
+}
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace bs.Tests
+{
+ class TargetWithoutSources : DependencyGraphTests
+ {
+ [Test]
+ public void WillGenerateTargetWithoutSources()
+ {
+ DependencyGraph depGraph = SetupGraphWithOneTargetWithoutSources();
+
+ depGraph.RequestTarget(defaulttargetFile);
+ FileAssert.Contains(defaulttargetFile, "Hello");
+ }
+
+ [Test]
+ public void WontGenerateTargetWithoutSourcesTwice()
+ {
+ DependencyGraph depGraph = SetupGraphWithOneTargetWithoutSources();
+
+ depGraph.RequestTarget(defaulttargetFile);
+ FileAssert.Contains(defaulttargetFile, "Hello");
+ depGraph.GenerateCallback += (target, instructions) => { throw new InvalidOperationException(); };
+ depGraph.RequestTarget(defaulttargetFile);
+ }
+
+ private static DependencyGraph SetupGraphWithOneTargetWithoutSources()
+ {
+ var depGraph = new DependencyGraph();
+ depGraph.RegisterTarget(defaulttargetFile, new TargetBuildInstructions()
+ {
+ Action = (target,sources) => File.WriteAllText(target, "Hello"),
+ SourceFiles = new string[0],
+ });
+ return depGraph;
+ }
+
+ [Test]
+ public void ThrowsIfDependencyDoesNotExist()
+ {
+ DependencyGraph depGraph = SetupSimpleCopyDepGraph();
+ Assert.Throws<MissingDependencyException>(() => depGraph.RequestTarget(defaulttargetFile));
+ }
+
+ [Test]
+ public void RegeneratesWhenSourceChanges()
+ {
+ DependencyGraph depGraph = SetupSimpleCopyDepGraph();
+ Assert.Throws<MissingDependencyException>(() => depGraph.RequestTarget(defaulttargetFile));
+
+ File.WriteAllText(defaultSourceFile, "One");
+ depGraph.RequestTarget(defaulttargetFile);
+ FileAssert.Contains(defaultSourceFile, "One");
+
+ File.WriteAllText(defaultSourceFile, "Two");
+ depGraph.RequestTarget(defaulttargetFile);
+ FileAssert.Contains(defaultSourceFile, "Two");
+ }
+
+ private static DependencyGraph SetupSimpleCopyDepGraph()
+ {
+ var depGraph = new DependencyGraph();
+
+ depGraph.RegisterTarget(defaulttargetFile, new TargetBuildInstructions()
+ {
+ Action = (target,sources) => File.Copy(sources.Single(), target, true),
+ SourceFiles = new[] { defaultSourceFile }
+ });
+ return depGraph;
+ }
+ }
+}
View
@@ -44,12 +44,17 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
+ <Compile Include="BuildHistory.cs" />
<Compile Include="FileAssert.cs" />
+ <Compile Include="GenerationRecord.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="TargetBuildInstructions.cs" />
<Compile Include="Tests\DependencyGraphTests.cs" />
<Compile Include="DependencyGraph.cs" />
<Compile Include="MissingDependencyException.cs" />
+ <Compile Include="Tests\SimpleCopyDepGraph.cs" />
+ <Compile Include="Tests\TargetWithoutSources.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

0 comments on commit f88478b

Please sign in to comment.