* Project.cs (.ctor): Init timeOfLastDirty.
[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
47                 public TargetBatchingImpl (Project project, XmlElement targetElement)
48                         : base (project)
49                 {
50                         if (targetElement == null)
51                                 throw new ArgumentNullException ("targetElement");
52
53                         inputs = targetElement.GetAttribute ("Inputs");
54                         outputs = targetElement.GetAttribute ("Outputs");
55                 }
56
57                 public bool Build (Target target, out bool executeOnErrors)
58                 {
59                         executeOnErrors = false;
60                         try {
61                                 if (!BuildTargetNeeded ()) {
62                                         LogTargetStarted (target);
63                                         LogTargetSkipped (target);
64                                         LogTargetFinished (target, true);
65                                         return true;
66                                 }
67
68                                 Init ();
69
70                                 ParseTargetAttributes (target);
71                                 BatchAndPrepareBuckets ();
72                                 return Run (target, out executeOnErrors);
73                         } finally {
74                                 consumedItemsByName = null;
75                                 consumedMetadataReferences = null;
76                                 consumedQMetadataReferences = null;
77                                 consumedUQMetadataReferences = null;
78                                 batchedItemsByName = null;
79                                 commonItemsByName = null;
80                         }
81                 }
82
83                 bool Run (Target target, out bool executeOnErrors)
84                 {
85                         executeOnErrors = false;
86                         if (buckets.Count > 0)
87                                 return RunBatched (target, out executeOnErrors);
88                         else
89                                 return RunUnbatched (target, out executeOnErrors);
90                 }
91
92                 bool RunBatched (Target target, out bool executeOnErrors)
93                 {
94                         bool result = true;
95                         executeOnErrors = false;
96                         foreach (Dictionary<string, BuildItemGroup> bucket in buckets) {
97                                 LogTargetStarted (target);
98                                 try {
99                                         project.SetBatchedItems (bucket, commonItemsByName);
100                                         if (!BuildTargetNeeded ()) {
101                                                 LogTargetSkipped (target);
102                                                 continue;
103                                         }
104
105                                         for (int i = 0; i < target.BuildTasks.Count; i ++) {
106                                                 //required setting here, as batchtask.Run resets
107                                                 //these to null before returning!
108                                                 project.SetBatchedItems (bucket, commonItemsByName);
109
110                                                 //FIXME: parsing attributes repeatedly
111                                                 BuildTask task = target.BuildTasks [i];
112                                                 result = new TaskBatchingImpl (project).Build (task, out executeOnErrors);
113                                                 if (!result && !task.ContinueOnError) {
114                                                         executeOnErrors = true;
115                                                         break;
116                                                 }
117                                         }
118                                 } finally {
119                                         LogTargetFinished (target, result);
120                                 }
121                         }
122                         project.SetBatchedItems (null, null);
123
124                         return result;
125                 }
126
127                 bool RunUnbatched (Target target, out bool executeOnErrors)
128                 {
129                         bool result = true;
130                         executeOnErrors = false;
131                         LogTargetStarted (target);
132                         try {
133                                 if (!BuildTargetNeeded ()) {
134                                         LogTargetSkipped (target);
135                                         LogTargetFinished (target, true);
136                                         return true;
137                                 }
138
139                                 foreach (BuildTask bt in target.BuildTasks) {
140                                         TaskBatchingImpl batchingImpl = new TaskBatchingImpl (project);
141                                         result = batchingImpl.Build (bt, out executeOnErrors);
142
143                                         if (!result && !bt.ContinueOnError) {
144                                                 executeOnErrors = true;
145                                                 break;
146                                         }
147                                 }
148                         } finally {
149                                 LogTargetFinished (target, result);
150                         }
151
152                         return result;
153                 }
154
155                 // Parse target's Input and Output attributes to get list of referenced
156                 // metadata and items to determine batching
157                 void ParseTargetAttributes (Target target)
158                 {
159                         if (!String.IsNullOrEmpty (inputs))
160                                 ParseAttribute (inputs);
161
162                         if (!String.IsNullOrEmpty (outputs))
163                                 ParseAttribute (outputs);
164                 }
165
166                 bool BuildTargetNeeded ()
167                 {
168                         ITaskItem [] inputFiles;
169                         ITaskItem [] outputFiles;
170                         DateTime oldestInput, youngestOutput;
171
172                         if (String.IsNullOrEmpty (inputs.Trim ()))
173                                 return true;
174
175                         if (String.IsNullOrEmpty (outputs.Trim ()))
176                                 return true;
177
178                         Expression e = new Expression ();
179                         e.Parse (inputs, true);
180                         inputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]));
181
182                         e = new Expression ();
183                         e.Parse (outputs, true);
184                         outputFiles = (ITaskItem[]) e.ConvertTo (project, typeof (ITaskItem[]));
185
186                         if (inputFiles == null || inputFiles.Length == 0)
187                                 return false;
188
189                         //FIXME: if input specified, then output must also
190                         //       be there, add tests and confirm
191                         if (outputFiles == null || outputFiles.Length == 0)
192                                 return false;
193
194                         if (File.Exists (inputFiles [0].ItemSpec))
195                                 oldestInput = File.GetLastWriteTime (inputFiles [0].ItemSpec);
196                         else
197                                 return true;
198
199                         if (File.Exists (outputFiles [0].ItemSpec))
200                                 youngestOutput = File.GetLastWriteTime (outputFiles [0].ItemSpec);
201                         else
202                                 return true;
203
204                         foreach (ITaskItem item in inputFiles) {
205                                 string file = item.ItemSpec;
206                                 if (file.Trim () == String.Empty)
207                                         continue;
208
209                                 if (File.Exists (file.Trim ())) {
210                                         if (File.GetLastWriteTime (file.Trim ()) > oldestInput)
211                                                 oldestInput = File.GetLastWriteTime (file.Trim ());
212                                 } else {
213                                         return true;
214                                 }
215                         }
216
217                         foreach (ITaskItem item in outputFiles) {
218                                 string file = item.ItemSpec;
219                                 if (file.Trim () == String.Empty)
220                                         continue;
221
222                                 if (File.Exists (file.Trim ())) {
223                                         if (File.GetLastWriteTime (file.Trim ()) < youngestOutput)
224                                                 youngestOutput = File.GetLastWriteTime (file.Trim ());
225                                 } else
226                                         return true;
227                         }
228
229                         if (oldestInput > youngestOutput)
230                                 return true;
231                         else
232                                 return false;
233                 }
234
235                 void LogTargetSkipped (Target target)
236                 {
237                         BuildMessageEventArgs bmea;
238                         bmea = new BuildMessageEventArgs (String.Format ("Skipping target \"{0}\" because its outputs are up-to-date.",
239                                 target.Name), null, "MSBuild", MessageImportance.Normal);
240                         target.Engine.EventSource.FireMessageRaised (this, bmea);
241                 }
242
243                 void LogTargetStarted (Target target)
244                 {
245                         TargetStartedEventArgs tsea;
246                         string projectFile = project.FullFileName;
247                         tsea = new TargetStartedEventArgs (String.Format ("Target {0} started.", target.Name), null, target.Name, projectFile, null);
248                         target.Engine.EventSource.FireTargetStarted (this, tsea);
249                 }
250
251                 void LogTargetFinished (Target target, bool succeeded)
252                 {
253                         TargetFinishedEventArgs tfea;
254                         string projectFile = project.FullFileName;
255                         tfea = new TargetFinishedEventArgs (String.Format ("Target {0} finished.", target.Name), null, target.Name, projectFile, null, succeeded);
256                         target.Engine.EventSource.FireTargetFinished (this, tfea);
257                 }
258
259         }
260 }
261
262 #endif