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/html/frlrfsystemdiagnosticsprocessstartinfoclassredirectstandardoutputtopic.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();
+			}
+		}
+	}
+}