2016-01-20 14 views
6

Ho codice che costruirà (non re build) un'intera soluzione da C#.Se creo una soluzione dal codice C#, come posso sapere quali applicazioni sono state effettivamente create?

Una compilazione standard consente di compilare solo progetti effettivamente modificati.

Al termine della costruzione, mi piacerebbe sapere quali progetti sono stati effettivamente realizzati.

ho provato:

1 - Alla ricerca di un valore modificato/invariato (o simili) dal BuidlResult dopo la costruzione è terminata

2 - Collegamento di un custom logger e cattura ogni evento, quindi studiando attentamente la messaggi per vedere se c'è qualche differenza tra i progetti modificati e invariati

Sono davvero sorpreso che tale informazione di base non sia facilmente disponibile. Ad esempio, sembra logico che l'argomento ProjectFinishedEventArgs dell'evento ProjectFinished di Logger contenga un valore booleano o di stato. Ma se è lì, allora l'ho trascurato.

Qualcuno sa come dire se il prodotto di un msbuild è stato ricompilato o no? Odio ricorrere al controllo di timestamp sui binari di output, ma forse è quello che dovrò fare.

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); 
} 
+0

Forse si può provare a guardare qualcosa come https://github.com/nagits/BuildVision e vedere come è fatto lì. –

+0

@OmarElabd Non avevo familiarità con questo progetto; ma lo sto verificando ora. Sembra interessante, indipendentemente dal fatto che sia d'aiuto con questo problema specifico. – JosephStyons

+0

@OmarElabd che sta solo seguendo; Mi piace questo progetto e sono sicuro di poter imparare molto da esso. Tuttavia, non penso che mi possa aiutare con questo particolare problema. Sebbene ti consenta di avviare una ricostruzione o una ricostruzione, non vedo da nessuna parte che sembra a conoscenza del fatto che una build abbia effettivamente richiesto una ricompilazione. Continuerò a cercare nel caso in cui non l'ho ancora notato. – JosephStyons

risposta

1

ho modificato la mia classe Logger a darmi quello che voglio, ma sto ancora sperando per una soluzione più "nativa". Eccolo, nel caso in cui qualcun altro lo trovi utile.

L'idea generale è di notare il tempo di modifica del file prima della creazione del progetto e di annotarlo nuovamente in seguito. Se è cambiato, supponi che il progetto sia stato ricompilato.

Ho iniziato con il MSDN example, e modificato questi metodi:

eventSource_ProjectStarted 
eventSource_ProjectFinished 

Se si avvia lì, poi il resto dovrebbe essere abbastanza chiaro. Sono felice di rispondere alle domande se qualcuno le ha.

Ancora meglio, se qualcuno può venire a rispondere a questa risposta e dire "Perché non fai X", allora sarei molto felice di sapere che cos'è "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; 
    } 
} 

Si noti che la classe "StudioProject" è quella che ho scritto. Non voglio postare tutto perché ha un sacco di cose che fanno supposizioni che sarebbero vere solo nel nostro codice locale. Tuttavia, il metodo rilevante ("OutputFile") è qui. Fa una scansione piuttosto stupida attraverso il file di progetto stesso per capire l'output EXE o 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; 
} 
Problemi correlati