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>
Related
I have a parallel build of multiple projects, each one of those at some point in time does invoke <Exec /> task. This exec task is running 3pty tool that crashes if there is another instance of this tool running. Is there some native way how to implement "mutex" in msbuild ?
The obvious solution (that works) is to make the build synchronous - but that is slowing down the whole build.
In the end I ended up writing my own msbuild extension library like below
msbuild part:
<UsingTask TaskName="MutexExec" AssemblyFile="$(CommonTasksAssembly)" />
C# part:
// <PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.9.0" />
// <PackageReference Include="Microsoft.Build.Tasks.Core" Version="16.9.0" />
// <PackageReference Include="Microsoft.Build.Framework" Version="16.9.0" />
using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Build.Framework;
using Microsoft.Build.Tasks;
/// <summary>
/// Like a Exec task, but with critical section
/// </summary>
public class MutexExec : Exec
{
/// <summary>
/// Gets or sets mutex name
/// </summary>
[Required]
public string MutexName { get; set; }
/// <inheritdoc />
public override bool Execute()
{
var timeout = TimeSpan.FromMinutes(5);
var stopwatch = Stopwatch.StartNew();
while (stopwatch.Elapsed < timeout)
{
bool createdNew;
using (var mutex = new Mutex(true, this.MutexName, out createdNew))
{
if (createdNew)
{
bool result = base.Execute();
try
{
this.Log.LogMessage(MessageImportance.Normal, "Releasing {0}", this.MutexName);
mutex.ReleaseMutex();
}
catch (Exception e)
{
this.Log.LogError("Failure releasing {0} - {1}", this.MutexName, e);
}
return result;
}
}
this.Log.LogMessage(MessageImportance.Normal, "Waiting for {0} - ellapsed {1}", this.MutexName, stopwatch.Elapsed);
Thread.Sleep(5000);
continue;
}
this.Log.LogError("Failed to acquire {0} in {1}.", this.MutexName, timeout);
return false;
}
}
I read and implemented Trying to using Nhibernate with Mono & SQLite - can't find System.Data.SQLite
However, as the last comment there states this seems not to work with NHibernate 3.1
The error is
HibernateException: The IDbCommand and IDbConnection implementation in
the assembly Mono.Data.Sqlite could not be found. Ensure that the
assembly Mono.Data.Sqlite is [...reachable...]
I have Mono.Data.Sqlite in the GAC.
I have tried both specifying "Mono.Data.Sqlite" as well as typeof(Mono.Data.Sqlite.SqliteConnection).Assembly.FullName as the name of the assembly
Has anyone any Ideas how to get this working?
There is a problem in the answer of Trying to using Nhibernate with Mono & SQLite - can't find System.Data.SQLite .
For the given constructor (3 parameters) to work the assembly in question (Mono.Data.Sqlite) needs to be loaded first.
This works if the 4-parameter base contructor is used like this:
public class MonoSQLiteDriver : NHibernate.Driver.ReflectionBasedDriver
{
public MonoSQLiteDriver()
: base(
"Mono.Data.Sqlite",
"Mono.Data.Sqlite",
"Mono.Data.Sqlite.SqliteConnection",
"Mono.Data.Sqlite.SqliteCommand")
{
}
public override bool UseNamedPrefixInParameter {
get {
return true;
}
}
public override bool UseNamedPrefixInSql {
get {
return true;
}
}
public override string NamedPrefix {
get {
return "#";
}
}
public override bool SupportsMultipleOpenReaders {
get {
return false;
}
}
}
(Still, credit goes to http://intellect.dk/post/Why-I-love-frameworks-with-lots-of-extension-points.aspx for the original idea - thanks.)
And if you use FluentNHibernate, then you'll also need:
public class MonoSQLiteConfiguration : PersistenceConfiguration<MonoSQLiteConfiguration>
{
public static MonoSQLiteConfiguration Standard
{
get { return new MonoSQLiteConfiguration(); }
}
public MonoSQLiteConfiguration()
{
Driver<MonoSQLiteDriver>();
Dialect<SQLiteDialect>();
Raw("query.substitutions", "true=1;false=0");
}
public MonoSQLiteConfiguration InMemory()
{
Raw("connection.release_mode", "on_close");
return ConnectionString(c => c
.Is("Data Source=:memory:;Version=3;New=True;"));
}
public MonoSQLiteConfiguration UsingFile(string fileName)
{
return ConnectionString(c => c
.Is(string.Format("Data Source={0};Version=3;New=True;", fileName)));
}
public MonoSQLiteConfiguration UsingFileWithPassword(string fileName, string password)
{
return ConnectionString(c => c
.Is(string.Format("Data Source={0};Version=3;New=True;Password={1};", fileName, password)));
}
}
I have not encountered any problems so far...
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;
}
}
}
I am trying to access settings in my config file, which is a series of xml elements listed as such:
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
Now I want to access it. I have set up classes like so:
Public Class DatabaseConfigurationHandler
Inherits ConfigurationSection
<ConfigurationProperty("Databases", IsDefaultCollection:=True)> _
Public ReadOnly Property Databases() As DatabaseCollection
Get
Return CType(Me("Databases"), DatabaseCollection)
End Get
End Property
End Class
Public Class DatabaseCollection
Inherits ConfigurationElementCollection
Protected Overloads Overrides Function CreateNewElement() As ConfigurationElement
Return (New Database())
End Function
Protected Overloads Overrides Function GetElementKey(ByVal element As ConfigurationElement) As Object
Return (CType(element, Database).DatabaseName)
End Function
End Class
Public Class Database
Inherits ConfigurationElement
<ConfigurationProperty("name", IsKey:=True, IsRequired:=True)> _
Public Property DatabaseName() As String
Get
Return Me("name").ToString()
End Get
Set(ByVal Value As String)
Me("name") = Value
End Set
End Property
<ConfigurationProperty("value", IsRequired:=True)> _
Public Property DatabaseValue() As String
Get
Return Me("value").ToString()
End Get
Set(ByVal Value As String)
Me("value") = Value
End Set
End Property
End Class
I want to be able get the element by it's name and return the value but I can't see to do that:
Dim config As New DatabaseConfigurationHandler
config = System.Configuration.ConfigurationManager.GetSection("databases/database")
Return config.Databases("DatabaseOne")
Am I missing some code, what am I doing wrong? Any other errors in the above?
Thanks.
Here's a cut and paste from something very similar I did a few days ago.
Config:
<ListConfigurations>
<lists>
<add Name="blah" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
<add Name="blah2" EndpointConfigurationName="blah" ListName="blah" ConnectionString="blah" TableName="blah" FieldsCsv="blah" DbFieldsCsv="blah"/>
</lists>
</ListConfigurations>
Config section C#:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace App
{
/// <summary>
/// Individual list configuration
/// </summary>
class ListConfiguration : ConfigurationElement
{
[ConfigurationProperty("Name", IsKey = true, IsRequired = true)]
public string Name
{
get { return (string)this["Name"]; }
}
[ConfigurationProperty("EndpointConfigurationName", IsRequired = true)]
public string EndpointConfigurationName
{
get { return (string)this["EndpointConfigurationName"]; }
}
[ConfigurationProperty("ListName", IsRequired = true)]
public string ListName
{
get { return (string)this["ListName"]; }
}
[ConfigurationProperty("ConnectionString", IsRequired = true)]
public string ConnectionString
{
get { return (string)this["ConnectionString"]; }
}
[ConfigurationProperty("TableName", IsRequired = true)]
public string TableName
{
get { return (string)this["TableName"]; }
}
[ConfigurationProperty("FieldsCsv", IsRequired = true)]
public string FieldsCsv
{
get { return (string)this["FieldsCsv"]; }
}
[ConfigurationProperty("DbFieldsCsv", IsRequired = true)]
public string DbFieldsCsv
{
get { return (string)this["DbFieldsCsv"]; }
}
}
/// <summary>
/// Collection of list configs
/// </summary>
class ListConfigurationCollection : ConfigurationElementCollection
{
protected override ConfigurationElement CreateNewElement()
{
return new ListConfiguration();
}
protected override object GetElementKey(ConfigurationElement element)
{
return ((ListConfiguration)element).Name;
}
}
/// <summary>
/// Config section
/// </summary>
class ListConfigurationSection : ConfigurationSection
{
[ConfigurationProperty("lists")]
public ListConfigurationCollection Lists
{
get { return (ListConfigurationCollection)this["lists"]; }
}
}
}
And the code to pick it up from the main app:
ListConfigurationSection configSection = null;
try
{
configSection = ConfigurationManager.GetSection("ListConfigurations") as ListConfigurationSection;
}
catch (System.Configuration.ConfigurationErrorsException)
{
}
There isn't any good reason to design this kind of stuff by hand anymore. Rather, you should be using the Configuration Section Designer on CodePlex:
http://csd.codeplex.com/
Once installed, you can just add a new item to your project (a configuration section designer) and then add the elements and the constraints. I've found it VERY easy to use, and I will probably never write a piece of code for configuration files again.
You can use this configuration handler instead.. It will work for ALL custom configuration sections
public class XmlConfigurator : IConfigurationSectionHandler
{
public object Create(object parent,
object configContext, XmlNode section)
{
if (section == null)
throw new ArgumentNullException("section",
"Invalid or missing configuration section " +
"provided to XmlConfigurator");
XPathNavigator xNav = section.CreateNavigator();
if (xNav == null)
throw new ApplicationException(
"Unable to create XPath Navigator");
Type sectionType = Type.GetType((string)
(xNav).Evaluate("string(#configType)"));
XmlSerializer xs = new XmlSerializer(sectionType);
return xs.Deserialize(new XmlNodeReader(section));
}
}
Your config file then has to include a reference to the type that the represents the root element
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
A sample config file might look like this:
<?xml version="1.0" encoding="utf-8" ?>
<ConnectionConfig
configType="MyNamespace.ConnectionConfig, MyNamespace.AssmblyName" >
<ConnCompanys>
<ConnCompany companyName="CompanyNameHere">
<ConnApps>
<ConnApp applicationName="Athena" vendorName="Oracle" >
<ConnSpecs>
<ConnSpec environments="DEV"
serverName="Athena"
port="1521"
catalog="DatabaseName"
logon="MyUserName"
password="%%552355%8234^kNfllceHGp55X5g==" />
<!-- etc...
And you will need to define the classes that each xml element maps to... using the appropriate XmlSerialization attributes ...
[XmlRoot("ConnectionConfig")]
public class ConnectionConfig
{
private ConnCompanys comps;
[XmlArrayItem(ElementName = "ConnCompany")]
public ConnCompanys ConnCompanys
{
get { return comps; }
set { comps = value; }
}
public ConnApp this[string CompanyName, string AppName]
{ get { return ConnCompanys[CompanyName][AppName]; } }
public ConnSpec this[string CompanyName, string AppName, APPENV env]
{
get
{
return ConnCompanys[CompanyName][AppName, env];
}
}
}
public class ConnCompanys : List<ConnCompany>
{
public ConnCompany this[string companyName]
{
get
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return comp;
return null;
}
}
public bool Contains(string companyName)
{
foreach (ConnCompany comp in this)
if (comp.CompanyName == companyName)
return true;
return false;
}
}
public class ConnCompany
{
#region private state fields
private string compNm;
private ConnApps apps;
#endregion private state fields
#region public properties
[XmlAttribute(DataType = "string", AttributeName = "companyName")]
public string CompanyName
{
get { return compNm; }
set { compNm = value; }
}
[XmlArrayItem(ElementName = "ConnApp")]
public ConnApps ConnApps
{
get { return apps; }
set { apps = value; }
}
#endregion public properties
#region indexers
public ConnApp this[string applicationName]
{ get { return ConnApps[applicationName]; } }
public ConnSpec this[string applicationName, APPENV environment]
{
get
{
foreach (ConnSpec con in this[applicationName].ConnSpecs)
if (con.Environment == environment)
return con;
return null;
}
}
#endregion indexers
}
etc...
My VB isn't up to much sorry but here's how you do it in C#.
C#
public class AppState : IConfigurationSectionHandler
{
static AppState()
{
xmlNode myConfigNode = (XmlNode)ConfigurationManager.GetSection("databases");
}
public object Create(object parent, object context, XmlNode configSection) {
return configSection;
}
}
App.Config
<configuration>
<configSections>
<section name="databases" type="MyAssembly.AppState, MyAssembly" />
</configSections>
<databases>
<database name="DatabaseOne" Value="[value]" />
<database name="DatabaseTwo" Value="[value]" />
</databases>
</configuration>
You might be interested in using a ConfigurationElementCollection where T is the type of the child ConfigurationElements. You can find sample code in C# at http://devpinoy.org/blogs/jakelite/archive/2009/01/10/iconfigurationsectionhandler-is-dead-long-live-iconfigurationsectionhandler.aspx
Cheers!
There's a nice simple way of doing this demonstrated here also:
codeproject.com/KB/XML/xml_config_section.aspx
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();
}
}
}