[io-layer] add URLs for some ximian bug numbers in sockets.cs
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / TargetBatchingImpl.cs
1 //
2 // TargetBatchingImpl.cs: Class that implements Target Batching Algorithm from the wiki.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //   Ankit Jain (jankit@novell.com)
7 //
8 // (C) 2005 Marek Sieradzki
9 // Copyright 2008 Novell, Inc (http://www.novell.com)
10 // Copyright 2009 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
31 using System;
32 using System.IO;
33 using System.Collections.Generic;
34 using System.Xml;
35
36 using Microsoft.Build.Framework;
37
38 namespace Microsoft.Build.BuildEngine {
39
40         internal class TargetBatchingImpl : BatchingImplBase
41         {
42                 string          inputs;
43                 string          outputs;
44                 string          name;
45
46                 public TargetBatchingImpl (Project project, XmlElement targetElement)
47                         : base (project)
48                 {
49                         if (targetElement == null)
50                                 throw new ArgumentNullException ("targetElement");
51
52                         inputs = targetElement.GetAttribute ("Inputs");
53                         outputs = targetElement.GetAttribute ("Outputs");
54                         name = targetElement.GetAttribute ("Name");
55                 }
56
57                 public bool Build (Target target, out bool executeOnErrors)
58                 {
59                         executeOnErrors = false;
60                         try {
61                                 string reason;
62                                 if (!BuildTargetNeeded (out reason)) {
63                                         LogTargetStarted (target);
64                                         LogTargetSkipped (target, reason);
65                                         LogTargetFinished (target, true);
66                                         return true;
67                                 }
68
69                                 if (!String.IsNullOrEmpty (reason))
70                                         target.Engine.LogMessage (MessageImportance.Low, reason);
71
72                                 Init ();
73
74                                 ParseTargetAttributes (target);
75                                 BatchAndPrepareBuckets ();
76                                 return Run (target, out executeOnErrors);
77                         } finally {
78                                 consumedItemsByName = null;
79                                 consumedMetadataReferences = null;
80                                 consumedQMetadataReferences = null;
81                                 consumedUQMetadataReferences = null;
82                                 batchedItemsByName = null;
83                                 commonItemsByName = null;
84                         }
85                 }
86
87                 bool Run (Target target, out bool executeOnErrors)
88                 {
89                         executeOnErrors = false;
90                         if (buckets.Count > 0) {
91                                 foreach (Dictionary<string, BuildItemGroup> bucket in buckets)
92                                         if (!RunTargetWithBucket (bucket, target, out executeOnErrors))
93                                                 return false;
94
95                                 return true;
96                         } else {
97                                 return RunTargetWithBucket (null, target, out executeOnErrors);
98                         }
99                 }
100
101                 bool RunTargetWithBucket (Dictionary<string, BuildItemGroup> bucket, Target target, out bool executeOnErrors)
102                 {
103                         bool target_result = true;
104                         executeOnErrors = false;
105
106                         LogTargetStarted (target);
107                         if (bucket != null)
108                                 project.PushBatch (bucket, commonItemsByName);
109                         try {
110                                 string reason;
111                                 if (!BuildTargetNeeded (out reason)) {
112                                         LogTargetSkipped (target, reason);
113                                         return true;
114                                 }
115
116                                 if (!String.IsNullOrEmpty (reason))
117                                         target.Engine.LogMessage (MessageImportance.Low, reason);
118
119                                 for (int i = 0; i < target.BuildTasks.Count; i ++) {
120                                         //FIXME: parsing attributes repeatedly
121                                         BuildTask bt = target.BuildTasks [i];
122
123                                         TaskBatchingImpl batchingImpl = new TaskBatchingImpl (project);
124                                         bool task_result = batchingImpl.Build (bt, out executeOnErrors);
125                                         if (task_result)
126                                                 continue;
127
128                                         // task failed, if ContinueOnError,
129                                         // ignore failed state for target
130                                         target_result = bt.ContinueOnError;
131
132                                         if (!bt.ContinueOnError) {
133                                                 executeOnErrors = true;
134                                                 return false;
135                                         }
136
137                                 }
138                         } finally {
139                                 if (bucket != null)
140                                         project.PopBatch ();
141                                 LogTargetFinished (target, target_result);
142                         }
143
144                         return target_result;
145                 }
146
147                 // Parse target's Input and Output attributes to get list of referenced
148                 // metadata and items to determine batching
149                 void ParseTargetAttributes (Target target)
150                 {
151                         if (!String.IsNullOrEmpty (inputs))
152                                 ParseAttribute (inputs);
153
154                         if (!String.IsNullOrEmpty (outputs))
155                                 ParseAttribute (outputs);
156                 }
157
158                 bool BuildTargetNeeded (out string reason)
159                 {
160                         reason = String.Empty;
161                         ITaskItem [] inputFiles;
162                         ITaskItem [] outputFiles;
163                         DateTime youngestInput, oldestOutput;
164
165                         if (String.IsNullOrEmpty (inputs.Trim ()))
166                                 return true;
167
168                         if (String.IsNullOrEmpty (outputs.Trim ())) {
169                                 project.ParentEngine.LogError ("Target {0} has inputs but no outputs specified.", name);
170                                 return true;
171                         }
172
173                         Expression e = new Expression ();
174                         e.Parse (inputs, ParseOptions.AllowItemsMetadataAndSplit);
175                         inputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
176
177                         e = new Expression ();
178                         e.Parse (outputs, ParseOptions.AllowItemsMetadataAndSplit);
179                         outputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
180
181                         if (outputFiles == null || outputFiles.Length == 0) {
182                                 reason = String.Format ("No output files were specified for target {0}, skipping.", name);
183                                 return false;
184                         }
185
186                         if (inputFiles == null || inputFiles.Length == 0) {
187                                 reason = String.Format ("No input files were specified for target {0}, skipping.", name);
188                                 return false;
189                         }
190
191                         youngestInput = DateTime.MinValue;
192                         oldestOutput = DateTime.MaxValue;
193
194                         string youngestInputFile, oldestOutputFile;
195                         youngestInputFile = oldestOutputFile = String.Empty;
196                         foreach (ITaskItem item in inputFiles) {
197                                 string file = item.ItemSpec.Trim ();
198                                 if (file.Length == 0)
199                                         continue;
200
201                                 if (!File.Exists (file)) {
202                                         reason = String.Format ("Target {0} needs to be built as input file '{1}' does not exist.", name, file);
203                                         return true;
204                                 }
205
206                                 DateTime lastWriteTime = File.GetLastWriteTime (file);
207                                 if (lastWriteTime > youngestInput) {
208                                         youngestInput = lastWriteTime;
209                                         youngestInputFile = file;
210                                 }
211                         }
212
213                         foreach (ITaskItem item in outputFiles) {
214                                 string file = item.ItemSpec.Trim ();
215                                 if (file.Length == 0)
216                                         continue;
217
218                                 if (!File.Exists (file)) {
219                                         reason = String.Format ("Target {0} needs to be built as output file '{1}' does not exist.", name, file);
220                                         return true;
221                                 }
222
223                                 DateTime lastWriteTime = File.GetLastWriteTime (file);
224                                 if (lastWriteTime < oldestOutput) {
225                                         oldestOutput = lastWriteTime;
226                                         oldestOutputFile = file;
227                                 }
228                         }
229
230                         if (youngestInput > oldestOutput) {
231                                 reason = String.Format ("Target {0} needs to be built as input file '{1}' is newer than output file '{2}'",
232                                                 name, youngestInputFile, oldestOutputFile);
233                                 return true;
234                         }
235
236                         return false;
237                 }
238
239                 void LogTargetSkipped (Target target, string reason)
240                 {
241                         BuildMessageEventArgs bmea;
242                         bmea = new BuildMessageEventArgs (String.IsNullOrEmpty (reason)
243                                                                 ? String.Format ("Skipping target \"{0}\" because its outputs are up-to-date.", target.Name)
244                                                                 : reason,
245                                                         null, "MSBuild", MessageImportance.Normal);
246                         target.Engine.EventSource.FireMessageRaised (this, bmea);
247                 }
248
249                 void LogTargetStarted (Target target)
250                 {
251                         TargetStartedEventArgs tsea;
252                         string projectFile = project.FullFileName;
253                         tsea = new TargetStartedEventArgs (String.Format ("Target {0} started.", target.Name), null,
254                                         target.Name, projectFile, target.TargetFile);
255                         target.Engine.EventSource.FireTargetStarted (this, tsea);
256                 }
257
258                 void LogTargetFinished (Target target, bool succeeded)
259                 {
260                         TargetFinishedEventArgs tfea;
261                         string projectFile = project.FullFileName;
262                         tfea = new TargetFinishedEventArgs (String.Format ("Target {0} finished.", target.Name), null,
263                                         target.Name, projectFile, target.TargetFile, succeeded);
264                         target.Engine.EventSource.FireTargetFinished (this, tfea);
265                 }
266
267         }
268 }