Windows service - MSBUILD - msbuild

I have a MSBUILD script used with my application factory in order to stop the Windows Service if it runs, then wait for a while and copy/paste the bin files.
Here is a part of my script :
<Message Text="Stopping Service" Importance="high"
Condition="$(%(Server.Identity)ServiceExists) == true" />
<ServiceController MachineName="%(Server.Identity)" ServiceName="$(ServiceName)" Action="Stop"
Condition="$(%(Server.Identity)ServiceExists) == true" />
<Sleep Seconds="60" Condition="$(%(Server.Identity)ServiceExists) == true" />
But even if my windows service is initially stopped, we are still doing this operation. What is wront in this condition : Condition="$(%(Server.Identity)ServiceExists) == true" ?

Related

MS Build Task not accepting properties

I am trying to build my project using MSbuild. The solution builds perfectly fine if I hardcode the configurations
<MSBuild Projects="%(SolutionsToBuild.Identity)" Properties="Configuration=Debug" ContinueOnError="false"/>
<MSBuild Projects="%(SolutionsToBuild.Identity)" Properties="Configuration=Release" ContinueOnError="false"/>
but if I use batching like this the values are not assigned
<MSBuild Projects="%(SolutionsToBuild.Identity)" Properties="Configuration=%(ConfigList.Identity)" ContinueOnError="false"/>
where ConfigList
<ItemGroup>
<ConfigList Condition=" '#(ConfigList)' == '' and $(Configuration) != '' " Include="$(Configuration.Split(';'))" />
<!-- parse all requested configurations into a list -->
<ConfigList Condition=" '#(ConfigList)' == '' " Include="Debug" />
<!-- if no configurations were specified, default to Debug -->
</ItemGroup>
and Configurations are provided from CLI
msbuild build.proj -p:Configuration="Release;Debug"
Also interestingly I am getting the value in
<Message Text="Build for configuration %(ConfigList.Identity)"/>
Whole Code
<Target Name="BuildApplication">
<ItemGroup>
<ConfigList Condition=" '#(ConfigList)' == '' and $(Configuration) != '' " Include="$(Configuration.Split(';'))" />
<!-- parse all requested configurations into a list -->
<ConfigList Condition=" '#(ConfigList)' == '' " Include="Debug" />
<!-- if no configurations were specified, default to Debug -->
</ItemGroup>
<Message Text="Build for configuration %(ConfigList.Identity)"/>
<MSBuild Projects="%(SolutionsToBuild.Identity)" Properties="Configuration=%(ConfigList.Identity)" ContinueOnError="false"/>
<!--<MSBuild Projects="%(SolutionsToBuild.Identity)" Properties="Configuration=Debug" ContinueOnError="false"/>-->
</Target>
I am not sure what I am doing wrong. Any help would be appreciated. Thanks !
Sources referred:
Using MSBuild to Build Multiple Configurations
Passing properties to MSBUILD Task

Processing batch items in parallel

I have an ItemGroup, and want to process all its items in parallel (using a custom task or an .exe).
I could write my task/exe to accept the entire ItemGroup and process its items in parallel internally. However, I want this parallelism to work in conjunction with MSBuild's /maxCpuCount param, since otherwise I might end up over-parallelizing.
This thread says there's no way.
My testing shows that MSBuild's /maxCpuCount only works for building different projects, not items (see code below)
How can I process items from an ItemGroup in parallel?
Is there a way to author a custom task to work in parallel in conjunction with MSBuild's Parallel support?
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" >
<!-- Runs only once - I guess MSBuild detects it's the same project -->
<!--<MSBuild Projects="$(MSBuildProjectFullPath);$(MSBuildProjectFullPath)" Targets="Wait3000" BuildInParallel="true" />-->
<!-- Runs in parallel!. Note that b.targets is a copy of the original a.targets -->
<MSBuild Projects="$(MSBuildProjectFullPath);b.targets" Targets="Wait3000" BuildInParallel="true" />
<!-- Runs sequentially -->
<ItemGroup>
<Waits Include="3000;2000"/>
</ItemGroup>
<Wait DurationMs="%(Waits.Identity)" />
</Target>
<Target Name="Wait3000">
<Wait DurationMs="3000" />
</Target>
<UsingTask TaskName="Wait" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
<ParameterGroup>
<DurationMs ParameterType="System.Int32" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
Log.LogMessage(string.Format("{0:HH\\:mm\\:ss\\:fff} Start DurationMs={1}", DateTime.Now, DurationMs), MessageImportance.High);
System.Threading.Thread.Sleep(DurationMs);
Log.LogMessage(string.Format("{0:HH\\:mm\\:ss\\:fff} End DurationMs={1}", DateTime.Now, DurationMs), MessageImportance.High);
</Code>
</Task>
</UsingTask>
</Project>
I know this is old, but if you get a few minutes, revisit your attempt to use the MSBuild task. Using the Properties and/or AdditionalProperties reserved item metadata elements* will resolve the issue you described in your code sample ("Runs only once - I guess MSBuild detects it's the same project").
The MSBuild file below processes items from an ItemGroup in parallel via MSBuild's parallel support (including /maxCpuCount). It does not use BuildTargetsInParallel from the MSBuild Extension Pack, nor any other custom or inline task.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" >
<ItemGroup>
<Waits Include="3000;2000"/>
</ItemGroup>
<ItemGroup>
<ProjectItems Include="$(MSBuildProjectFullPath)">
<Properties>
WaitMs=%(Waits.Identity)
</Properties>
</ProjectItems>
</ItemGroup>
<MSBuild Projects="#(ProjectItems)" Targets="WaitSpecifiedMs" BuildInParallel="true" />
</Target>
<Target Name="WaitSpecifiedMs">
<Wait DurationMs="$(WaitMs)" />
</Target>
</Project>
* Well-hidden under "Properties Metadata" on the MSBuild Task reference page.
As you said yourself, you can't parallelize on target or task level, you can yield though.
My custom tasks parallelize heavily using TPL, i.e. my base task wrapper has a ForEach wrapper.
public bool ForEach<T>(IEnumerable<T> enumerable, Action<T> action, int max = -1)
{
return enumerable != null && Parallel.ForEach(enumerable, new ParallelOptions { MaxDegreeOfParallelism = max }, (e, s) =>
{
if (Canceled)
s.Stop();
if (s.ShouldExitCurrentIteration)
return;
action(e);
Interlocked.Increment(ref _total);
}).IsCompleted;
}
Typically limit is omitted and managed by .NET itself, with few exception like non-thread safe operations like MSDeploy, deploying SSRS reports that has a config DoS limit of 20 from single IP, or a zip task that degrades heavily if it's more than CPU count even by 1. It's probably not worth reading maxCpuCount and use Environment.ProcessorCount or %NUMBER_OF_PROCESSORS%, but you can try parsing the command line or reflecting on the host object, e.g. my base task class has this method to get all properties, targets, etc. for various extra special global flags.
private void Engine(object host)
{
var type = host.GetType();
if (type.FullName != "Microsoft.Build.BackEnd.TaskHost")
{
Log.Warn("[Host] {0}", type.AssemblyQualifiedName);
return;
}
var flags = BindingFlags.NonPublic | BindingFlags.Instance;
var taskLoggingContext = type.GetProperty("LoggingContext", flags).GetValue(host, null);
var targetLoggingContext = taskLoggingContext.GetType().GetProperty("TargetLoggingContext", flags).GetValue(taskLoggingContext, null);
ProjectTask = taskLoggingContext.GetType().GetProperty("Task", flags).GetValue(taskLoggingContext, null).To<ProjectTaskInstance>();
ProjectTarget = targetLoggingContext.GetType().GetProperty("Target", flags).GetValue(targetLoggingContext, null).To<ProjectTargetInstance>();
var entry = type.GetField("requestEntry", flags).GetValue(host);
var config = entry.GetType().GetProperty("RequestConfiguration").GetValue(entry, null);
Project = config.GetType().GetProperty("Project").GetValue(config, null).To<ProjectInstance>();
Properties = Project.Properties.ToDictionary(p => p.Name, p => p.EvaluatedValue);
Typical task would look something like this using ForEach:
public class Transform : Task
{
[Required]
public ITaskItem[] Configs { get; set; }
protected override void Exec()
{
//...
ForEach(Configs, i =>
{
//...
}, Environment.ProcessorCount);
//...
}

NLog fails to open file on Windows with Mono

I have a small set of ServiceStack REST services that is using NLog 2.1 (from NuGet) for logging.
My test server is running:
Windows 7
IIS 7.5
.NET 4.5
NLog config:
<nlog throwExceptions="true" internalLogToConsole="true" internalLogLevel="Debug" xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="c" xsi:type="Console" />
<target name="f1" xsi:type="File" fileName="C:\logs\test.log" />
</targets>
<rules>
<logger name="*" writeTo="c,f1" />
</rules>
</nlog>
My NLog config is exceedingly simple... just trying to get it working...
and in this configuration, everything works fine. NLog creates the log files correctly.
On my DEVELOPMENT machine, I am using:
Windows 7
Xamarin Studio / XSP4
Mono 3.2.3
Here is my Application_Start...
protected void Application_Start() {
LogManager.LogFactory = new NLogFactory();
ILog log = LogManager.GetLogger(typeof(Global));
log.Info("Application_Start called");
try {
new AppHost().Init();
} catch (Exception e) {
log.Error("Exception caught initializing AppHost");
}
}
In this configuration, my service's AppHost().Init() throws an exception as ServiceStack is registering my services in ServiceController.cs. I believe that part is irrelevant except that it is the first time something is logged outside of Application_Start (because both of the calls in Application_Start work... the log.info before the exception and the log.error after the exception).
Here is the exception that is shown:
The most relevant bit is that there was a System.NotImplementedException thrown at NLog.Internal.FileAppenders.BaseFileAppender.WindowsCreateFile (System.String fileName, Boolean allowConcurrentWrite).
I have found a workaround (in the accepted answer below). Hopefully anyone else who runs into this will quickly come upon this solution.
Some digging around on Google led me to this NLog pull request:
Avoid Win32-specific file functions in Mono where parts not implemented.
It appears that this change tries to use the preprocessor to NOT call WindowsCreateFile at all. However, for some reason, this still executes.
So I then went to check the newest version of BaseFileAppender.cs in the NLog GitHub repository to make sure someone didn't at some later point break this again.
#if !NET_CF && !SILVERLIGHT && !MONO
try
{
if (!this.CreateFileParameters.ForceManaged && PlatformDetector.IsDesktopWin32)
{
return this.WindowsCreateFile(this.FileName, allowConcurrentWrite);
}
}
catch (SecurityException)
{
InternalLogger.Debug("Could not use native Windows create file, falling back to managed filestream");
}
#endif
Hmmm... it's still there. What gives? Why doesn't MONO seem to be defined by the preprocessor, thus allowing this block to execute? I'm not sure. Before I started down that path of investigation, I noticed another change...
if (!this.CreateFileParameters.ForceManaged && ...
So after following that tracing that ForceManaged boolean back to it's origin, it appeared that I could set forceManaged="true" on my FileTarget declaration in my NLog config. Could it really be that simple?!
<nlog throwExceptions="true" internalLogToConsole="true" internalLogLevel="Debug" xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="c" xsi:type="Console" />
<target name="f1" xsi:type="File" forceManaged="true" fileName="C:\logs\test.log" />
</targets>
<rules>
<logger name="*" writeTo="c,f1" />
</rules>
</nlog>
Once that change was made, everything worked... the call to WindowsCreateFile that was throwing the exception was skipped & a managed filestream was used instead, which works great. Hallelujah!
Lastly, I could not find that forceManaged flag in the NLog documentation anywhere. Would likely have found this solution sooner if it was.

Is there a way to easily output the current time in MSBuild (version 3.5)?

I'd like to output the current time (preferably before every task or at minimum when starting/completing every target) when invoking MSBuild 3.5.
I tried creating a target that I would continually call that looks like:
<Target Name="EchoTime">
<Time Format="yyyy-MM-dd HH:mm:ss.fff">
<Output TaskParameter="FormattedTime" PropertyName="currentTime" />
</Time>
<Message Text = "$(currentTime)" />
</Target>
...but it turns out that one target can only call another target once per execution.
So if I try...
<Target Name="TimeTest" >
<Message Text = "--------------------------------------------------" />
<CallTarget Targets="EchoTime" />
<Message Text = " " />
<Message Text = "Try calling EchoTime again" />
<Message Text = " " />
<CallTarget Targets="EchoTime" />
<Message Text = "--------------------------------------------------" />
</Target>
Then the output looks like...
Build started 10/12/2011 2:24:52 PM.
Project "C:\Temp\MSBuildSandbox\MSBuild_EchoTime.xml" on node 0 (TimeTest target(s)).
--------------------------------------------------
EchoTime:
2011-10-12 14:24:52.756
TimeTest:
Try calling EchoTime again
--------------------------------------------------
Done Building Project "C:\Temp\MSBuildSandbox\MSBuild_EchoTime.xml" (TimeTest target(s)).
Anyone know an easy way to achieve this?
CallTarget cannot execute the same target twice but there's a workaround using the MSBuild task:
<Target Name="TimeTest" >
<Message Text = "--------------------------------------------------" />
<MSBuild
Targets="EchoTime"
Projects="$(MSBuildProjectFile)"
Properties="prop1=val1"
/>
<Message Text = " " />
<Message Text = "Try calling EchoTime again" />
<Message Text = " " />
<MSBuild
Targets="EchoTime"
Projects="$(MSBuildProjectFile)"
Properties="prop1=val2"
/>
<Message Text = "--------------------------------------------------" />
</Target>
Note setting the value for prop1 to a different value for each subsequent call.

msbuild ITaskItem output

I'm trying to use MSBuild.ExtensionPack.Web.Iis7AppPool task with GetInfo task action.
This task's output parameter is SiteInfo property, which its type is ITaskItem. This is a propery, not a collection.
How can I access the SiteInfo metadata? I can access with $, but then all I can get is the web site name. I tried to use %(SiteInfo.) with no success.
If the output parameter is an ITaskItem and you want to access the metadata then you need to assign it to an ItemGroup instead of a Property.
<Target Name="Test">
<MSBuild.ExtensionPack.Web.Iis7AppPool
TaskAction="GetInfo"
Name="DefaultAppPool">
<Output TaskParameter="AppPoolInfo" ItemName="PoolInfo" />
</MSBuild.ExtensionPack.Web.Iis7AppPool>
<Message Text="AppPool info = [#(PoolInfo)]" />
<Message Text="AppPool MaxProcesses = [%(PoolInfo.MaxProcesses)]" />
<MSBuild.ExtensionPack.Web.Iis7WebSite
TaskAction="GetInfo"
Name="Default Web Site">
<Output TaskParameter="SiteId" PropertyName="WebId" />
<Output TaskParameter="SiteInfo" ItemName="WebInfo" />
</MSBuild.ExtensionPack.Web.Iis7WebSite>
<Message Text="WebSite id = [$(WebId)]" />
<Message Text="WebSite app pool = [%(WebInfo.ApplicationPoolName)]" />
</Target>