- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有代码可以从 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/
我有两个维度 DimFlag 和 DimPNL 以及一个事实表 FactAmount 。我正在寻找:当 pnl 是 stat(Is Stat=1) 时:sum (Actual x FlagId)对于
我想对包含其部分内容的文本字段执行简单搜索,但我不知道从哪里开始。我基本上想要人们对“包含搜索”的期望。如果我在 issue 中搜索 345 ,我会想要这个结果: 123456 234567 3456
我在 VBE 的 C# 插件中有这段代码(强调“VBE”:它不是 MS-Office 插件): public abstract class HostApplicationBase : IHostApp
我有一个 ImageView,它显示来自资源的图像。ImageView 的宽度是固定的 (60dp)。高度设置为 wrap_content。调整图像大小以适合此宽度(节省宽高比 - 这很完美) 问题是
我正在建立一个网站,但遇到了一个问题:谷歌浏览器开发者工具中的背景以较低/较高的分辨率延伸。当我直接从手机打开网站时,背景不适合屏幕,只是“剪切”了背景。 这是网站:https://feargames
好吧,首先,这是 HTML 模板: ... ... ... ... 如您所见,页面位于标题下方,并且通过 JS 代码可见
我读到了 BK-trees (Burkhard-Keller-Trees) 几个月前,据说这是一种保存您想通过距离度量再次读取的内容的好方法。因此,在每种情况下,您都希望通过相似性检索某些内容。 然而
在 python 中,很容易根据字符数用空格填充字符串。例如: print "aaa".ljust(10) + "end" print "www".ljust(10) + "end" 输出是: aaa
我的问题不是特定于编程语言的,而是更通用的问题,以了解人们的思维方式。 通常在大型开发公司中,每项工作都有特定的角色,例如程序员和架构师。因此架构师的观点是拥有完美的架构师和解决方案设计,另一方面程序
我想将数据传递给 then 方法,但是当我通过给它 this.passedResolve 来执行此操作时,它会得到 undefined function Promises(callback){
我希望使用 Erlang/Elixir 在金融市场创建一个平台即服务。我将在金融市场提供 AWS lambda 风格的函数,但我计划向客户分发我自己的基于 ARM 的硬件终端(基于 Nvidia Je
已结束。此问题正在寻求书籍、工具、软件库等的推荐。它不满足Stack Overflow guidelines 。目前不接受答案。 我们不允许提出寻求书籍、工具、软件库等推荐的问题。您可以编辑问题,以便
我已经看到很多代码,这些代码使用Runnable的循环时间来实现某些计时器/超时。问题是,如果有人决定更改此Runnable的循环时间,则计时器将不正确。 例: #define FOO_TIMER_1
当我将 WPF DataGrid 的 ColumnHeaderHeight 设置为 Auto (double.NaN) 时,如何获取列标题的实际呈现高度? 我似乎无法在 DataGrid 类中找到该属
目前最实用的png修复方法是什么?轻量级,支持背景重复和背景位置。 最佳答案 IE7.JS在我看来: IE7.js is a JavaScript library to make Microsoft
我已经进行了长时间的搜索并尝试了常见的嫌疑人,但现在是寻求帮助的时候了。 我的 Android Activity 非常愉快地从 SQLite 加载 EditText、Spinner 和 CheckBo
因此,我在 MySQL 全文中创建精确搜索时遇到了一些困难。 在我的数据库中,我正在尝试查找标题中包含特定关键字的职位。 所以我可以尝试 WHERE MATCH(jobTitle) AGAINST (
我正在尝试将 JavaScript 包含到一个表单应用程序中,该应用程序从现场收集施工数据。我已经用谷歌搜索了这个废话,但我无法弄清楚将 html 元素保存在数组中是否合法(或者我的语法是否正确)。
我知道有六种方法可以获取 session.save_path 指令的值(phpinfo()、session_save_path()等),但当值为空字符串时(默认情况下为空字符串),实际路径可以是多个位
我知道 npm 库在安装时可以在分层树中安装同一库的多个版本,如下所示: a@0.1.0 -> b@1.0 -> c@2.0 -> b@2.0 在上面,版本0.1.0的包a被拉入
我是一名优秀的程序员,十分优秀!