Rev 30 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements. See the NOTICE file* distributed with this work for additional information* regarding copyright ownership. The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License. You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied. See the License for the* specific language governing permissions and limitations* under the License.** Contains some contributions under the Thrift Software License.* Please see doc/old-thrift-license.txt in the Thrift distribution for* details.*/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 logprivate 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 emptyprivate 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 timestampstring 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 lastCompFileInfo f = new FileInfo(ThriftLibrary.ItemSpec);string thriftLibTime = f.LastWriteTimeUtc.ToFileTimeUtc().ToString();if (lastComp.CompareTo(thriftLibTime) < 0){//new thrift library, do a compilelastWrite = 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 anythingLogMessage("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 completedFile.WriteAllText(lastBuildPath, lastWrite);thriftImpl = new TaskItem(outputPath);return true;}}}