| using System; | 
 | using System.Collections.Generic; | 
 | using System.Linq; | 
 | using System.Text; | 
 | using Microsoft.Build.Framework; | 
 | using Microsoft.Build.Utilities; | 
 | using Microsoft.Build.Tasks; | 
 | using System.IO; | 
 | using System.Diagnostics; | 
 |  | 
 | namespace ThriftMSBuildTask | 
 | { | 
 | 	/// <summary> | 
 | 	/// MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll | 
 | 	/// </summary> | 
 | 	public class ThriftBuild : Task | 
 | 	{ | 
 | 		/// <summary> | 
 | 		/// The full path to the thrift.exe compiler | 
 | 		/// </summary> | 
 | 		[Required] | 
 | 		public ITaskItem ThriftExecutable | 
 | 		{ | 
 | 			get; | 
 | 			set; | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// The full path to a thrift.dll C# library | 
 | 		/// </summary> | 
 | 		[Required] | 
 | 		public ITaskItem ThriftLibrary | 
 | 		{ | 
 | 			get; | 
 | 			set; | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// A direcotry containing .thrift files | 
 | 		/// </summary> | 
 | 		[Required] | 
 | 		public ITaskItem ThriftDefinitionDir | 
 | 		{ | 
 | 			get; | 
 | 			set; | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// The name of the auto-gen and compiled thrift library. It will placed in | 
 | 		/// the same directory as ThriftLibrary | 
 | 		/// </summary> | 
 | 		[Required] | 
 | 		public ITaskItem OutputName | 
 | 		{ | 
 | 			get; | 
 | 			set; | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// The full path to the compiled ThriftLibrary. This allows msbuild tasks to use this | 
 | 		/// output as a variable for use elsewhere. | 
 | 		/// </summary> | 
 | 		[Output] | 
 | 		public ITaskItem ThriftImplementation | 
 | 		{ | 
 | 			get { return thriftImpl; } | 
 | 		} | 
 |  | 
 | 		private ITaskItem thriftImpl; | 
 | 		private const string lastCompilationName = "LAST_COMP_TIMESTAMP"; | 
 |  | 
 | 		//use the Message Build Task to write something to build log | 
 | 		private void LogMessage(string text, MessageImportance importance) | 
 | 		{ | 
 | 			Message m = new Message(); | 
 | 			m.Text = text; | 
 | 			m.Importance = importance.ToString(); | 
 | 			m.BuildEngine = this.BuildEngine; | 
 | 			m.Execute(); | 
 | 		} | 
 |  | 
 | 		//recursively find .cs files in srcDir, paths should initially be non-null and empty | 
 | 		private void FindSourcesHelper(string srcDir, List<string> paths) | 
 | 		{ | 
 | 			string[] files = Directory.GetFiles(srcDir, "*.cs"); | 
 | 			foreach (string f in files) | 
 | 			{ | 
 | 				paths.Add(f); | 
 | 			} | 
 | 			string[] dirs = Directory.GetDirectories(srcDir); | 
 | 			foreach (string dir in dirs) | 
 | 			{ | 
 | 				FindSourcesHelper(dir, paths); | 
 | 			} | 
 | 		} | 
 |  | 
 | 		/// <summary> | 
 | 		/// Quote paths with spaces | 
 | 		/// </summary> | 
 | 		private string SafePath(string path) | 
 | 		{ | 
 | 			if (path.Contains(' ') && !path.StartsWith("\"")) | 
 | 			{ | 
 | 				return "\"" + path + "\""; | 
 | 			} | 
 | 			return path; | 
 | 		} | 
 |  | 
 | 		private ITaskItem[] FindSources(string srcDir) | 
 | 		{ | 
 | 			List<string> files = new List<string>(); | 
 | 			FindSourcesHelper(srcDir, files); | 
 | 			ITaskItem[] items = new ITaskItem[files.Count]; | 
 | 			for (int i = 0; i < items.Length; i++) | 
 | 			{ | 
 | 				items[i] = new TaskItem(files[i]); | 
 | 			} | 
 | 			return items; | 
 | 		} | 
 |  | 
 | 		private string LastWriteTime(string defDir) | 
 | 		{ | 
 | 			string[] files = Directory.GetFiles(defDir, "*.thrift"); | 
 | 			DateTime d = (new DirectoryInfo(defDir)).LastWriteTime; | 
 | 			foreach(string file in files) | 
 | 			{ | 
 | 				FileInfo f = new FileInfo(file); | 
 | 				DateTime curr = f.LastWriteTime; | 
 | 				if (DateTime.Compare(curr, d) > 0) | 
 | 				{ | 
 | 					d = curr; | 
 | 				} | 
 | 			} | 
 | 			return d.ToFileTimeUtc().ToString(); | 
 | 		} | 
 |  | 
 | 		public override bool Execute() | 
 | 		{ | 
 | 			string defDir = SafePath(ThriftDefinitionDir.ItemSpec); | 
 | 			//look for last compilation timestamp | 
 | 			string lastBuildPath = Path.Combine(defDir, lastCompilationName); | 
 | 			DirectoryInfo defDirInfo = new DirectoryInfo(defDir); | 
 | 			string lastWrite = LastWriteTime(defDir); | 
 | 			if (File.Exists(lastBuildPath)) | 
 | 			{ | 
 | 				string lastComp = File.ReadAllText(lastBuildPath); | 
 | 				//don't recompile if the thrift library has been updated since lastComp | 
 | 				FileInfo f = new FileInfo(ThriftLibrary.ItemSpec); | 
 | 				string thriftLibTime = f.LastWriteTimeUtc.ToFileTimeUtc().ToString(); | 
 | 				if (lastComp.CompareTo(thriftLibTime) < 0) | 
 | 				{ | 
 | 					//new thrift library, do a compile | 
 | 					lastWrite = thriftLibTime; | 
 | 				} | 
 | 				else if (lastComp == lastWrite || (lastComp == thriftLibTime && lastComp.CompareTo(lastWrite) > 0)) | 
 | 				{ | 
 | 					//the .thrift dir hasn't been written to since last compilation, don't need to do anything | 
 | 					LogMessage("ThriftImpl up-to-date", MessageImportance.High); | 
 | 					return true; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			//find the directory of the thriftlibrary (that's where output will go) | 
 | 			FileInfo thriftLibInfo = new FileInfo(SafePath(ThriftLibrary.ItemSpec)); | 
 | 			string thriftDir = thriftLibInfo.Directory.FullName; | 
 |  | 
 | 			string genDir = Path.Combine(thriftDir, "gen-csharp"); | 
 | 			if (Directory.Exists(genDir)) | 
 | 			{ | 
 | 				try | 
 | 				{ | 
 | 					Directory.Delete(genDir, true); | 
 | 				} | 
 | 				catch { /*eh i tried, just over-write now*/} | 
 | 			} | 
 |  | 
 | 			//run the thrift executable to generate C# | 
 | 			foreach (string thriftFile in Directory.GetFiles(defDir, "*.thrift")) | 
 | 			{ | 
 | 				LogMessage("Generating code for: " + thriftFile, MessageImportance.Normal); | 
 | 				Process p = new Process(); | 
 | 				p.StartInfo.FileName = SafePath(ThriftExecutable.ItemSpec); | 
 | 				p.StartInfo.Arguments = "--gen csharp -o " + SafePath(thriftDir) + " -r " + thriftFile; | 
 | 				p.StartInfo.UseShellExecute = false; | 
 | 				p.StartInfo.CreateNoWindow = true; | 
 | 				p.StartInfo.RedirectStandardOutput = false; | 
 | 				p.Start(); | 
 | 				p.WaitForExit(); | 
 | 				if (p.ExitCode != 0) | 
 | 				{ | 
 | 					LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High); | 
 | 					return false; | 
 | 				} | 
 | 				if (p.ExitCode != 0) | 
 | 				{ | 
 | 					LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High); | 
 | 					return false; | 
 | 				} | 
 | 			} | 
 |  | 
 | 			Csc csc = new Csc(); | 
 | 			csc.TargetType = "library"; | 
 | 			csc.References = new ITaskItem[] { new TaskItem(ThriftLibrary.ItemSpec) }; | 
 | 			csc.EmitDebugInformation = true; | 
 | 			string outputPath = Path.Combine(thriftDir, OutputName.ItemSpec); | 
 | 			csc.OutputAssembly = new TaskItem(outputPath); | 
 | 			csc.Sources = FindSources(Path.Combine(thriftDir, "gen-csharp")); | 
 | 			csc.BuildEngine = this.BuildEngine; | 
 | 			LogMessage("Compiling generated cs...", MessageImportance.Normal); | 
 | 			if (!csc.Execute()) | 
 | 			{ | 
 | 				return false; | 
 | 			} | 
 |  | 
 | 			//write file to defDir to indicate a build was successfully completed | 
 | 			File.WriteAllText(lastBuildPath, lastWrite); | 
 |  | 
 | 			thriftImpl = new TaskItem(outputPath); | 
 |  | 
 | 			return true; | 
 | 		} | 
 | 	} | 
 | } |