I modified my Logger class to give me what I want, but I still hope for a more โnativeโ solution. Here it is, if someone finds it useful.
The general idea is to mark the changed file time before building the project, and then mark it again. If it has changed, assume the project has been recompiled.
I started with an MSDN example and changed these methods:
eventSource_ProjectStarted eventSource_ProjectFinished
If you start there, then the rest should be pretty clear. I am happy to answer questions if anyone has it.
Even better, if someone can come and delete this answer and say โWhy don't you make Xโ, then I would be very happy to hear what โXโ is.
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;
Note that the "StudioProject" class is the one I wrote. I donโt want to publish all this because it has a lot of things that make assumptions that would be true only in our local code base. However, the corresponding method ("OutputFile") is here. It does a rather dumb scan through the project file itself to determine the output EXE or 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;
source share