How do I call static class methods from msbuild? - msbuild

How does one call a class static method from msbuild and store its results in a list?
EDIT: Okay, let me explain a bit further. I am using sandcastle help file builder to generate documentation for my application. One of the requirements is that you must specify the documentation sources as follows:
<DocumentationSources>
<DocumentationSource sourceFile="$(MSBuildProjectDirectory)\..\src\myApp\bin\Debug\myApp.exe" xmlns="" />
<DocumentationSource sourceFile="$(MSBuildProjectDirectory)\..\src\myApp\bin\Debug\myApp.xml" xmlns="" />
</DocumentationSources>
Sandcastle Help File Builder comes with a utils assembly that has a way of retrieving all dll and xml files from a specified directory. I want to call the method from this assembly and store its result as a list of <DocumentationSource>. This is a static method which returns Collection<string>

Custom Tasks are great but potential overkill if you want to do something simple. I believe Draco is asking about the Property Functions feature in MSBuild 4.
An example of setting a property by using a static function (ripped directly from above page):
<Today>$([System.DateTime]::Now)</Today>
And to call a static function on parameters:
$([Class]:: Property.Method(Parameters))
Or, perhaps you want something crazier like inline tasks.

Create a custom task calling that static method and returning an array of ITaskItem.
Or
You could try using the MSBuild Extension Pack Assembly.Invoke :
<PropertyGroup>
<StaticMethodAssemblyPath>path</StaticMethodAssemblyPath>
</PropertyGroup>
<MSBuild.ExtensionPack.Framework.Assembly TaskAction="Invoke"
NetArguments="#(ArgsM)"
NetClass="StaticMethodClassName"
NetMethod="StaticMethodName"
NetAssembly="${StaticMethodAssemblyPath}">
<Output TaskParameter="Result" PropertyName="R"/>
</MSBuild.ExtensionPack.Framework.Assembly>

Generally, the most flexible option is to create a custom MSBuild task. This is all untested code meant to just to give you the idea:
In your msbuild file:
<UsingTask TaskName="FindFiles" AssemblyFile="FindFiles.dll" />
<!--
As you'll see below, SearchDirectory and SearchPatterns are input parameters,
MatchingFiles is an output parameter, SourceFiles is an ItemGroup assigned to
the output.
-->
<FindFiles SearchDirectory="$(MyDirectory)" SearchPatterns="*.dll;*.xml">
<Output ItemName="SourceFiles" TaskParameter="MatchingFiles" />
</FindFiles>
<!-- You can then use the generated ItemGroup output elsewhere. -->
<DocumentationSources>
<DocumentationSource sourceFile="#(SourceFiles)" xmlns="" />
</DocumentationSources>
FindFiles.cs:
using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace FindFiles
{
public class FindFiles : Task
{
// input parameter
[Required]
public string SearchDirectory { get; set; }
// output parameter
[Required]
public string[] SearchPatterns { get; set; }
[Output]
public string[] MatchingFiles { get; private set; }
private bool ValidateParameters()
{
if (String.IsNullOrEmpty(SearchDirectory))
{
return false;
}
if (!Directory.Exists(SearchDirectory))
{
return false;
}
if (SearchPatterns == null || SearchPatterns.Length == 0)
{
return false;
}
return true;
}
// MSBuild tasks use the command pattern, this is where the magic happens,
// refactor as needed
public override bool Execute()
{
if (!ValidateParameters())
{
return false;
}
List<string> matchingFiles = new List<string>();
try
{
foreach (string searchPattern in SearchPatterns)
{
matchingFiles.AddRange(
Directory.GetFiles(SearchDirectory, searchPattern)
);
}
}
catch (IOException)
{
// it might be smarter to just let this exception fly, depending on
// how you want the task to behave
return false;
}
MatchingFiles = matchingFiles.ToArray();
return true;
}
}
}

Related

ASP.NET MVC Custom configuration GetSection returns null

I'm working on an inherited ASP.NET MVC 4 project using .net framework 4.5.
We've added a new configuration section files and relevant class files and from what we can tell (docs.Microsoft and other online guides) it's set up correctly.
The Problem
ConfigurationManager.GetSection() returns null.
According to the docs this returns null if the section doesn't exist. Troubleshooting this has been troublesome.
The Code
The website is an ASP.NET Web Application. Properties window sets assembly name to Client.Project.UI.Base (which is the DLL in the published bin). This is the assembly name used for the config types FQN and assembly in web.config.
NB: the config section SupportCaseConfiguration was originally in a separate file and the SupportTickets section just specified the configSource. This has been moved into the web.config to reduce the number of potential issues while troubleshooting.
web.config:
<configSections>
<!-- define type for new section -->
<section name="SupportTickets" type="Client.Project.UI.Base.Infrastructure.Services.SupportCaseConfigurationSection, Client.Project.UI.Base"/>
</configSections>
<!-- new config section -->
<SupportTickets>
<SupportCaseConfiguration>
<caseTypes>
<add name="tenant.TestCase" label="Test Case" recipient="email_here" ccList="" bccList="" />
</caseTypes>
</SupportCaseConfiguration>
</SupportTickets>
SupportCaseConfiguration.cs:
namespace Client.Project.UI.Base.Infrastructure.Services
{
using System.Configuration;
//Extend the ConfigurationSection class.
public class SupportCaseConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("caseTypes", IsDefaultCollection = true)]
public CaseTypeElementCollection CaseTypes
{
get { return (CaseTypeElementCollection)this["caseTypes"]; }
}
}
//Extend the ConfigurationElementCollection class.
[ConfigurationCollection(typeof(CaseTypeElement))]
public class CaseTypeElementCollection : ConfigurationElementCollection
{
public CaseTypeElement this[int index]
{
get { return (CaseTypeElement)BaseGet(index); }
set
{
if (BaseGet(index) != null)
BaseRemoveAt(index);
BaseAdd(index, value);
}
}
protected override ConfigurationElement CreateNewElement()
{
return new CaseTypeElement();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((CaseTypeElement)element).Name;
}
}
//Extend the ConfigurationElement class. This class represents a single element in the collection.
public class CaseTypeElement : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name
{
get { return (string)this["name"]; }
set { this["name"] = value; }
}
[ConfigurationProperty("label", IsRequired = true)]
public string Label
{
get { return (string)this["label"]; }
set { this["label"] = value; }
}
[ConfigurationProperty("recipient", IsRequired = true)]
public string Recipient
{
get { return (string)this["recipient"]; }
set { this["recipient"] = value; }
}
[ConfigurationProperty("ccList", IsRequired = true)]
public string CcList
{
get { return (string)this["ccList"]; }
set { this["ccList"] = value; }
}
[ConfigurationProperty("bccList", IsRequired = true)]
public string BccList
{
get { return (string)this["bccList"]; }
set { this["bccList"] = value; }
}
}
}
Elsewhere, getting new config data:
SupportCaseConfigurationSection supportTicketsConfigurationSection = ConfigurationManager.GetSection("SupportCaseConfiguration") as SupportCaseConfigurationSection;
The site is being published locally, I can attach a debugger to ensure the latest versions of files are being used. I can see the config section in the published web.config.
I've been looking at this I can no longer see if anything is amiss. It all looks fine for me...
Any ideas, troubleshooting tips or even pointing out I'm being a muppet would be useful.
Cheers.
I can only assume the issue was something to do with the config classes in the site assembly.
Even after trying online examples, copypasting into the project, not even they worked.
As soon as I put the configuration section/element classes in a separate project (moved out of the website project) it started working.

Usage of IAdapterFactory with popupMenus in eclipse rcp

My requirement was to add a new menu entry to “Compare with” present in one of the 3rd party views.
As this was using “org.eclipse.ui.popupMenus” to add a menu entry to the above contribution. Even I was forced to use the same extension point even though its deprecated.
I was able to add a menu entry to the contribution with the below code
<extension point="org.eclipse.ui.popupMenus">
<objectContribution
adaptable="true"
id="test.id"
objectClass="local.change">
<action
class="compare.commparetool"
enablesFor="1"
id="id"
label="Compare "
menubarPath="compareWith/group1">
</action>
<visibility>
<objectState
name="local.change"
value=".txt">
</objectState>
</visibility>
</extension>
The above configuration is working fine.
Next requirement was to add property tester to hide the menu entries whenever a file selected is other than .txt file.
As we cannot add property tester to object contribution, I have used IAdapterfactory. Below code is not working.
Observation:
I have added many menu entries (“org.eclipse.ui.menus”) in different views in “Compare with” which is not related to this.
But if user clicks on any of these commands, and then try the view in question, it is working as expected as expected.
Below is the code. Am I missing anything. Do I need to register the adapters in some other place also??
<extension point="org.eclipse.core.runtime.adapters">
<factory
adaptableType="local.change"
class="LocalChangeAdapterFactory">
<adapter
type="org.eclipse.ui.IActionFilter">
</adapter>
</factory>
</extension>
public class LocalChangeAdapterFactory implements IAdapterFactory
{
#SuppressWarnings("unchecked")
#Override
public Object getAdapter(final Object adaptableObject, final Class adapterType)
{
if (adapterType == IActionFilter.class)
{
return LocalChangeActionFilter.getInstance();
}
return null;
}
#SuppressWarnings("unchecked")
#Override
public Class[] getAdapterList()
{
return new Class[] { LocalChangeActionFilter.class };
}
}
public class LocalChangeActionFilter implements IActionFilter
{
private static LocalChangeActionFilter INSTANCE = new LocalChangeActionFilter();
private LocalChangeActionFilter()
{
}
#Override
public boolean testAttribute(final Object target, final String name, final String value)
{
String fileName = "";
if(target.getId==1){
return true;
}else{
return false;
}
public static LocalChangeActionFilter getInstance()
{
return INSTANCE;
}
}
The adaptableType attribute of the adapter factory should specify the type of the existing object that you want to adapt to an IActionFilter. So this is probably a resource of file:
adaptableType="org.eclipse.core.resources.IResource">
The getAdapter method of the IActionFactory should return a class matching the adapter attribute, not your implementing class:
public Class[] getAdapterList()
{
return new Class[] { IActionFilter.class };
}
Your testAttribute method if the action filter must test the name parameter matches the value in the objectState:
#Override
public boolean testAttribute(final Object target, final String name, final String value)
{
if (name.equals("local.change"))
{
.... do test
return true;
}
return false;
}

Castle Windsor Fluent API: Define Array with Single item as Dependency

Given this XML configuration (which works)
<component type="X.Y.Z.ActivityService, X.Y.Z.Services" id="X.Y.Z.ActivityService" lifestyle="transient">
<parameters>
<Listeners>
<array>
<item>${DefaultActivityListener}</item>
</array>
</Listeners>
</parameters>
</component>
<component type="X.Y.Z.DefaultActivityListener, X.Y.Z.Services" id="DefaultActivityListener" lifestyle="transient" />
I have converted to use the fluent API as below (which doesn't work):
Container.Register(
Component.For<X.Y.Z.ActivityService>()
.ServiceOverrides(
ServiceOverride.ForKey("Listeners").Eq(typeof(X.Y.Z.DefaultActivityListener).Name))
.LifeStyle.Transient
);
Container.Register(
Component.For<X.Y.Z.DefaultActivityListener>()
.Named("DefaultActivityListener")
.LifeStyle.Transient
);
When I now attempt to resolve an instance of X.Y.Z.ActivityService Windsor throws a NotImplementedException in Castle.MicroKernel.SubSystems.Conversion.ArrayConverter.PerformConversion(String, Type).
The implementation of the PerformConversion method is:
public override object PerformConversion(String value, Type targetType)
{
throw new NotImplementedException();
}
I should add that if I remove the ServiceOverrides call, all behaves as expected. So there is specifically something wrong in the way I am wiring up the Listeners parameter. Listeners by the way is a property as opposed to a constructor parameter.
Seeing as the XML config works as expected how do I best use the fluent API (short of implementing the PerformConversion method) in order to achieve the same result?
I am using Release 2.0.
EDIT
I will extend the question to how would you achieve this configuration in code, with or without use of the fluent API.
UPDATE
It appears the problem occurs if you attempt to assign a single element to an array property. Unit tests provided below to illustrate issue.
namespace Components
{
public class A
{
public I[] I { get; set; }
}
public interface I
{
string Name { get; }
}
public class B : I
{
public string Name { get { return "B"; } }
}
public class C : I
{
public string Name { get { return "C"; } }
}
}
[TestMethod]
public void ArrayPropertyTestApi()
{
//PASSES
using (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer())
{
container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName, typeof(Components.C).FullName)));
container.Register(Component.For<Components.B>());
container.Register(Component.For<Components.C>());
Components.A svc = container.Resolve<Components.A>();
Assert.IsTrue(svc.I.Length == 2);
Assert.IsTrue(svc.I[0].Name == "B");
Assert.IsTrue(svc.I[1].Name == "C");
}
}
[TestMethod]
public void ArrayPropertyTestApi2()
{
//FAILS
using (Castle.Windsor.WindsorContainer container = new Castle.Windsor.WindsorContainer())
{
container.Register(Component.For<Components.A>().ServiceOverrides(ServiceOverride.ForKey("I").Eq(typeof(Components.B).FullName)));
container.Register(Component.For<Components.B>());
container.Register(Component.For<Components.C>());
Components.A svc = container.Resolve<Components.A>(); //Throws NotImplementedException
Assert.IsTrue(svc.I.Length == 1);
Assert.IsTrue(svc.I[0].Name == "B");
}
}
Question still stands.
Thanks.
[TestFixture]
public class WindsorTests {
[Test]
public void ArrayConfig() {
var container = new WindsorContainer();
container.Register(Component.For<Listener>().Named("listener"));
container.Register(Component.For<ActivityService>()
.ServiceOverrides(ServiceOverride.ForKey("listeners").Eq(new[] {"listener"})));
var service = container.Resolve<ActivityService>();
Assert.AreEqual(1, service.Listeners.Length);
}
}
public class Listener {}
public class ActivityService {
public Listener[] Listeners { get; set; }
}
The key part here is the new[] {"listener"}. The MicroKernel needs to know that the parameter listeners is an array, if you pass just "listener" it assumes that the parameter is scalar and throws because it can't convert a scalar to an array.

Normalizing a list of items in MSBuild

I am trying to get a list of all unit test assemblies under the root of my project. I can do this as follows:
<CreateItem Include="**\bin\**\*.UnitTest.*.dll">
<Output TaskParameter="Include" ItemName="Items"/>
</CreateItem>
However, this will find the same DLLs multiple times since they exist in multiple sub-directories. Is there an easy way for me to normalize based on item metadata (ie. the file name and extension) so that I get a list of unique unit test DLLs? Or do I have to resort to writing my own task?
Even though this is old, I could never get Thomas solution to work myself, but I did find sort of a workaround using only built-in commands with v4.0 of msbuild:
<ItemGroup>
<TestAssemblies Include="$(SolutionRoot)\**\bin\*.Tests.dll" />
<TestItems Include="%(TestAssemblies.FileName)%(TestAssemblies.Extension)">
<ItemPath>%(TestAssemblies.Identity)</ItemPath>
</TestItems>
<DistinctTestItems Include="#(TestItems->Distinct())"></DistinctTestItems>
</ItemGroup>
<Message Text="%(DistinctTestItems.ItemPath)" Importance="high" />
Documentation: Item Functions
The MSBuild Extension Pack contains the task MSBuildHelper, supporting the command RemoveDuplicateFiles.
<CreateItem Include="**\bin\**\*.UnitTest.*.dll">
<Output TaskParameter="Include" ItemName="Items"/>
</CreateItem>
<MSBuild.ExtensionPack.Framework.MsBuildHelper TaskAction="RemoveDuplicateFiles" InputItems1="#(Items)">
<Output TaskParameter="OutputItems" ItemName="Items"/>
</MSBuild.ExtensionPack.Framework.MsBuildHelper>
I had a good search online and couldn't find any way of doing this. If anyone knows a clean built-in way then please let me know. In the meantime, I wrote a simple task to do the job. The usage looks like this:
<NormalizeByMetadata Items="#(ItemsToNormalize)" MetadataName="Filename">
<Output TaskParameter="NormalizedItems" ItemName="MyNormalizedItems"/>
</NormalizeByMetadata>
After the above task has executed, MyNormalizedItems will contain only those items from ItemsToNormalize that have a unique value for the Filename metadata. If two or more items have the same value for their Filename metadata, the first match will be included in the output.
The code for the MSBuild task is:
public class NormalizeByMetadata : Task
{
[Required]
public ITaskItem[] Items
{
get;
set;
}
[Required]
public string MetadataName
{
get;
set;
}
[Output]
public ITaskItem[] NormalizedItems
{
get;
private set;
}
public override bool Execute()
{
NormalizedItems = Items.Distinct(new ItemEqualityComparer(MetadataName)).ToArray();
return true;
}
private sealed class ItemEqualityComparer : IEqualityComparer<ITaskItem>
{
private readonly string _metadataName;
public ItemEqualityComparer(string metadataName)
{
Debug.Assert(metadataName != null);
_metadataName = metadataName;
}
public bool Equals(ITaskItem x, ITaskItem y)
{
if (x == null || y == null)
{
return x == y;
}
var xMetadata = x.GetMetadata(_metadataName);
var yMetadata = y.GetMetadata(_metadataName);
return string.Equals(xMetadata, yMetadata);
}
public int GetHashCode(ITaskItem obj)
{
if (obj == null)
{
return 0;
}
var objMetadata = obj.GetMetadata(_metadataName);
return objMetadata.GetHashCode();
}
}
}

MSBuild: How can I check if a process exists?

Is it possible to write a Condition in msbuild that checks if a certain process exists?
Or, alternatively, does anyone know of such a task?
Today, my process creates a pid file, which existence I check. But I do not like all the extra maintenance involved with such a file.
Any ideas?
There is no such task in MSBuild Extension Pack or in MSBuild Community Tasks. But you could easily create a such one. Something like this:
using System.Diagnostics;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace StackOverflow.MSBuild
{
public class IsProcessRunning : Task
{
private string processName;
private bool isRunning;
[Required]
public string ProcessName
{
get { return processName; }
set { processName = value; }
}
[Output]
public bool IsRunning
{
get { return isRunning; }
}
public override bool Execute()
{
if(string.IsNullOrEmpty(processName))
{
Log.LogError("ProcessName could not be empty");
return false;
}
foreach(Process clsProcess in Process.GetProcesses())
{
if(clsProcess.ProcessName.Contains(processName))
{
isRunning = true;
}
}
return true;
}
}
}
And you use it like that:
<UsingTask AssemblyFile="$(Task_Assembly_path)"
TaskName="StackOverflow.MSBuild.IsProcessRunning" />
<Target Name="TestTask">
<IsProcessRunning ProcessName="${Process}">
<Output ItemName="Result" TaskParameter="IsRunning"/>
</IsProcessRunning>
<Message Text="Process ${Process} is running"
Condition="'${Result}' == 'true'"/>
</Target>