blob: 9cf5f41783836bb50b53293728d5500263782217 [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();
178 }
179
180 Csc csc = new Csc();
181 csc.TargetType = "library";
182 csc.References = new ITaskItem[] { new TaskItem(ThriftLibrary.ItemSpec) };
183 csc.EmitDebugInformation = true;
184 string outputPath = Path.Combine(thriftDir, OutputName.ItemSpec);
185 csc.OutputAssembly = new TaskItem(outputPath);
186 csc.Sources = FindSources(Path.Combine(thriftDir, "gen-csharp"));
187 csc.BuildEngine = this.BuildEngine;
188 LogMessage("Compiling generated cs...", MessageImportance.Normal);
189 if (!csc.Execute())
190 {
191 return false;
192 }
193
194 //write file to defDir to indicate a build was successfully completed
195 File.WriteAllText(lastBuildPath, lastWrite);
196
197 thriftImpl = new TaskItem(outputPath);
198
199 return true;
200 }
201 }
202}