Skip to content

Commit

Permalink
package builder - simple builds working
Browse files Browse the repository at this point in the history
  • Loading branch information
hlotyaks committed Jan 5, 2020
1 parent 9873b82 commit 7173308
Show file tree
Hide file tree
Showing 25 changed files with 444 additions and 53 deletions.
63 changes: 47 additions & 16 deletions GraphBuilder/src/Graph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,76 @@ namespace PackageAnalyzer
{
public class Graph
{
private Dictionary<string, List<string>> nodes;

private Dictionary<string, List<string>> directednodes;
private Dictionary<string, List<string>> parentnodes;
public Graph()
{
nodes = new Dictionary<string, List<string>>();
directednodes = new Dictionary<string, List<string>>();
parentnodes = new Dictionary<string, List<string>>();
}

public List<string> Paths(string V)
{
return directednodes[V];
}

public int NodeCount
{
get { return nodes.Keys.Count; }
get { return directednodes.Keys.Count; }
}

public List<string> Edges(string node)
public List<string> Children(string node)
{
return nodes[node];
return parentnodes[node];
}

public void AddEdge(string V)
{
if (nodes.ContainsKey(V))
if (!directednodes.ContainsKey(V))
{
return;
directednodes.Add(V, new List<string>());
}

nodes.Add(V, new List<string>());
if (!parentnodes.ContainsKey(V))
{
parentnodes.Add(V, new List<string>());
}

}

public void AddEdge(string V, string E)
{
if (nodes.ContainsKey(V))
// Add vertex and edge information
// Example:
// A <= B
// B is the vertex and A is the edge between B and A
// A is also the parent of B
if (directednodes.ContainsKey(V))
{
nodes[V].Add(E);
directednodes[V].Add(E);
}
else
{
nodes.Add(V, new List<string>());
nodes[V].Add(E);
directednodes.Add(V, new List<string>());
directednodes[V].Add(E);
}

// If this vertex has not been seen yet also store it as a possible parent
if(!parentnodes.ContainsKey(V))
{
parentnodes.Add(V, new List<string>());
}

// Add the edge and vertex parent information
if (parentnodes.ContainsKey(E))
{
parentnodes[E].Add(V);
}
else
{
parentnodes.Add(E, new List<string>());
parentnodes[E].Add(V);
}
}

private bool IsCyclicInternal(string V, Dictionary<string, bool> visited, Dictionary<string, bool> recursive)
Expand All @@ -53,7 +84,7 @@ private bool IsCyclicInternal(string V, Dictionary<string, bool> visited, Dictio
visited[V] = true;
recursive[V] = true;

foreach (string E in nodes[V])
foreach (string E in directednodes[V])
{
if (!visited[E] && IsCyclicInternal(E, visited, recursive))
{
Expand All @@ -80,13 +111,13 @@ public bool IsCyclic
Dictionary<string, bool> recursive = new Dictionary<string, bool>();

// initialize the search...
foreach (string V in nodes.Keys)
foreach (string V in directednodes.Keys)
{
visited.Add(V,false);
recursive.Add(V,false);
}

foreach (string V in nodes.Keys)
foreach (string V in directednodes.Keys)
{
if (IsCyclicInternal(V, visited, recursive))
{
Expand Down
22 changes: 21 additions & 1 deletion GraphBuilder/tests/Graph.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,20 @@ public void SimpleGraphCycleTest2()
GraphBuilder gb = GraphTestUtilities.PopulateGB("simplecycle2");

Assert.IsFalse(gb.Graph.IsCyclic);
Assert.AreEqual(1, gb.Graph.Paths("A").Count);
Assert.IsTrue(gb.Graph.Paths("A").Contains("B"));

}


[TestMethod]
public void SimplGraphCycleTest3()
public void SimpleGraphCycleTest3()
{
GraphBuilder gb = GraphTestUtilities.PopulateGB("simplecycle3");

Assert.IsTrue(gb.Graph.IsCyclic);
Assert.AreEqual(1, gb.Graph.Paths("B").Count);
Assert.IsTrue(gb.Graph.Paths("B").Contains("C"));
}

[TestMethod]
Expand All @@ -44,6 +49,21 @@ public void IntermediateGraphCycleTest1()
GraphBuilder gb = GraphTestUtilities.PopulateGB("intermediatecycle1");

Assert.IsFalse(gb.Graph.IsCyclic);

Assert.AreEqual(2, gb.Graph.Children("A").Count);
Assert.IsTrue(gb.Graph.Children("A").Contains("B"));
Assert.IsTrue(gb.Graph.Children("A").Contains("C"));

Assert.AreEqual(0, gb.Graph.Children("B").Count);

Assert.AreEqual(2, gb.Graph.Children("C").Count);
Assert.IsTrue(gb.Graph.Children("C").Contains("D"));
Assert.IsTrue(gb.Graph.Children("C").Contains("E"));

Assert.AreEqual(1, gb.Graph.Children("D").Count);
Assert.IsTrue(gb.Graph.Children("D").Contains("E"));

Assert.AreEqual(0, gb.Graph.Children("E").Count);
}
}
}
23 changes: 17 additions & 6 deletions GraphBuilder/tests/GraphBuilder.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ public void SimpleGraphBuilderTest1()

Assert.AreEqual(1, gb.Graph.NodeCount);

Assert.AreEqual(0, gb.Graph.Edges("A").Count);
Assert.AreEqual(0, gb.Graph.Paths("A").Count);

Assert.AreEqual(0, gb.Graph.Children("A").Count);

}

Expand All @@ -35,10 +37,11 @@ public void SimpleGraphBuilderTest2()

Assert.AreEqual(2, gb.Graph.NodeCount);

Assert.AreEqual(1, gb.Graph.Edges("A").Count);
Assert.AreEqual(1, gb.Graph.Paths("A").Count);

Assert.AreEqual(0, gb.Graph.Edges("B").Count);
Assert.AreEqual(0, gb.Graph.Paths("B").Count);

Assert.AreEqual(1, gb.Graph.Children("B").Count);
}

[TestMethod]
Expand All @@ -48,11 +51,19 @@ public void SimpleGraphBuilderTest3()

Assert.AreEqual(3, gb.Graph.NodeCount);

Assert.AreEqual(2, gb.Graph.Edges("A").Count);
Assert.AreEqual(2, gb.Graph.Paths("A").Count);

Assert.AreEqual(0, gb.Graph.Paths("B").Count);

Assert.AreEqual(0, gb.Graph.Paths("C").Count);

Assert.IsTrue(gb.Graph.Paths("A").Contains("B"));

Assert.IsTrue(gb.Graph.Paths("A").Contains("C"));

Assert.AreEqual(0, gb.Graph.Edges("B").Count);
Assert.AreEqual(1, gb.Graph.Children("B").Count);

Assert.AreEqual(0, gb.Graph.Edges("C").Count);
Assert.AreEqual(1, gb.Graph.Children("C").Count);
}


Expand Down
131 changes: 117 additions & 14 deletions PackageBuilder/src/PackageBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,139 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.IO.Compression;

namespace PackageAnalyzer
{
public class PackageBuilder
{
// location relative to specified rootFolder.
const string PACKAGE_DESCRIPTIONS = "//packages//descriptions";
const string PACKAGE_CACHE = "//packages//cache";
const string PACKAGE_SOURCE = "//src";
const string PACKAGE_OUTPUT = "//output";
const string PACKAGE_DESCRIPTIONS = @"\packages\descriptions\";
const string PACKAGE_CACHE = @"\packages\cache\";
const string PACKAGE_SOURCE = @"\src\";
const string PACKAGE_OUTPUT = @"\output\";
const string PACKAGE_EXTENSION = ".zip";
string _cacheRoot;
string _sourceRoot;
string _outputRoot;
string _packageRoot;



public PackageBuilder()
List<string> _cacheContents;
public PackageBuilder(string rootFolder)
{
_cacheRoot = $"{rootFolder}{PACKAGE_CACHE}";
_sourceRoot = $"{rootFolder}{PACKAGE_SOURCE}";
_outputRoot = $"{rootFolder}{PACKAGE_OUTPUT}";
_packageRoot = $"{rootFolder}{PACKAGE_DESCRIPTIONS}";

// create output dir if not exist.
if(!Directory.Exists(_outputRoot))
{
Directory.CreateDirectory(_outputRoot);
}

// initialize cache contents
DirectoryInfo di = new DirectoryInfo(_cacheRoot);
_cacheContents = di.GetFiles().Select(f => f.Name.Substring(0,f.Name.Length - PACKAGE_EXTENSION.Length)).ToList();
}

public Dictionary<string, bool> Build(string startNode, string sourceFolder, Dictionary<string, string> hash, Graph g)
{
List<string> packages = new List<string>();
packages.Add(startNode);

Dictionary<string, bool> packageBuildResults = new Dictionary<string, bool>();

BuildPackages(packages, hash, g, packageBuildResults);

public List<string> Build(string startNode, string rootFolder)
return packageBuildResults;
}

private void BuildPackages(List<string> packages, Dictionary<string, string> hash, Graph g, Dictionary<string, bool> buildResults)
{
List<string> builtPackages = new List<string>();
if (packages.Count == 0)
{
return;
}

List<string> children = new List<string>();

// if the parent built then this needs to build
// if the hash version doesn't exist in cache the this needs to build
// if the hash version exists in cache then copy to output

bool forceBuild;

foreach (string package in packages)
{
bool packagebuilt;
buildResults.TryGetValue(package, out packagebuilt);
if (packagebuilt)
{
continue;
}

forceBuild = false;

// if the packages dependency was built then this package will beuilt too.
foreach (string k in buildResults.Keys)
{
if (g.Paths(package).Contains(k) && buildResults[k])
{
forceBuild = true;
}
}

buildResults.Add(package, BuildPackage(package, hash, forceBuild));

children.AddRange(g.Children(package));
}

GraphBuilder gb = new GraphBuilder($"{rootFolder}{PACKAGE_DESCRIPTIONS}");
BuildPackages(children, hash, g, buildResults);
}

if (gb.Graph.IsCyclic)
private bool BuildPackage(string package, Dictionary<string, string> hash, bool force)
{
// in this context all building means is getting a zip version of the package with hash extention to the output folder.
// this can happen 1 of 2 ways. 1) copy the existing one from cache or 2) zip the src add a new one to cache and output

string packageHashName = $"{hash[package]}";
if (!PackageExists(packageHashName) || force)
{
Compile(package, packageHashName);
CopyOutputToCache(packageHashName);
return true; // true because package did 'build'
}
else
{
// need logging. throw cycle exception
throw new Exceptions.PackageBuilderCycleException("Cycle detected in package graph.");
CopyCacheToOuput(packageHashName);
return false; // false because package didn't 'build'
}

return builtPackages;
}

private bool PackageExists(string packageHashName)
{
return _cacheContents.Contains(packageHashName);
}

private void CopyCacheToOuput(string packageHashName)
{
File.Copy($"{_cacheRoot}{packageHashName}.zip", $"{_outputRoot}{packageHashName}.zip", true);
}

private void CopyOutputToCache(string packageHashName)
{
File.Copy($"{_outputRoot}{packageHashName}.zip", $"{_cacheRoot}{packageHashName}.zip", true);
}

private void Compile(string package, string packageHashName)
{
string source = $"{_sourceRoot}{package}";
string dest = $"{_outputRoot}{packageHashName}.zip";

ZipFile.CreateFromDirectory($"{_sourceRoot}{package}", $"{_outputRoot}{packageHashName}.zip");
}
}
}
Loading

0 comments on commit 7173308

Please sign in to comment.