views:

966

answers:

3

There is a MSBuild script, that includes number if Delphi and C# projects, unit tests etc.

The problem is: how to mark build failed if warnings were raised (for testing purposes, not for release builds)? Using LogError instead of LogWarning in custom tasks seems to be not a good option, because the build should test as much as it's able (until real error) to report as much warnings as possible in a time (build project is using in CruiseControl.NET).

May be, the solution is to create my own logger that would store warnings flag inside, but I cannot find if there is a way to read this flag in the end of build?

P.S. There is no problem to fail the build immediately after receiving a warning (Delphi compiler output is processed by custom task, and /warnaserror could be used for C#), but the desired behavior is "build everything; collect all warnings; fail the build" to report about all warnings, not only about the first one.

A: 

The C# compiler (csc.exe) has a /warnaserror switch will will treat warnings as errors and fail the build. This is also available as a setting in the .csproj file. I assume Delphi has a similar ability.

Jason Stangroome
Sorry, but the task is to collect all warnings (or, at least, as much as possible, until real error), and mark build as failed after that. It's not convenient to wait for CruiseControl build; receive message about first warning; fix the problem and wait for the next one.
Abelevich
For who wants to know anyway: Delphi 2009 has a treat all warnings as errors
Lars Truijens
Sorry, didn't realise CC.NET gives up after first error.
Jason Stangroome
You can have msbuild ignore errors, but then it wouldn't fail at all and all your builds are succeeded.
Lars Truijens
+1  A: 
msbuild.exe %~nx1 /t:Rebuild /p:Configuration=Release >> %MrB-BUILDLOG%
findstr /r /c:"[1-9][0-9]* Error(s)" >> %MrB-BUILDLOG%
if not errorlevel 1 (
   echo ERROR: sending notification email for build errors in '%~nx1'. >> %MrB-BUILDLOG%
) else (
   findstr /r /c:"[1-9][0-9]* Warning(s)" >> %MrB-BUILDLOG%
   if not errorlevel 1 (
       echo ERROR: sending notification email for build warnings in '%~nx1'. >>

%MrB-BUILDLOG% ) else ( echo Successful build of '%~nx1'. >> %MrB-BUILDLOG% ) )

kenny
Not a solution in msbuild, but a workaround
Lars Truijens
+5  A: 

AFAIK MSBuild has no built-in support to retrieve the warning count at a given point of the build script. You can however follow these steps to achieve this goal:

  1. Create a custom logger that listens for the warning event and counts the number of warnings
  2. Create a custom task that exposes an [Output] WarningCount property
  3. The custom task gets somehow the value of the warning count from the custom logger

The most difficult step is step 3. For this there are several options and you can freely search them under IPC - Inter Process Comunication. Follows a working example of how you can achieve this. Each item is a different Class Library.

SharedMemory

http://weblogs.asp.net/rosherove/archive/2003/05/01/6295.aspx

I've created a wrapper for named shared memory that was part of a larger project. It basically allows serialized types and object graphs to be stored in and retrieved from shared memory (including as you'd expect cross process). Whether the larger project ever gets completed is another matter ;-).

SampleLogger

Implements the custom logger that keeps track of the warning count.

namespace SampleLogger
{
    using System;
    using Microsoft.Build.Utilities;
    using Microsoft.Build.Framework;
    using DM.SharedMemory;

    public class MySimpleLogger : Logger
    {
        private Segment s;
        private int warningCount;

        public override void Initialize(IEventSource eventSource)
        {
            eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised);

            this.s = new Segment("MSBuildMetadata", SharedMemoryCreationFlag.Create, 65535);
            this.s.SetData(this.warningCount.ToString());
        }

        void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
        {
            this.warningCount++;
            this.s.SetData(this.warningCount.ToString());
        }

        public override void Shutdown()
        {
            this.s.Dispose();
            base.Shutdown();
        }
    }
}

SampleTasks

Implements the custom task that reads the number of warnings raised in the MSbuild project. The custom task reads from the shared memory written by the custom logger implemented in class library SampleLogger.

namespace SampleTasks
{
    using System;
    using Microsoft.Build.Utilities;
    using Microsoft.Build.Framework;
    using DM.SharedMemory;

    public class BuildMetadata : Task
    {
        public int warningCount;

        [Output]
        public int WarningCount
        {
            get
            {
                Segment s = new Segment("MSBuildMetadata", SharedMemoryCreationFlag.Attach, 0);
                int warningCount = Int32.Parse(s.GetData() as string);
                return warningCount;
            }
        }

        public override bool Execute()
        {
            return true;
        }
    }
}

Going for a spin.

<?xml version="1.0" encoding="UTF-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Main">
    <UsingTask TaskName="BuildMetadata" AssemblyFile="F:\temp\SampleLogger\bin\debug\SampleTasks.dll" />

    <Target Name="Main">
     <Warning Text="Sample warning #1" />
     <Warning Text="Sample warning #2" />

     <BuildMetadata>
      <Output
       TaskParameter="WarningCount"
       PropertyName="WarningCount" />
     </BuildMetadata>

     <Error Text="A total of $(WarningCount) warning(s) were raised." Condition="$(WarningCount) > 0" />
    </Target>
</Project>

If you run the following command:

c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild test.xml /logger:SampleLogger.dll

This will be the output:

Microsoft (R) Build Engine Version 2.0.50727.3053
[Microsoft .NET Framework, Version 2.0.50727.3053]
Copyright (C) Microsoft Corporation 2005. All rights reserved.

Build started 30-09-2008 13:04:39.
__________________________________________________
Project "F:\temp\SampleLogger\bin\debug\test.xml" (default targets):

Target Main:
    F:\temp\SampleLogger\bin\debug\test.xml : warning : Sample warning #1
    F:\temp\SampleLogger\bin\debug\test.xml : warning : Sample warning #2
    F:\temp\SampleLogger\bin\debug\test.xml(15,3): error : A total of 2 warning(s) were raised.
Done building target "Main" in project "test.xml" -- FAILED.

Done building project "test.xml" -- FAILED.

Build FAILED.
F:\temp\SampleLogger\bin\debug\test.xml : warning : Sample warning #1
F:\temp\SampleLogger\bin\debug\test.xml : warning : Sample warning #2
F:\temp\SampleLogger\bin\debug\test.xml(15,3): error : A total of 2 warning(s) were raised.
    2 Warning(s)
    1 Error(s)

Time Elapsed 00:00:00.01
smink
I tried to use almost the same thing, but used static member in Task subclass to collect warnings count (and it didn't work). Thanks for idea about shared memory.
Abelevich