Skip to content

Commit

Permalink
Hashing content of macrodef and caching the generated dll. No need to…
Browse files Browse the repository at this point in the history
… recompile it each time unless the macro changes. VS2010 solution. Some tests for handling macro redefinitions.
  • Loading branch information
sanjayprabhu committed Apr 18, 2011
1 parent da8ca12 commit 0bf40c4
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 18 deletions.
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
build
build
*.user
*.ncb
*.suo
[Bb]in
[Db]ebug*/
obj/
[Rr]elease*/
_ReSharper*/
4 changes: 3 additions & 1 deletion default.build
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<!-- User targets -->
<target name="clean" description="Delete Automated Build artifacts">
<delete dir="${build.dir}" if="${directory::exists(build.dir)}"/>
<delete dir="bin" if="${directory::exists('bin')}"/>
<delete dir="obj" if="${directory::exists('obj')}"/>
</target>

<target name="compile" description="Compiles using the AutomatedDebug Configuration">
Expand All @@ -24,7 +26,7 @@

<target name="full" depends="clean, test, dist" description="Compiles, tests, and produces distributions" />

<target name="dist">
<target name="dist" depends="doc">
</target>

<target name="test" depends="compile">
Expand Down
Binary file added lib/NAnt.Core.dll
Binary file not shown.
74 changes: 74 additions & 0 deletions macrodef.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0ACD9902-48B2-4C19-9653-4964A71ADC2A}</ProjectGuid>
<OutputType>Library</OutputType>
<NoStandardLibraries>false</NoStandardLibraries>
<AssemblyName>macrodef</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<RootNamespace>macrodef</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NAnt.Core">
<HintPath>lib\NAnt.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Content Include=".gitignore" />
<Content Include="COPYING.txt" />
<Content Include="doc\images\arrow.gif" />
<Content Include="doc\images\bullet.gif" />
<Content Include="doc\images\logo.gif" />
<Content Include="doc\style.css" />
<Content Include="README.txt" />
</ItemGroup>
<ItemGroup>
<None Include="default.build" />
<None Include="test\duplicate-redefinition.build" />
<None Include="test\duplicate.build" />
<None Include="test\tests.build" />
</ItemGroup>
<ItemGroup>
<Compile Include="src\AssemblyInfo.cs" />
<Compile Include="src\AssertFailTask.cs" />
<Compile Include="src\MacroAttribute.cs" />
<Compile Include="src\MacroDefInvocation.cs" />
<Compile Include="src\MacroDefSequential.cs" />
<Compile Include="src\MacroDefTask.cs" />
<Compile Include="src\MacroElement.cs" />
<Compile Include="src\SimpleCSharpCompiler.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSHARP.Targets" />
<ProjectExtensions>
<VisualStudio AllowExistingFolder="true" />
</ProjectExtensions>
</Project>
20 changes: 20 additions & 0 deletions macrodef.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "macrodef", "macrodef.csproj", "{0ACD9902-48B2-4C19-9653-4964A71ADC2A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0ACD9902-48B2-4C19-9653-4964A71ADC2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0ACD9902-48B2-4C19-9653-4964A71ADC2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0ACD9902-48B2-4C19-9653-4964A71ADC2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0ACD9902-48B2-4C19-9653-4964A71ADC2A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
57 changes: 49 additions & 8 deletions src/MacroDefTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using NAnt.Core;
using NAnt.Core.Attributes;
Expand Down Expand Up @@ -119,6 +121,12 @@ public string name
set { _name = value; }
}

protected override void InitializeXml(XmlNode elementNode, PropertyDictionary properties, FrameworkInfo framework)
{
base.InitializeXml(elementNode, properties, framework);
macrodefNode = elementNode;
}

public static void ExecuteTask(string name, XmlNode xml, Task task)
{
MacroDefTask macrodef = (MacroDefTask) macrodefs[name];
Expand All @@ -131,18 +139,49 @@ private void Invoke(XmlNode xml, Task task)
invocation.Execute();
}

protected override void ExecuteTask()
//Bad... does way too many things, should be moved to respective classes.
protected override void ExecuteTask()
{
macrodefs.Add(_name, this);
if(macrodefs.Contains(_name))
{
if (GetUniqueIdentifier() != ((MacroDefTask)macrodefs[_name]).GetUniqueIdentifier())
throw new BuildException("Different MacroDef with the name : " + _name + " already exists. Cannot redefine.", Location);
Log(Level.Info, string.Format("macrodef \"{0}\" already included.", _name));
return;
}
macrodefs[_name] = this;

CodeCompileUnit compileUnit = GenerateCode();
SimpleCSharpCompiler simpleCSharpCompiler = new SimpleCSharpCompiler();
SimpleCSharpCompiler simpleCSharpCompiler = new SimpleCSharpCompiler(GetUniqueIdentifier());

if(simpleCSharpCompiler.PrecompiledDllExists())
{
TypeFactory.ScanAssembly(simpleCSharpCompiler.PreCompiledDllPath, this);
}
else
{
Log(Level.Info, string.Format("\"{0}\" New or Modified. Compiling.", _name));
CodeCompileUnit compileUnit = GenerateCode();
compiledAssembly = simpleCSharpCompiler.CompileAssembly(compileUnit);
LogGeneratedCode(simpleCSharpCompiler, compileUnit);
TypeFactory.ScanAssembly(compiledAssembly, this);
}
}

compiledAssembly = simpleCSharpCompiler.CompileAssembly(compileUnit);
LogGeneratedCode(simpleCSharpCompiler, compileUnit);
private string GetUniqueIdentifier()
{
if (contentAsGuid == Guid.Empty)
contentAsGuid = GenerateHash(macrodefNode);
return _name + contentAsGuid;
}

TypeFactory.ScanAssembly(compiledAssembly, this);
}
//Create a 16 byte hash from the definition of the macrodef and return a guid constructed from that hash (guid so that we can use it in a filename)
private Guid GenerateHash(XmlNode macrodefNode)
{
byte[] original = Encoding.Default.GetBytes(macrodefNode.InnerXml);
HashAlgorithm algorithm = MD5.Create();
byte[] hashed = algorithm.ComputeHash(original);
return new Guid(hashed);
}

private void LogGeneratedCode(SimpleCSharpCompiler simpleCSharpCompiler, CodeCompileUnit compileUnit)
{
Expand All @@ -163,6 +202,8 @@ private void LogGeneratedCode(SimpleCSharpCompiler simpleCSharpCompiler, CodeCom
};

private Assembly compiledAssembly;
private XmlNode macrodefNode;
private Guid contentAsGuid = Guid.Empty;

public CodeCompileUnit GenerateCode()
{
Expand Down
30 changes: 24 additions & 6 deletions src/SimpleCSharpCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,25 @@ namespace Macrodef
// adapted from <script> task
internal class SimpleCSharpCompiler
{
public SimpleCSharpCompiler()
private readonly string uniqueIdentifier;

public SimpleCSharpCompiler(string UniqueIdentifier)
{
Provider = CreateCodeDomProvider("Microsoft.CSharp.CSharpCodeProvider", "System");
uniqueIdentifier = UniqueIdentifier;
Provider = CreateCodeDomProvider("Microsoft.CSharp.CSharpCodeProvider", "System");
//Compiler = provider.CreateCompiler();
//CodeGen = provider.CreateGenerator();
}

//public readonly ICodeCompiler Compiler;
//public readonly ICodeCompiler Compiler;
//public readonly ICodeGenerator CodeGen;
public readonly CodeDomProvider Provider;

public string PreCompiledDllPath
{
get { return OuptutDllPath(uniqueIdentifier); }
}

public string GetSourceCode(CodeCompileUnit compileUnit)
{
StringWriter sw = new StringWriter(CultureInfo.InvariantCulture);
Expand All @@ -31,13 +39,13 @@ public string GetSourceCode(CodeCompileUnit compileUnit)
return sw.ToString();
}

private static CompilerParameters CreateCompilerOptions()
private static CompilerParameters CreateCompilerOptions(string uniqueIdentifier)
{
CompilerParameters options = new CompilerParameters();
options.GenerateExecutable = false;
// <script> task uses true - and hence doesn't work properly (second script that contains task defs fails)!
options.GenerateInMemory = false;

options.OutputAssembly = OuptutDllPath(uniqueIdentifier);
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
{
try
Expand All @@ -56,9 +64,14 @@ private static CompilerParameters CreateCompilerOptions()
return options;
}

private static string OuptutDllPath(string uniqueIdentifier)
{
return Path.Combine(Path.GetTempPath(), uniqueIdentifier + ".dll");
}

public Assembly CompileAssembly(CodeCompileUnit compileUnit)
{
CompilerParameters options = CreateCompilerOptions();
CompilerParameters options = CreateCompilerOptions(uniqueIdentifier);

CompilerResults results = Provider.CompileAssemblyFromDom(options, compileUnit);

Expand Down Expand Up @@ -99,5 +112,10 @@ private static CodeDomProvider CreateCodeDomProvider(string typeName, string ass
}
return (CodeDomProvider) provider;
}

public bool PrecompiledDllExists()
{
return File.Exists(OuptutDllPath(uniqueIdentifier));
}
}
}
15 changes: 15 additions & 0 deletions test/duplicate-redefinition.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" ?>
<project name="duplicate-redefinition-test" default="duplicate-redefinition-test" xmlns="http://nant.sf.net/schemas/nant.xsd">
<loadtasks assembly="../build/Macrodef.dll" verbose="true" />

<target name="duplicate-redefinition-test">
<nant buildfile="duplicate.build" />
<macrodef name="duplicate-test">
<sequential>
<echo message="Do something different macro, for duplicate redefinition test." />
</sequential>
</macrodef>
</target>

</project>

11 changes: 11 additions & 0 deletions test/duplicate.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" ?>
<project name="duplicate-test" xmlns="http://nant.sf.net/schemas/nant.xsd">
<loadtasks assembly="../build/Macrodef.dll" verbose="true" />

<macrodef name="duplicate-test">
<sequential>
<echo message="Do nothing macro, for duplicate test." />
</sequential>
</macrodef>
</project>

21 changes: 19 additions & 2 deletions test/tests.build
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" ?>
<project name="exoftware.nant" default="test" xmlns="http://nant.sf.net/schemas/nant.xsd">
<loadtasks assembly="build/Macrodef.dll" verbose="true" />
<loadtasks assembly="../build/Macrodef.dll" verbose="true" />

<macrodef name="assert-equals">
<attributes>
Expand Down Expand Up @@ -100,13 +100,30 @@
</macro-with-elements>
<fail if="${ element_executed != 'true'}" message="element_executed: expected true but was ${element_executed}"/>
</target>


<target name="test-duplicate-macrodef">
<!--Should not fail if the same macro is included twice-->
<nant buildfile="duplicate.build" />
<nant buildfile="duplicate.build" />
</target>

<target name="test-duplicate-macrodef-redefinition">
<exec program="nant" commandline="/f:duplicate-redefinition.build" resultproperty="redefinition.failed" failonerror="false"/>
<if test="${redefinition.failed=='0'}">
<fail message="Should have failed because we are trying to redefine an existing macro."/>
</if>
</target>


<target name="test" depends="
test-macro-defined,
test-attributes,
test-properties-backedup,
test-property-subst,
test-elements
test-elements,
test-duplicate-macrodef,
test-duplicate-macrodef-redefinition
"/>

</project>
Expand Down

0 comments on commit 0bf40c4

Please sign in to comment.