gpt4 book ai didi

c# - 如果我从 C# 代码构建解决方案,我如何知道实际构建了哪些应用程序?

转载 作者:太空狗 更新时间:2023-10-29 21:56:57 25 4
gpt4 key购买 nike

我有代码可以从 C# 构建(而不是重新构建)整个解决方案。

标准构建只会编译实际发生变化的项目。

构建完成后,我想知道实际构建了哪些项目。

我试过:

1 - 从 BuidlResult 中寻找更改/未更改的值(或类似值)构建完成后

2 - 附上 custom logger并捕获每个事件,然后仔细研究消息以查看更改和未更改的项目之间是否有任何区别

我真的很惊讶,这么基本的信息不是现成的。例如,Logger 的 ProjectFinished 事件的 ProjectFinishedEventArgs 参数包含 bool 值或状态值似乎合乎逻辑。但如果它在那里,那我就忽略了它。

有谁知道如何判断 msbuild 的产品是否被重新编译?我讨厌诉诸于检查输出二进制文件的时间戳,但也许这就是我必须要做的。

private void DoBuild()
{
ProjectCollection pc = new ProjectCollection();
BuildLog = new CRMBuildLogger { Parameters = _logfilename };
Dictionary<string, string> globalProperty = new Dictionary<string, string>();
BuildParameters bp = new BuildParameters(pc)
{
DetailedSummary = true,
Loggers = new List<ILogger>() { BuildLog }
};
BuildRequestData buildRequest = new BuildRequestData(SolutionFileName, globalProperty, "12.0", new[] { "Build" }, null);
BuildLog.BuildResult = BuildManager.DefaultBuildManager.Build(bp, buildRequest);
}

最佳答案

我修改了我的 Logger 类来满足我的需求,但我仍然希望有一个更“原生”的解决方案。就在这里,以防其他人发现它有用。

大意是在项目构建前记录文件修改时间,然后再记录。如果它发生了变化,则假设该项目已重新编译。

我从 MSDN example 开始, 并修改了这些方法:

eventSource_ProjectStarted
eventSource_ProjectFinished

如果您从这里开始,那么剩下的应该就很清楚了。如果有人有问题,我很乐意回答。

更好的是,如果有人可以过来驳斥这个答案并说“你为什么不做 X”,那么我会很高兴听到“X”是什么。

using System;
using System.Collections.Generic;
using System.IO;
using System.Security;
using BuildMan.Classes;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace JustBuild
{
public struct ProjectOutputTimeStamp
{
public string ProjectName;
public DateTime OutputDateTime_BeforeBuild;
public DateTime OutputDateTime_AfterBuild;
}

public class CRMBuildLogger : Logger
{
public List<string> Errors = new List<string>();
public List<string> Warnings = new List<string>();
public List<string> Messages = new List<string>();
public List<ProjectOutputTimeStamp> outputs = new List<ProjectOutputTimeStamp>();

public BuildResult BuildResult;
/// <summary>
/// Initialize is guaranteed to be called by MSBuild at the start of the build
/// before any events are raised.
/// </summary>
public override void Initialize(IEventSource eventSource)
{
if (null == Parameters)
{
throw new LoggerException("Log file was not set.");
}
string[] parameters = Parameters.Split(';');

string logFile = parameters[0];
if (String.IsNullOrEmpty(logFile))
{
throw new LoggerException("Log file was not set.");
}

if (parameters.Length > 1)
{
throw new LoggerException("Too many parameters passed.");
}

try
{
// Open the file
streamWriter = new StreamWriter(logFile);
}
catch (Exception ex)
{
if
(
ex is UnauthorizedAccessException
|| ex is ArgumentNullException
|| ex is PathTooLongException
|| ex is DirectoryNotFoundException
|| ex is NotSupportedException
|| ex is ArgumentException
|| ex is SecurityException
|| ex is IOException
)
{
throw new LoggerException("Failed to create log file: " + ex.Message);
}
// Unexpected failure
throw;
}

// For brevity, we'll only register for certain event types. Loggers can also
// register to handle TargetStarted/Finished and other events.
if (eventSource == null) return;
eventSource.ProjectStarted += eventSource_ProjectStarted;
eventSource.MessageRaised += eventSource_MessageRaised;
eventSource.WarningRaised += eventSource_WarningRaised;
eventSource.ErrorRaised += eventSource_ErrorRaised;
eventSource.ProjectFinished += eventSource_ProjectFinished;
}

void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
{
// BuildErrorEventArgs adds LineNumber, ColumnNumber, File, amongst other parameters
string line = String.Format(": ERROR {0}({1},{2}): ", e.File, e.LineNumber, e.ColumnNumber);
Errors.Add(line);
WriteLineWithSenderAndMessage(line, e);
}

void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
{
// BuildWarningEventArgs adds LineNumber, ColumnNumber, File, amongst other parameters
string line = String.Format(": Warning {0}({1},{2}): ", e.File, e.LineNumber, e.ColumnNumber);
Warnings.Add(line);
WriteLineWithSenderAndMessage(line, e);
}

void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
{
// BuildMessageEventArgs adds Importance to BuildEventArgs
// Let's take account of the verbosity setting we've been passed in deciding whether to log the message
if ((e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal))
|| (e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal))
|| (e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
)
{
Messages.Add(e.Message);
WriteLineWithSenderAndMessage(String.Empty, e);
}
}

void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
{
int idx = IndexOfProjectTimeStamp(e.ProjectFile);
DateTime outputfiledatetime = DateTime.MinValue;
StudioProject proj = new StudioProject(e.ProjectFile);
FileInfo outputFile;
if (File.Exists(e.ProjectFile))
{
outputFile = new FileInfo(proj.OutputFile());
outputfiledatetime = outputFile.LastWriteTime;
}

//keep track of the mod date/time of the project output.
//if the mod date changes as a result of the build, then that means the project changed.
//this is necessary because the MSBuild engine doesn't tell us which projects were actually recompiled during a "build".
//see also: http://stackoverflow.com/questions/34903800
ProjectOutputTimeStamp p = new ProjectOutputTimeStamp()
{
OutputDateTime_BeforeBuild = outputfiledatetime,
ProjectName = e.ProjectFile,
OutputDateTime_AfterBuild = DateTime.MinValue
};
if (-1 == idx)
outputs.Add(p);
else
outputs[idx] = p;

WriteLine(String.Empty, e);
indent++;
}

private int IndexOfProjectTimeStamp(string projectname)
{
for (int i = 0; i < outputs.Count; ++i)
if (outputs[i].ProjectName.ToUpper() == projectname.ToUpper())
return i;
return -1;
}

void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
{
int idx = IndexOfProjectTimeStamp(e.ProjectFile);
DateTime outputfiledatetime = DateTime.MinValue;
StudioProject proj = new StudioProject(e.ProjectFile);
FileInfo outputFile;
if (File.Exists(e.ProjectFile))
{
outputFile = new FileInfo(proj.OutputFile());
outputfiledatetime = outputFile.LastWriteTime;
}

//keep track of the mod date/time of the project output.
//if the mod date changes as a result of the build, then that means the project changed.
//this is necessary because the MSBuild engine doesn't tell us which projects were actually recompiled during a "build".
//see also: http://stackoverflow.com/questions/34903800
ProjectOutputTimeStamp p = outputs[idx];
p.OutputDateTime_AfterBuild = outputfiledatetime;

if (-1 < idx)
outputs[idx] = p;

indent--;
WriteLine(String.Empty, e);
}

public List<string> RecompiledProjects()
{
//let callers ask "which projects were actually recompiled" and get a list of VBPROJ files.
List<string> result = new List<string>();
foreach (ProjectOutputTimeStamp p in outputs)
{
if(p.OutputDateTime_AfterBuild>p.OutputDateTime_BeforeBuild)
result.Add(p.ProjectName);
}
return result;
}

/// <summary>
/// Write a line to the log, adding the SenderName and Message
/// (these parameters are on all MSBuild event argument objects)
/// </summary>
private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
{
if (0 == String.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
{
// Well, if the sender name is MSBuild, let's leave it out for prettiness
WriteLine(line, e);
}
else
{
WriteLine(e.SenderName + ": " + line, e);
}
}

/// <summary>
/// Just write a line to the log
/// </summary>
private void WriteLine(string line, BuildEventArgs e)
{
for (int i = indent; i > 0; i--)
{
streamWriter.Write("\t");
}
streamWriter.WriteLine(line + e.Message);
}

/// <summary>
/// Shutdown() is guaranteed to be called by MSBuild at the end of the build, after all
/// events have been raised.
/// </summary>
public override void Shutdown()
{
// Done logging, let go of the file
streamWriter.Close();
}

private StreamWriter streamWriter;
private int indent;
}
}

请注意,“StudioProject”类是我编写的。我不想发布整个事情,因为它有很多东西做出假设,这些假设只在我们的本地代码库中是正确的。但是,相关方法(“OutputFile”)在这里。它对项目文件本身进行非常愚蠢的扫描,以找出输出 EXE 或 DLL。

public string OutputFile()
{
if (_ProjectFile == null) return string.Empty;

string result = string.Empty;
StreamReader reader = new StreamReader(_ProjectFile);
string projFolder = new DirectoryInfo(_ProjectFile).Parent?.FullName;
bool insideCurrentConfig = false;
string configuration = string.Empty;
string assemblyName = string.Empty;
string outputPath = string.Empty;
bool isExe = false;
do
{
string currentLine = reader.ReadLine();
if (currentLine == null) continue;
if ((configuration == string.Empty) && (currentLine.Contains("<Configuration"))) configuration = currentLine.Split('>')[1].Split('<')[0];
if (!insideCurrentConfig && !isExe && currentLine.Contains("WinExe")) isExe = true;
if ((assemblyName == string.Empty) && (currentLine.Contains("<AssemblyName>"))) assemblyName = currentLine.Split('>')[1].Split('<')[0];
if (configuration != string.Empty && currentLine.Contains("<PropertyGroup") && currentLine.Contains(configuration)) insideCurrentConfig = true;
if (insideCurrentConfig && currentLine.Contains("<OutputPath>")) outputPath = currentLine.Split('>')[1].Split('<')[0];
if ((outputPath != null) && (assemblyName != null)) result = projFolder + "\\" + outputPath + assemblyName + (isExe?".exe":".dll");
if (insideCurrentConfig && currentLine.Contains("</PropertyGroup>")) return result; //if we were in the current config, and that config is ending, then we are done.
} while (!reader.EndOfStream);
return result;
}

关于c# - 如果我从 C# 代码构建解决方案,我如何知道实际构建了哪些应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34903800/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com