ReactOS Sin Verifier Added: trunk/cis/ReactOS.Verify/ Added: trunk/cis/ReactOS.Verify/App.config Added: trunk/cis/ReactOS.Verify/AssemblyInfo.cs Added: trunk/cis/ReactOS.Verify/Default.build Added: trunk/cis/ReactOS.Verify/Main.cs Added: trunk/cis/ReactOS.Verify/RedirectableProcess.cs _____
Added: trunk/cis/ReactOS.Verify/App.config --- trunk/cis/ReactOS.Verify/App.config 2005-11-23 16:57:00 UTC (rev 19489) +++ trunk/cis/ReactOS.Verify/App.config 2005-11-23 17:25:55 UTC (rev 19490) @@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8" ?> +<configuration> + <appSettings> + <add key="smtpServer" value="localhost" /> + <add key="errorEmail" value="mailbox@somewhere-on-the-net" /> + <add key="makeParameters" value="" /> + <add key="fastDisk" value="" /> + </appSettings> +</configuration> _____
Added: trunk/cis/ReactOS.Verify/AssemblyInfo.cs --- trunk/cis/ReactOS.Verify/AssemblyInfo.cs 2005-11-23 16:57:00 UTC (rev 19489) +++ trunk/cis/ReactOS.Verify/AssemblyInfo.cs 2005-11-23 17:25:55 UTC (rev 19490) @@ -0,0 +1,14 @@
+using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("ReactOS Sin Verifier")] +[assembly: AssemblyDescription("ReactOS Sin Verifier")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("ReactOS Project")] +[assembly: AssemblyProduct("React Operating System")] +[assembly: AssemblyCopyright("Copyright 2005 ReactOS Project")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] _____
Added: trunk/cis/ReactOS.Verify/Default.build --- trunk/cis/ReactOS.Verify/Default.build 2005-11-23 16:57:00 UTC (rev 19489) +++ trunk/cis/ReactOS.Verify/Default.build 2005-11-23 17:25:55 UTC (rev 19490) @@ -0,0 +1,20 @@
+<?xml version="1.0"?> +<project name="ReactOS.Verify" default="build"> + + <property name="output.dir" value="bin" /> + + <target name="build" description="Build component"> + <mkdir dir="${output.dir}" /> + <csc target="exe" + output="${output.dir}\ReactOS.Verify.exe" + optimize="true" + debug="true" + doc="${output.dir}\ReactOS.Verify.xml" + warninglevel="0"> + <sources> + <include name="*.cs" /> + </sources> + </csc> + </target> + +</project> _____
Added: trunk/cis/ReactOS.Verify/Main.cs --- trunk/cis/ReactOS.Verify/Main.cs 2005-11-23 16:57:00 UTC (rev 19489) +++ trunk/cis/ReactOS.Verify/Main.cs 2005-11-23 17:25:55 UTC (rev 19490) @@ -0,0 +1,279 @@
+using System; +using System.IO; +using System.Diagnostics; +using System.Configuration; +using System.Web.Mail; +using System.Collections; +using System.Collections.Specialized; + +namespace ReactOS.Verify +{ + public class MainClass + { + /// <summary> + /// Run the application. + /// </summary> + /// <param name="script">Script to run.</param> + /// <param name="args">Arguments to pass to script.</param> + /// <param name="workingDirectory">Working directory.</param> + /// <param name="standardOutput">Receives standard output.</param> + /// <param name="standardError">Receives standard error.</param> + /// <returns> + /// Exit code. + /// </returns> + private static int RunScript(string script, + string args, + string workingDirectory, + StringDictionary environmentVarables, + out string standardOutput, + out string standardError) + { + ProcessStartInfo scriptProcessStartInfo = new ProcessStartInfo(script, + args); + scriptProcessStartInfo.CreateNoWindow = true; + /* + * All standard streams must be redirected. + * Otherwise DuplicateHandle() will fail. + */ + scriptProcessStartInfo.RedirectStandardInput = true; + scriptProcessStartInfo.RedirectStandardError = true; + scriptProcessStartInfo.RedirectStandardOutput = true; + scriptProcessStartInfo.UseShellExecute = false; + scriptProcessStartInfo.WorkingDirectory = workingDirectory; + if (environmentVarables != null) + { + foreach (DictionaryEntry de in environmentVarables) + scriptProcessStartInfo.EnvironmentVariables.Add(de.Key as string, de.Value as string); + } + RedirectableProcess redirectableProcess = new RedirectableProcess(scriptProcessStartInfo); + standardOutput = redirectableProcess.ProcessOutput; + standardError = redirectableProcess.ProcessError; + return redirectableProcess.ExitCode; + } + + /// <summary> + /// Retrieve value of configuration from configuration file. + /// </summary> + /// <param name="name">Name of configuration option.</param> + /// <param name="defaultValue"> + /// Default value to be returned if the option does not exist. + /// </param> + /// <returns> + /// Value of configuration option or null if the option does not + /// exist and no default value is provided. + /// </returns> + private static string GetConfigurationOption(string name, + string defaultValue) + { + if (ConfigurationSettings.AppSettings[name] != null) + { + string s = ConfigurationSettings.AppSettings[name].Trim(); + return s.Equals(String.Empty) ? defaultValue : s; + } + else + return defaultValue; + } + + /// <summary> + /// Send an email. + /// </summary> + /// <param name="subject">Subject of the email.</param> + /// <param name="body">Content of the email.</param> + private static void SendErrorMail(string subject, string body) + { + try + { + string smtpServer = GetConfigurationOption("smtpServer", "localhost"); + string email = GetConfigurationOption("errorEmail", null); + if (email == null) + return; + MailMessage mm = new MailMessage(); + mm.Priority = MailPriority.Normal; + mm.From = email; + mm.To = email; + mm.Subject = subject; + mm.Body += body; + mm.Body += "<br>"; + mm.BodyFormat = MailFormat.Html; + SmtpMail.SmtpServer = smtpServer; + SmtpMail.Send(mm); + } + catch (Exception ex) + { + Console.Error.WriteLine(ex.Message); + } + } + + /// <summary> + /// Fail with an error message. + /// </summary> + /// <param name="text">Error message.</param> + private static void Fail(string text) + { + Console.WriteLine(text); + } + + /// <summary> + /// Paths to fast disk for temporary files. + /// </summary> + private static string TemporaryFastDisk + { + get + { + string temporaryFastDisk = GetConfigurationOption("temporaryFastDisk", null); + if (temporaryFastDisk == null || temporaryFastDisk.Trim().Equals(String.Empty)) + return null; + return temporaryFastDisk.Trim(); + } + } + + /// <summary> + /// Paths to fast disk for intermediate files. + /// </summary> + private static string IntermediateFastDisk + { + get + { + string intermediateFastDisk = GetConfigurationOption("intermediateFastDisk", null); + if (intermediateFastDisk == null || intermediateFastDisk.Trim().Equals(String.Empty)) + return null; + return intermediateFastDisk.Trim(); + } + } + + /// <summary> + /// Paths to fast disk for output files. + /// </summary> + private static string OutputFastDisk + { + get + { + string outputFastDisk = GetConfigurationOption("outputFastDisk", null); + if (outputFastDisk == null || outputFastDisk.Trim().Equals(String.Empty)) + return null; + return outputFastDisk.Trim(); + } + } + + /// <summary> + /// Return collection of environment variables. + /// </summary> + /// <returns>Collection of environment variables or null if there is none.</returns> + private static StringDictionary GetEnvironmentVarables() + { + StringDictionary environmentVarables = new StringDictionary(); + if (TemporaryFastDisk != null) + environmentVarables.Add("ROS_TEMPORARY", TemporaryFastDisk); + if (IntermediateFastDisk != null) + environmentVarables.Add("ROS_INTERMEDIATE", IntermediateFastDisk); + if (OutputFastDisk != null) + environmentVarables.Add("ROS_OUTPUT", OutputFastDisk); + return environmentVarables; + } + + /// <summary> + /// Run make. + /// </summary> + /// <param name="arguments">Arguments.</param> + /// <param name="standardOutput">Receives standard output.</param> + /// <param name="standardError">Receives standard error.</param> + /// <returns>Make exit code.</returns> + private static int RunMake(string arguments, + out string standardOutput, + out string standardError) + { + string make = "mingw32-make"; + string makeParameters = GetConfigurationOption("makeParameters", ""); + string reactosDirectory = Path.Combine(System.Environment.CurrentDirectory, + "reactos"); + return RunScript(make, + makeParameters + " " + arguments, + reactosDirectory, + GetEnvironmentVarables(), + out standardOutput, + out standardError); + } + + /// <summary> + /// Verify a revision of ReactOS by building it all. + /// </summary> + private static int VerifyFull() + { + string standardOutput; + string standardError; + int exitCode = RunMake("bootcd", + out standardOutput, + out standardError); + if (exitCode != 0) + { + Fail(String.Format("make bootcd failed: (error: {0}) {1}", + standardError, + standardOutput)); + return exitCode; + } + + string reactosDirectory = Path.Combine(System.Environment.CurrentDirectory, + "reactos"); + string isoFilename = Path.Combine(reactosDirectory, + "ReactOS.iso"); + if (!File.Exists(isoFilename)) + Fail("make bootcd produced no ReactOS.iso"); + + return exitCode; + } + + /// <summary> + /// Verify a revision of ReactOS by building parts. + /// </summary> + /// <param name="components">Comma separated list of components to build.</param> + private static int VerifyPartial(string components) + { + string standardOutput; + string standardError; + string componentParameters = """ + components.Replace(",", "" "") + """; + int exitCode = RunMake(componentParameters, + out standardOutput, + out standardError); + if (exitCode != 0) + Fail(String.Format("make failed for targets {0}: (error: {1}) {2}", + componentParameters, + standardError, + standardOutput)); + return exitCode; + } + + /// <summary> + /// Verify a revision of ReactOS. + /// </summary> + /// <param name="args">Arguments from command line.</param> + private static int Verify(string[] args) + { + if (args.Length > 0) + return VerifyPartial(args[0]); + else + return VerifyFull(); + } + + /// <summary> + /// Program entry point. + /// </summary> + /// <param name="args">Arguments from command line.</param> + /// <remarks> + /// If exit code is 0, then the commit was processed successfully. + /// If exit code is 1, then the commit was not processed successfully. + /// </remarks> + public static void Main(string[] args) + { + try + { + System.Environment.ExitCode = Verify(args); + } + catch (Exception ex) + { + string text = String.Format("Exception: {0}", ex); + SendErrorMail("ReactOS Verify Error", text); + System.Environment.ExitCode = 1; + } + } + } +} _____
Added: trunk/cis/ReactOS.Verify/RedirectableProcess.cs --- trunk/cis/ReactOS.Verify/RedirectableProcess.cs 2005-11-23 16:57:00 UTC (rev 19489) +++ trunk/cis/ReactOS.Verify/RedirectableProcess.cs 2005-11-23 17:25:55 UTC (rev 19490) @@ -0,0 +1,145 @@
+/* + * When using the ProcessStartInfo.RedirectStandardXxx properties there is a chance of + * the parent and child process blocking due to a race condition. This class handles the + * problem. + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/h tml/frlrfsystemdiagnosticsprocessstartinfoclassredirectstandardoutputtop ic.asp + */ +using System; +using System.IO; +using System.Threading; +using System.Diagnostics; + +namespace ReactOS.Verify +{ + /// <summary> + /// Process that redirects standard output and standard error streams. + /// </summary> + public class RedirectableProcess + { + /// <summary> + /// Process. + /// </summary> + private Process process; + + /// <summary> + /// Redirected standard error stream. + /// </summary> + private string processError; + + /// <summary> + /// Redirected standard output stream. + /// </summary> + private string processOutput; + + /// <summary> + /// Exit code. + /// </summary> + private int exitCode; + + /// <summary> + /// Redirected standard error stream. + /// </summary> + public string ProcessError + { + get + { + return processError; + } + } + + /// <summary> + /// Redirected standard output stream. + /// </summary> + public string ProcessOutput + { + get + { + return processOutput; + } + } + + /// <summary> + /// Exit code. + /// </summary> + public int ExitCode + { + get + { + return exitCode; + } + } + + /// <summary> + /// Run an excutable and redirect standard error and/or standard output safely. + /// </summary> + public RedirectableProcess(ProcessStartInfo processStartInfo) + { + Run(processStartInfo, null); + } + + /// <summary> + /// Run an excutable and redirect standard error and/or standard output safely. + /// </summary> + public RedirectableProcess(ProcessStartInfo processStartInfo, string input) + { + Run(processStartInfo, input); + } + + private void Run(ProcessStartInfo processStartInfo, string input) + { + process = new Process(); + process.StartInfo = processStartInfo; + process.Start(); + if (processStartInfo.RedirectStandardInput && input != null) + { + process.StandardInput.AutoFlush = true; + process.StandardInput.WriteLine(input); + } + Thread readStandardError = null; + if (processStartInfo.RedirectStandardError) + { + readStandardError = new Thread(new ThreadStart(ReadStandardError)); + readStandardError.Start(); + } + Thread readStandardOutput = null; + if (processStartInfo.RedirectStandardOutput) + { + readStandardOutput = new Thread(new ThreadStart(ReadStandardOutput)); + readStandardOutput.Start(); + } + if (processStartInfo.RedirectStandardError) + { + readStandardError.Join(); + } + if (processStartInfo.RedirectStandardOutput) + { + readStandardOutput.Join(); + } + process.WaitForExit(); + exitCode = process.ExitCode; + process = null; + } + + /// <summary> + /// Read standard error thread entry-point. + /// </summary> + private void ReadStandardError() + { + if (process != null) + { + processError = process.StandardError.ReadToEnd(); + } + } + + /// <summary> + /// Read standard output thread entry-point. + /// </summary> + private void ReadStandardOutput() + { + if (process != null) + { + processOutput = process.StandardOutput.ReadToEnd(); + } + } + } +}