blob: a2bb641565e8bb5faab938687aacb742b770c199 [file] [log] [blame]
David Reiss7f42bcf2008-01-11 20:59:12 +00001using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using Microsoft.Build.Framework;
6using Microsoft.Build.Utilities;
7using Microsoft.Build.Tasks;
8using System.IO;
9using System.Diagnostics;
10
11namespace ThriftMSBuildTask
12{
13 /// <summary>
14 /// MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll
15 /// </summary>
16 public class ThriftBuild : Task
17 {
18 /// <summary>
19 /// The full path to the thrift.exe compiler
20 /// </summary>
21 [Required]
22 public ITaskItem ThriftExecutable
23 {
24 get;
25 set;
26 }
27
28 /// <summary>
29 /// The full path to a thrift.dll C# library
30 /// </summary>
31 [Required]
32 public ITaskItem ThriftLibrary
33 {
34 get;
35 set;
36 }
37
38 /// <summary>
39 /// A direcotry containing .thrift files
40 /// </summary>
41 [Required]
42 public ITaskItem ThriftDefinitionDir
43 {
44 get;
45 set;
46 }
47
48 /// <summary>
49 /// The name of the auto-gen and compiled thrift library. It will placed in
50 /// the same directory as ThriftLibrary
51 /// </summary>
52 [Required]
53 public ITaskItem OutputName
54 {
55 get;
56 set;
57 }
58
59 /// <summary>
60 /// The full path to the compiled ThriftLibrary. This allows msbuild tasks to use this
61 /// output as a variable for use elsewhere.
62 /// </summary>
63 [Output]
64 public ITaskItem ThriftImplementation
65 {
66 get { return thriftImpl; }
67 }
68
69 private ITaskItem thriftImpl;
70 private const string lastCompilationName = "LAST_COMP_TIMESTAMP";
71
72 //use the Message Build Task to write something to build log
73 private void LogMessage(string text, MessageImportance importance)
74 {
75 Message m = new Message();
76 m.Text = text;
77 m.Importance = importance.ToString();
78 m.BuildEngine = this.BuildEngine;
79 m.Execute();
80 }
81
82 //recursively find .cs files in srcDir, paths should initially be non-null and empty
83 private void FindSourcesHelper(string srcDir, List<string> paths)
84 {
85 string[] files = Directory.GetFiles(srcDir, "*.cs");
86 foreach (string f in files)
87 {
88 paths.Add(f);
89 }
90 string[] dirs = Directory.GetDirectories(srcDir);
91 foreach (string dir in dirs)
92 {
93 FindSourcesHelper(dir, paths);
94 }
95 }
96
97 /// <summary>
98 /// Quote paths with spaces
99 /// </summary>
100 private string SafePath(string path)
101 {
102 if (path.Contains(' ') && !path.StartsWith("\""))
103 {
104 return "\"" + path + "\"";
105 }
106 return path;
107 }
108
109 private ITaskItem[] FindSources(string srcDir)
110 {
111 List<string> files = new List<string>();
112 FindSourcesHelper(srcDir, files);
113 ITaskItem[] items = new ITaskItem[files.Count];
114 for (int i = 0; i < items.Length; i++)
115 {
116 items[i] = new TaskItem(files[i]);
117 }
118 return items;
119 }
120
121 private string LastWriteTime(string defDir)
122 {
123 string[] files = Directory.GetFiles(defDir, "*.thrift");
124 DateTime d = (new DirectoryInfo(defDir)).LastWriteTime;
125 foreach(string file in files)
126 {
127 FileInfo f = new FileInfo(file);
128 DateTime curr = f.LastWriteTime;
129 if (DateTime.Compare(curr, d) > 0)
130 {
131 d = curr;
132 }
133 }
134 return d.ToFileTimeUtc().ToString();
135 }
136
137 public override bool Execute()
138 {
139 string defDir = SafePath(ThriftDefinitionDir.ItemSpec);
140 //look for last compilation timestamp
141 string lastBuildPath = Path.Combine(defDir, lastCompilationName);
142 DirectoryInfo defDirInfo = new DirectoryInfo(defDir);
143 string lastWrite = LastWriteTime(defDir);
144 if (File.Exists(lastBuildPath))
145 {
146 string lastComp = File.ReadAllText(lastBuildPath);
147
148 if (lastComp == lastWrite)
149 {
150 //the .thrift dir hasn't been written to since last compilation, don't need to do anything
151 LogMessage("ThriftImpl up-to-date", MessageImportance.Normal);
152 return true;
153 }
154 }
155
156 //find the directory of the thriftlibrary (that's where output will go)
157 FileInfo thriftLibInfo = new FileInfo(SafePath(ThriftLibrary.ItemSpec));
158 string thriftDir = thriftLibInfo.Directory.FullName;
159
160 string genDir = Path.Combine(thriftDir, "gen-csharp");
161 if (Directory.Exists(genDir))
162 {
163 Directory.Delete(genDir, true);
164 }
165
166 //run the thrift executable to generate C#
167 foreach (string thriftFile in Directory.GetFiles(defDir, "*.thrift"))
168 {
169 LogMessage("Generating code for: " + thriftFile, MessageImportance.Normal);
170 Process p = new Process();
171 p.StartInfo.FileName = SafePath(ThriftExecutable.ItemSpec);
172 p.StartInfo.Arguments = "-csharp -o " + SafePath(thriftDir) + " -r " + thriftFile;
173 p.StartInfo.UseShellExecute = false;
174 p.StartInfo.CreateNoWindow = true;
175 p.StartInfo.RedirectStandardOutput = false;
176 p.Start();
177 p.WaitForExit();
Mark Slee1e150182008-01-22 04:04:30 +0000178 if (p.ExitCode != 0)
179 {
180 LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High);
181 return false;
182 }
David Reiss7f42bcf2008-01-11 20:59:12 +0000183 }
184
185 Csc csc = new Csc();
186 csc.TargetType = "library";
187 csc.References = new ITaskItem[] { new TaskItem(ThriftLibrary.ItemSpec) };
188 csc.EmitDebugInformation = true;
189 string outputPath = Path.Combine(thriftDir, OutputName.ItemSpec);
190 csc.OutputAssembly = new TaskItem(outputPath);
191 csc.Sources = FindSources(Path.Combine(thriftDir, "gen-csharp"));
192 csc.BuildEngine = this.BuildEngine;
193 LogMessage("Compiling generated cs...", MessageImportance.Normal);
194 if (!csc.Execute())
195 {
196 return false;
197 }
198
199 //write file to defDir to indicate a build was successfully completed
200 File.WriteAllText(lastBuildPath, lastWrite);
201
202 thriftImpl = new TaskItem(outputPath);
203
204 return true;
205 }
206 }
207}