Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
30 ashish 1
/**
2
 * Licensed to the Apache Software Foundation (ASF) under one
3
 * or more contributor license agreements. See the NOTICE file
4
 * distributed with this work for additional information
5
 * regarding copyright ownership. The ASF licenses this file
6
 * to you under the Apache License, Version 2.0 (the
7
 * "License"); you may not use this file except in compliance
8
 * with the License. You may obtain a copy of the License at
9
 *
10
 *   http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing,
13
 * software distributed under the License is distributed on an
14
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
 * KIND, either express or implied. See the License for the
16
 * specific language governing permissions and limitations
17
 * under the License.
18
 *
19
 * Contains some contributions under the Thrift Software License.
20
 * Please see doc/old-thrift-license.txt in the Thrift distribution for
21
 * details.
22
 */
23
 
24
using System;
25
using System.Collections.Generic;
26
using System.Linq;
27
using System.Text;
28
using Microsoft.Build.Framework;
29
using Microsoft.Build.Utilities;
30
using Microsoft.Build.Tasks;
31
using System.IO;
32
using System.Diagnostics;
33
 
34
namespace ThriftMSBuildTask
35
{
36
	/// <summary>
37
	/// MSBuild Task to generate csharp from .thrift files, and compile the code into a library: ThriftImpl.dll
38
	/// </summary>
39
	public class ThriftBuild : Task
40
	{
41
		/// <summary>
42
		/// The full path to the thrift.exe compiler
43
		/// </summary>
44
		[Required]
45
		public ITaskItem ThriftExecutable
46
		{
47
			get;
48
			set;
49
		}
50
 
51
		/// <summary>
52
		/// The full path to a thrift.dll C# library
53
		/// </summary>
54
		[Required]
55
		public ITaskItem ThriftLibrary
56
		{
57
			get;
58
			set;
59
		}
60
 
61
		/// <summary>
62
		/// A direcotry containing .thrift files
63
		/// </summary>
64
		[Required]
65
		public ITaskItem ThriftDefinitionDir
66
		{
67
			get;
68
			set;
69
		}
70
 
71
		/// <summary>
72
		/// The name of the auto-gen and compiled thrift library. It will placed in
73
		/// the same directory as ThriftLibrary
74
		/// </summary>
75
		[Required]
76
		public ITaskItem OutputName
77
		{
78
			get;
79
			set;
80
		}
81
 
82
		/// <summary>
83
		/// The full path to the compiled ThriftLibrary. This allows msbuild tasks to use this
84
		/// output as a variable for use elsewhere.
85
		/// </summary>
86
		[Output]
87
		public ITaskItem ThriftImplementation
88
		{
89
			get { return thriftImpl; }
90
		}
91
 
92
		private ITaskItem thriftImpl;
93
		private const string lastCompilationName = "LAST_COMP_TIMESTAMP";
94
 
95
		//use the Message Build Task to write something to build log
96
		private void LogMessage(string text, MessageImportance importance)
97
		{
98
			Message m = new Message();
99
			m.Text = text;
100
			m.Importance = importance.ToString();
101
			m.BuildEngine = this.BuildEngine;
102
			m.Execute();
103
		}
104
 
105
		//recursively find .cs files in srcDir, paths should initially be non-null and empty
106
		private void FindSourcesHelper(string srcDir, List<string> paths)
107
		{
108
			string[] files = Directory.GetFiles(srcDir, "*.cs");
109
			foreach (string f in files)
110
			{
111
				paths.Add(f);
112
			}
113
			string[] dirs = Directory.GetDirectories(srcDir);
114
			foreach (string dir in dirs)
115
			{
116
				FindSourcesHelper(dir, paths);
117
			}
118
		}
119
 
120
		/// <summary>
121
		/// Quote paths with spaces
122
		/// </summary>
123
		private string SafePath(string path)
124
		{
125
			if (path.Contains(' ') && !path.StartsWith("\""))
126
			{
127
				return "\"" + path + "\"";
128
			}
129
			return path;
130
		}
131
 
132
		private ITaskItem[] FindSources(string srcDir)
133
		{
134
			List<string> files = new List<string>();
135
			FindSourcesHelper(srcDir, files);
136
			ITaskItem[] items = new ITaskItem[files.Count];
137
			for (int i = 0; i < items.Length; i++)
138
			{
139
				items[i] = new TaskItem(files[i]);
140
			}
141
			return items;
142
		}
143
 
144
		private string LastWriteTime(string defDir)
145
		{
146
			string[] files = Directory.GetFiles(defDir, "*.thrift");
147
			DateTime d = (new DirectoryInfo(defDir)).LastWriteTime;
148
			foreach(string file in files)
149
			{
150
				FileInfo f = new FileInfo(file);
151
				DateTime curr = f.LastWriteTime;
152
				if (DateTime.Compare(curr, d) > 0)
153
				{
154
					d = curr;
155
				}
156
			}
157
			return d.ToFileTimeUtc().ToString();
158
		}
159
 
160
		public override bool Execute()
161
		{
162
			string defDir = SafePath(ThriftDefinitionDir.ItemSpec);
163
			//look for last compilation timestamp
164
			string lastBuildPath = Path.Combine(defDir, lastCompilationName);
165
			DirectoryInfo defDirInfo = new DirectoryInfo(defDir);
166
			string lastWrite = LastWriteTime(defDir);
167
			if (File.Exists(lastBuildPath))
168
			{
169
				string lastComp = File.ReadAllText(lastBuildPath);
170
				//don't recompile if the thrift library has been updated since lastComp
171
				FileInfo f = new FileInfo(ThriftLibrary.ItemSpec);
172
				string thriftLibTime = f.LastWriteTimeUtc.ToFileTimeUtc().ToString();
173
				if (lastComp.CompareTo(thriftLibTime) < 0)
174
				{
175
					//new thrift library, do a compile
176
					lastWrite = thriftLibTime;
177
				}
178
				else if (lastComp == lastWrite || (lastComp == thriftLibTime && lastComp.CompareTo(lastWrite) > 0))
179
				{
180
					//the .thrift dir hasn't been written to since last compilation, don't need to do anything
181
					LogMessage("ThriftImpl up-to-date", MessageImportance.High);
182
					return true;
183
				}
184
			}
185
 
186
			//find the directory of the thriftlibrary (that's where output will go)
187
			FileInfo thriftLibInfo = new FileInfo(SafePath(ThriftLibrary.ItemSpec));
188
			string thriftDir = thriftLibInfo.Directory.FullName;
189
 
190
			string genDir = Path.Combine(thriftDir, "gen-csharp");
191
			if (Directory.Exists(genDir))
192
			{
193
				try
194
				{
195
					Directory.Delete(genDir, true);
196
				}
197
				catch { /*eh i tried, just over-write now*/}
198
			}
199
 
200
			//run the thrift executable to generate C#
201
			foreach (string thriftFile in Directory.GetFiles(defDir, "*.thrift"))
202
			{
203
				LogMessage("Generating code for: " + thriftFile, MessageImportance.Normal);
204
				Process p = new Process();
205
				p.StartInfo.FileName = SafePath(ThriftExecutable.ItemSpec);
206
				p.StartInfo.Arguments = "--gen csharp -o " + SafePath(thriftDir) + " -r " + thriftFile;
207
				p.StartInfo.UseShellExecute = false;
208
				p.StartInfo.CreateNoWindow = true;
209
				p.StartInfo.RedirectStandardOutput = false;
210
				p.Start();
211
				p.WaitForExit();
212
				if (p.ExitCode != 0)
213
				{
214
					LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High);
215
					return false;
216
				}
217
				if (p.ExitCode != 0)
218
				{
219
					LogMessage("thrift.exe failed to compile " + thriftFile, MessageImportance.High);
220
					return false;
221
				}
222
			}
223
 
224
			Csc csc = new Csc();
225
			csc.TargetType = "library";
226
			csc.References = new ITaskItem[] { new TaskItem(ThriftLibrary.ItemSpec) };
227
			csc.EmitDebugInformation = true;
228
			string outputPath = Path.Combine(thriftDir, OutputName.ItemSpec);
229
			csc.OutputAssembly = new TaskItem(outputPath);
230
			csc.Sources = FindSources(Path.Combine(thriftDir, "gen-csharp"));
231
			csc.BuildEngine = this.BuildEngine;
232
			LogMessage("Compiling generated cs...", MessageImportance.Normal);
233
			if (!csc.Execute())
234
			{
235
				return false;
236
			}
237
 
238
			//write file to defDir to indicate a build was successfully completed
239
			File.WriteAllText(lastBuildPath, lastWrite);
240
 
241
			thriftImpl = new TaskItem(outputPath);
242
 
243
			return true;
244
		}
245
	}
246
}