Update mcs/class/Commons.Xml.Relaxng/Commons.Xml.Relaxng/RelaxngPattern.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 #if NET_2_0
32
33 using System;
34 using System.IO;
35 using System.Collections.Generic;
36 using System.Xml;
37
38 using Microsoft.Build.Framework;
39
40 namespace Microsoft.Build.BuildEngine {
41
42         internal class TargetBatchingImpl : BatchingImplBase
43         {
44                 string          inputs;
45                 string          outputs;
46                 string          name;
47
48                 public TargetBatchingImpl (Project project, XmlElement targetElement)
49                         : base (project)
50                 {
51                         if (targetElement == null)
52                                 throw new ArgumentNullException ("targetElement");
53
54                         inputs = targetElement.GetAttribute ("Inputs");
55                         outputs = targetElement.GetAttribute ("Outputs");
56                         name = targetElement.GetAttribute ("Name");
57                 }
58
59                 public bool Build (Target target, out bool executeOnErrors)
60                 {
61                         executeOnErrors = false;
62                         try {
63                                 string reason;
64                                 if (!BuildTargetNeeded (out reason)) {
65                                         LogTargetStarted (target);
66                                         LogTargetSkipped (target, reason);
67                                         LogTargetFinished (target, true);
68                                         return true;
69                                 }
70
71                                 if (!String.IsNullOrEmpty (reason))
72                                         target.Engine.LogMessage (MessageImportance.Low, reason);
73
74                                 Init ();
75
76                                 ParseTargetAttributes (target);
77                                 BatchAndPrepareBuckets ();
78                                 return Run (target, out executeOnErrors);
79                         } finally {
80                                 consumedItemsByName = null;
81                                 consumedMetadataReferences = null;
82                                 consumedQMetadataReferences = null;
83                                 consumedUQMetadataReferences = null;
84                                 batchedItemsByName = null;
85                                 commonItemsByName = null;
86                         }
87                 }
88
89                 bool Run (Target target, out bool executeOnErrors)
90                 {
91                         executeOnErrors = false;
92                         if (buckets.Count > 0) {
93                                 foreach (Dictionary<string, BuildItemGroup> bucket in buckets)
94                                         if (!RunTargetWithBucket (bucket, target, out executeOnErrors))
95                                                 return false;
96
97                                 return true;
98                         } else {
99                                 return RunTargetWithBucket (null, target, out executeOnErrors);
100                         }
101                 }
102
103                 bool RunTargetWithBucket (Dictionary<string, BuildItemGroup> bucket, Target target, out bool executeOnErrors)
104                 {
105                         bool target_result = true;
106                         executeOnErrors = false;
107
108                         LogTargetStarted (target);
109                         if (bucket != null)
110                                 project.PushBatch (bucket, commonItemsByName);
111                         try {
112                                 string reason;
113                                 if (!BuildTargetNeeded (out reason)) {
114                                         LogTargetSkipped (target, reason);
115                                         return true;
116                                 }
117
118                                 if (!String.IsNullOrEmpty (reason))
119                                         target.Engine.LogMessage (MessageImportance.Low, reason);
120
121                                 for (int i = 0; i < target.BuildTasks.Count; i ++) {
122                                         //FIXME: parsing attributes repeatedly
123                                         BuildTask bt = target.BuildTasks [i];
124
125                                         TaskBatchingImpl batchingImpl = new TaskBatchingImpl (project);
126                                         bool task_result = batchingImpl.Build (bt, out executeOnErrors);
127                                         if (task_result)
128                                                 continue;
129
130                                         // task failed, if ContinueOnError,
131                                         // ignore failed state for target
132                                         target_result = bt.ContinueOnError;
133
134                                         if (!bt.ContinueOnError) {
135                                                 executeOnErrors = true;
136                                                 return false;
137                                         }
138
139                                 }
140                         } finally {
141                                 if (bucket != null)
142                                         project.PopBatch ();
143                                 LogTargetFinished (target, target_result);
144                         }
145
146                         return target_result;
147                 }
148
149                 // Parse target's Input and Output attributes to get list of referenced
150                 // metadata and items to determine batching
151                 void ParseTargetAttributes (Target target)
152                 {
153                         if (!String.IsNullOrEmpty (inputs))
154                                 ParseAttribute (inputs);
155
156                         if (!String.IsNullOrEmpty (outputs))
157                                 ParseAttribute (outputs);
158                 }
159
160                 bool BuildTargetNeeded (out string reason)
161                 {
162                         reason = String.Empty;
163                         ITaskItem [] inputFiles;
164                         ITaskItem [] outputFiles;
165                         DateTime youngestInput, oldestOutput;
166
167                         if (String.IsNullOrEmpty (inputs.Trim ()))
168                                 return true;
169
170                         if (String.IsNullOrEmpty (outputs.Trim ())) {
171                                 project.ParentEngine.LogError ("Target {0} has inputs but no outputs specified.", name);
172                                 return true;
173                         }
174
175                         Expression e = new Expression ();
176                         e.Parse (inputs, ParseOptions.AllowItemsMetadataAndSplit);
177                         inputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
178
179                         e = new Expression ();
180                         e.Parse (outputs, ParseOptions.AllowItemsMetadataAndSplit);
181                         outputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]), ExpressionOptions.ExpandItemRefs);
182
183                         if (outputFiles == null || outputFiles.Length == 0) {
184                                 reason = String.Format ("No output files were specified for target {0}, skipping.", name);
185                                 return false;
186                         }
187
188                         if (inputFiles == null || inputFiles.Length == 0) {
189                                 reason = String.Format ("No input files were specified for target {0}, skipping.", name);
190                                 return false;
191                         }
192
193                         youngestInput = DateTime.MinValue;
194                         oldestOutput = DateTime.MaxValue;
195
196                         string youngestInputFile, oldestOutputFile;
197                         youngestInputFile = oldestOutputFile = String.Empty;
198                         foreach (ITaskItem item in inputFiles) {
199                                 string file = item.ItemSpec.Trim ();
200                                 if (file.Length == 0)
201                                         continue;
202
203                                 if (!File.Exists (file)) {
204                                         reason = String.Format ("Target {0} needs to be built as input file '{1}' does not exist.", name, file);
205                                         return true;
206                                 }
207
208                                 DateTime lastWriteTime = File.GetLastWriteTime (file);
209                                 if (lastWriteTime > youngestInput) {
210                                         youngestInput = lastWriteTime;
211                                         youngestInputFile = file;
212                                 }
213                         }
214
215                         foreach (ITaskItem item in outputFiles) {
216                                 string file = item.ItemSpec.Trim ();
217                                 if (file.Length == 0)
218                                         continue;
219
220                                 if (!File.Exists (file)) {
221                                         reason = String.Format ("Target {0} needs to be built as output file '{1}' does not exist.", name, file);
222                                         return true;
223                                 }
224
225                                 DateTime lastWriteTime = File.GetLastWriteTime (file);
226                                 if (lastWriteTime < oldestOutput) {
227                                         oldestOutput = lastWriteTime;
228                                         oldestOutputFile = file;
229                                 }
230                         }
231
232                         if (youngestInput > oldestOutput) {
233                                 reason = String.Format ("Target {0} needs to be built as input file '{1}' is newer than output file '{2}'",
234                                                 name, youngestInputFile, oldestOutputFile);
235                                 return true;
236                         }
237
238                         return false;
239                 }
240
241                 void LogTargetSkipped (Target target, string reason)
242                 {
243                         BuildMessageEventArgs bmea;
244                         bmea = new BuildMessageEventArgs (String.IsNullOrEmpty (reason)
245                                                                 ? String.Format ("Skipping target \"{0}\" because its outputs are up-to-date.", target.Name)
246                                                                 : reason,
247                                                         null, "MSBuild", MessageImportance.Normal);
248                         target.Engine.EventSource.FireMessageRaised (this, bmea);
249                 }
250
251                 void LogTargetStarted (Target target)
252                 {
253                         TargetStartedEventArgs tsea;
254                         string projectFile = project.FullFileName;
255                         tsea = new TargetStartedEventArgs (String.Format ("Target {0} started.", target.Name), null,
256                                         target.Name, projectFile, target.TargetFile);
257                         target.Engine.EventSource.FireTargetStarted (this, tsea);
258                 }
259
260                 void LogTargetFinished (Target target, bool succeeded)
261                 {
262                         TargetFinishedEventArgs tfea;
263                         string projectFile = project.FullFileName;
264                         tfea = new TargetFinishedEventArgs (String.Format ("Target {0} finished.", target.Name), null,
265                                         target.Name, projectFile, target.TargetFile, succeeded);
266                         target.Engine.EventSource.FireTargetFinished (this, tfea);
267                 }
268
269         }
270 }
271
272 #endif