Elton Stoneman

  Home  |   Contact  |   Syndication    |   Login
  85 Posts | 0 Stories | 199 Comments | 0 Trackbacks

News

Archives

Post Categories

[Source: http://geekswithblogs.net/EltonStoneman]

As Cory pointed out, my original task to execute T4 templates under MSBuild (replacing property markers in the T4 script with evaluated values from the build, see: An MSBuild task to execute T4 templates), fails under MSBuild 3.5. This is down to a change in the object model in the framework. All the useful classes are internal and all the useful properties are private, so they have to be reflected over - which makes my code brittle.

I've updated the task as part of a set here: Sixeyed MSBuild tasks, and it now works with either MSBuild 2.0 or 3.5.

Another update will be coming soon, to allow you to use named item group values, as well as property values and whole item groups in the script. A script can be marked with %(Collection.Identity) and when the task is called iteratively over a collection, the script will be populated with the current collection value each time. I'm also planning to change it to use the T4 engine directly rather than invoking the TextTransform console app, to hopefully speed up the execution time.

I'm using this task increasingly in builds now. It's a lot neater than having multiple XML or Regex file updates, and the template can be used to isolate logic so the builds stay clean. Currently it's used to dynamically generate Wix scripts, generate BizTalk bindings file for a set of environments, generate documentation projects for Sandcastle Help File Builder and produce config files for running DevPartner code coverage. Some of those templates may be generically useful, so I'll add them to MSDN when I get to the next update.

posted on Saturday, October 04, 2008 9:14 AM

Feedback

# re: ExecuteT4Template MSBuild task: Updated 10/15/2008 8:27 AM Woj
Great! Your sample is very useful and instructive, but I've found one weak point...

I've been playing with ExecuteT4Template and the sample project builds "successfuly" even in case of TextTransform.exe errors.

At the minimum you can make
ProcessHelper.Run return int:
-> return process.ExitCode
and then
ExecuteT4Template.Execute
-> if (0 == ProcessHelper.Run...
success = true;

Then build report 'FAILED' in case of transformation errors.
It would be useful to get those by intercepting OutStream messages and return them to build engine to show up ;-)

Best regards

# re: ExecuteT4Template MSBuild task: Updated 10/15/2008 1:02 PM Elton
Hi Woj, glad you've found it useful.

You're right though - when the template engine fails and you get a text file that just says "ErrorGeneratingOutput", the task does not return the failure to MSBuild.

Thanks for your suggestion - I'm looking at removing the use of TextTemplate.exe altogether though, and running the engine direct. That should be quicker and will let me log errors and warnings to the build, and return the correct status.

When I do the next update I'll let you know - I'm also planning to move it to the MSDN Code Gallery and post some proper sample templates.

Elton.

# re: ExecuteT4Template MSBuild task: Updated 3/25/2009 6:01 PM tricky
Firstly thx for the code just waht I was looking for.

I have found a weird error, I build and run the examples from Sixeyed MSBuild tasks link above, but when I try and incorporate the task in my own project I run into null object references trying to resolve the parent project.

I ran the code in debug and discovered the reason is that the contexts dictionary line ProjectFactory line 35 returns a single entry

[1, xxxxxx] not [0,xxxxx]

I have changed my code to obtain the one value in the dict and all seems well so far ...

You can probably shed some more light on this.


# re: ExecuteT4Template MSBuild task: Updated 4/1/2009 10:41 AM Giulio Vian
The path "C:\Program Files\Common Files\Microsoft Shared\TextTemplating\1.1" inside the task is valid only for x86. On an x64 the correct path is "C:\Program Files (x86)\Common Files\Microsoft Shared\TextTemplating\1.1".
Moreover "1.1" is for VS08 RTM; VS08 SP1 has "1.2".
HTH

# re: ExecuteT4Template MSBuild task: Updated 4/1/2009 10:57 AM Elton
Thanks Giulio - the task defaults the path as you say, but you can override it in your MSBuild script by specifying the ToolPath property.

# re: ExecuteT4Template MSBuild task: Updated 8/3/2009 11:23 AM Tommaso
I've found the same problem that tricky had. In my case instead of having 0 or 1 I had 38.
I've a target that is something like:
<Target ...>
<ExecuteT4Template...>
<...>
</Target>

if I change it to
<Target ...>
<ExecuteT4Template...>
<...>
</Target>

the key number becomes 39.

The other issue I found is that assemblies referred in the template cannot be found (such as System.Core.dll). I added few lines to add the -P parameters to the command line:

string incldirs="";
foreach (string refpath in helper.GetItemGroupValue("ReferencePath").Split(new char[] { ';' }))
{
incldirs += "-P \"" + Path.GetDirectoryName(refpath)+ "\" ";
}

# re: ExecuteT4Template MSBuild task: Updated 8/18/2009 5:08 AM Tommaso
Regarding the ProjectFactory wrong index problem. I realized that in some case there can be more than one value in the accessed data structure (contexts). In my case a Solution and a Project.
As workaround in Project Factory I check for the project that has among the usingTasks a task with the same name of the type of the executingTask passed as argument. But I am quite sure that there could be some case where two project could be listed and possibly both having conflicting UsingTasks (both uses ExecuteT4Template, for instance).
Here is the code for my workaround:

project = null;
string currentTaskType = executingTask.GetType().Name;
foreach (var cnt in contexts.Values) {
object parentProject = ReflectionHelper.GetPropertyValue(cnt, ReflectedMember.ParentProject);
ms.UsingTaskCollection utc = ReflectionHelper.GetPropertyValue(parentProject, ReflectedMember.UsingTasks) as ms.UsingTaskCollection;
foreach (ms.UsingTask ut in utc) {
if (ut.TaskName == currentTaskType) {
project = new ReflectedProject(parentProject);
break;
}
}
if (project != null)
break;
}

# re: ExecuteT4Template MSBuild task: Updated 1/10/2010 7:27 PM Nick Zdunic
For the following where there is more than one template item the code is also failing.

<Target Name="ExecuteT4Template">
<ExecuteT4Template ... />
<ExecuteT4Template />
</Target>

When it gets to the second template the line in ProjectFactory which gets the contexts (Hastable contexts = ReflectionHelper.GetFieldValue(engineCallback, ReflectedMember.ExecutionContexts) as Hashtable;) has a problem. It returns one context as item zero in the Hashtable, but it's value is null)

Any ideas on why this would be occuring?


Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: