MSBuild:如何获取引发的警告数量?

时间:2020-03-06 14:54:59  来源:igfitidea点击:

有一个MSBuild脚本,其中包括Delphi和Cprojects的编号,单元测试等。

问题是:如果引发警告(出于测试目的,而不是发行版本),如何标记构建失败?在自定义任务中使用LogError而不是LogWarning似乎不是一个好选择,因为构建应在可能的情况下进行尽可能多的测试(直到出现实际错误),以报告尽可能多的警告(构建项目正在CruiseControl.NET中使用) )。

可能的解决方案是创建自己的记录器,该记录器将警告标志存储在内部,但是我找不到在构建结束时是否有办法读取此标志?

P.S.收到警告后立即使构建失败是没有问题的(Delphi编译器输出由自定义任务处理,并且/ warnaserror可以用于C#),但是所需的行为是"构建所有内容;收集所有警告;使构建失败"报告所有警告,而不仅仅是第一个警告。

P.P.S.据我实际上并不需要警告的数量,而只是警告它们的存在的标记,我决定简化信令机制,并使用琐碎的Mutex代替共享内存。代码如下:

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.Threading;

namespace Intrahealth.Build.WarningLogger
{
    public sealed class WarningLoggerCheck : Task
    {
        public override bool Execute()
        {
            Log.LogMessage("WarningLoggerCheck:" + mutexName + "...");
            result = false;
            Mutex m = null;
            try
            {
                m = Mutex.OpenExisting(mutexName);
            }
            catch (WaitHandleCannotBeOpenedException)
            {
                result = true;
            }
            catch (Exception)
            {
            }

            if (result)
                Log.LogMessage("WarningLoggerCheck PASSED");
            else
                Log.LogError("Build log contains warnings. Build is FAILED");

            return result;
        }

        private bool result = true;
        [Output]
        public bool Result
        {
            get { return result; }
        }

        private string mutexName = "WarningLoggerMutex";
        public string MutexName
        {
            get { return mutexName; }
            set { mutexName = value ?? "WarningLoggerMutex"; }
        }
    }

    public class WarningLogger : Logger
    {
        internal static int warningsCount = 0;
        private string mutexName = String.Empty;
        private Mutex mutex = null;

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

        private void SetMutex()
        {
            if (mutexName == String.Empty)
            {
                mutexName = "WarningLoggerMutex";
                if (this.Parameters != null && this.Parameters != String.Empty)
                {
                    mutexName = this.Parameters;
                }
            }

            mutex = new Mutex(false, mutexName);
        }

        void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
        {
            if (e.Message != null && e.Message.Contains("MSB3146"))
                return;
            if (e.Code != null && e.Code.Equals("MSB3146"))
                return;

            if (warningsCount == 0)
                SetMutex();
            warningsCount++;
        }
    }
}

解决方案

Ccompiler(csc.exe)具有一个/ warnaserror开关,它将把警告视为错误,并使构建失败。这也可以作为.csproj文件中的设置使用。我认为Delphi具有类似的功能。

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%
) 别的 (
echo成功建立'%〜nx1'。 >>%MrB-BUILDLOG%
)
)

AFAIK MSBuild不支持在构建脚本的给定点检索警告计数的内置支持。但是,我们可以按照以下步骤实现此目标:

  • 创建一个自定义记录器,侦听警告事件并计算警告数量
  • 创建一个自定义任务,该任务公开一个[Output] WarningCount属性
  • 自定义任务从自定义记录器中以某种方式获取警告计数的值

最困难的步骤是步骤3. 为此,有几个选项,我们可以在IPC进程间通信中自由搜索它们。遵循一个如何实现此目标的工作示例。每个项目都是一个不同的类库。

共享内存

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

实现跟踪警告计数的自定义记录器。

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

实现自定义任务,该任务读取MSbuild项目中引发的警告数量。定制任务从由类库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;
        }
    }
}

去兜风。

<?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>

如果运行以下命令:

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

这将是输出:

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