Merge pull request #309 from i59/patch-1
[mono.git] / mcs / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / BatchingImplBase.cs
1 //
2 // BatchingImplBase.cs: Base class that implements BatchingAlgorithm 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.Text;
37 using System.Xml;
38
39 using Microsoft.Build.Framework;
40
41 namespace Microsoft.Build.BuildEngine {
42         internal class BatchingImplBase {
43
44                 protected Dictionary<string, BuildItemGroup> consumedItemsByName;
45                 protected List<MetadataReference> consumedMetadataReferences;
46                 protected List<MetadataReference> consumedQMetadataReferences;
47                 protected List<MetadataReference> consumedUQMetadataReferences;
48                 protected Dictionary<string, BuildItemGroup> batchedItemsByName;
49                 protected Dictionary<string, BuildItemGroup> commonItemsByName;
50
51                 protected Project project;
52                 protected ICollection<Dictionary<string, BuildItemGroup>> buckets;
53
54                 protected BatchingImplBase (Project project)
55                 {
56                         if (project == null)
57                                 throw new ArgumentNullException ("project");
58
59                         this.project = project;
60                 }
61
62                 protected void Init ()
63                 {
64                         // all referenced item lists
65                         consumedItemsByName = new Dictionary<string, BuildItemGroup> (StringComparer.OrdinalIgnoreCase);
66
67                         // all referenced metadata
68                         consumedMetadataReferences = new List<MetadataReference> ();
69                         consumedQMetadataReferences = new List<MetadataReference> ();
70                         consumedUQMetadataReferences = new List<MetadataReference> ();
71                 }
72
73                 protected void BatchAndPrepareBuckets ()
74                 {
75                         batchedItemsByName = new Dictionary<string, BuildItemGroup> (StringComparer.OrdinalIgnoreCase);
76
77                         // These will passed as is for every batch
78                         commonItemsByName = new Dictionary<string, BuildItemGroup> (StringComparer.OrdinalIgnoreCase);
79
80                         ValidateUnqualifiedMetadataReferences ();
81
82                         if (consumedUQMetadataReferences.Count > 0) {
83                                 // Atleast one unqualified metadata ref is found, so
84                                 // batching will be done for all referenced item lists
85                                 foreach (KeyValuePair<string, BuildItemGroup> pair in consumedItemsByName)
86                                         batchedItemsByName [pair.Key] = pair.Value;
87                         }
88
89                         // All items referred via qualified metadata refs will be batched
90                         foreach (MetadataReference mr in consumedQMetadataReferences) {
91                                 BuildItemGroup group;
92                                 if (project.TryGetEvaluatedItemByNameBatched (mr.ItemName, out group))
93                                         batchedItemsByName [mr.ItemName] = group;
94                         }
95
96                         // CommonItemNames = ConsumedItemNames - BatchedItemNames
97                         foreach (KeyValuePair<string, BuildItemGroup> pair in consumedItemsByName) {
98                                 if (!batchedItemsByName.ContainsKey (pair.Key))
99                                         commonItemsByName [pair.Key] = pair.Value;
100                         }
101
102                         // Bucketizing
103                         buckets = Bucketize ();
104                 }
105
106                 protected void ParseAttribute (string value)
107                 {
108                         Expression expr = new Expression ();
109                         expr.Parse (value, ParseOptions.AllowItemsMetadataAndSplit);
110
111                         foreach (object o in expr.Collection) {
112                                 MetadataReference mr = o as MetadataReference;
113                                 if (mr != null) {
114                                         consumedMetadataReferences.Add (mr);
115                                         if (mr.IsQualified)
116                                                 consumedQMetadataReferences.Add (mr);
117                                         else
118                                                 consumedUQMetadataReferences.Add (mr);
119                                         continue;
120                                 }
121
122                                 ItemReference ir = o as ItemReference;
123                                 if (ir != null) {
124                                         BuildItemGroup group;
125                                         if (!project.TryGetEvaluatedItemByNameBatched (ir.ItemName, out group))
126                                                 if (!project.EvaluatedItemsByName.TryGetValue (ir.ItemName, out group))
127                                                         group = new BuildItemGroup ();
128
129                                         consumedItemsByName [ir.ItemName] = group;
130                                 }
131                         }
132                 }
133
134                 //Ensure that for every metadataReference in consumedUQMetadataReferences,
135                 //every item in every itemlist in consumedItemsByName has a non-null value
136                 //for that metadata
137                 void ValidateUnqualifiedMetadataReferences ()
138                 {
139                         if (consumedUQMetadataReferences.Count > 0 &&
140                                 consumedItemsByName.Count == 0 &&
141                                 consumedQMetadataReferences.Count == 0) {
142                                 throw new Exception ("Item metadata should be referenced with the item name %(ItemName.MetadataName)");
143                         }
144
145                         foreach (MetadataReference mr in consumedUQMetadataReferences) {
146                                 foreach (KeyValuePair<string, BuildItemGroup> pair in consumedItemsByName) {
147                                         foreach (BuildItem item in pair.Value) {
148                                                 if (item.HasMetadata (mr.MetadataName))
149                                                         continue;
150
151                                                 throw new Exception (String.Format (
152                                                         "Metadata named '{0}' not found in item named {1} in item list named {2}",
153                                                         mr.MetadataName, item.FinalItemSpec, pair.Key));
154                                         }
155                                 }
156                         }
157                 }
158
159                 ICollection<Dictionary<string, BuildItemGroup>> Bucketize ()
160                 {
161                         var buckets = new Dictionary<string, Dictionary<string, BuildItemGroup>> (
162                                         StringComparer.OrdinalIgnoreCase);
163
164                         // For each item list represented in "BatchedItemNames", and then for each item
165                         // within that list, get the values for that item for each of the metadata in
166                         // "ConsumedMetadataReferences". In the table of metadata values, "%(MyItem.MyMetadata)"
167                         // would get a separate entry than "%(MyMetadata)", even though the metadata name is the same.
168
169                         foreach (KeyValuePair<string, BuildItemGroup> pair in batchedItemsByName) {
170                                 string itemName = pair.Key;
171                                 BuildItemGroup group = pair.Value;
172                                 foreach (BuildItem item in group) {
173                                         StringBuilder key_sb = new StringBuilder ();
174                                         string value = String.Empty;
175
176                                         // build the bucket key, unique set of metadata values
177                                         foreach (MetadataReference mr in consumedMetadataReferences) {
178                                                 value = String.Empty;
179                                                 if (mr.IsQualified) {
180                                                         if (String.Compare (mr.ItemName, itemName) == 0)
181                                                                 value = item.GetEvaluatedMetadata (mr.MetadataName);
182                                                 } else {
183                                                         if (item.HasMetadata (mr.MetadataName))
184                                                                 value = item.GetEvaluatedMetadata (mr.MetadataName);
185                                                 }
186
187                                                 key_sb.AppendFormat ("{0}.{1}:{2},",
188                                                                 mr.IsQualified ? mr.ItemName : "",
189                                                                 mr.MetadataName,
190                                                                 value);
191                                         }
192
193                                         // Every bucket corresponds to a unique _set_ of metadata values
194                                         // So, every bucket would have itemGroups with same set of metadata
195                                         // values
196
197                                         string bucket_key = key_sb.ToString ();
198                                         Dictionary<string, BuildItemGroup> bucket;
199                                         if (!buckets.TryGetValue (bucket_key, out bucket))
200                                                 // new bucket
201                                                 buckets [bucket_key] = bucket = new Dictionary<string, BuildItemGroup> (
202                                                                 StringComparer.OrdinalIgnoreCase);
203
204                                         string itemGroup_key = item.Name;
205                                         BuildItemGroup itemGroup;
206                                         if (!bucket.TryGetValue (itemGroup_key, out itemGroup))
207                                                 bucket [itemGroup_key] = itemGroup = new BuildItemGroup ();
208
209                                         itemGroup.AddItem (item);
210                                 }
211                         }
212
213                         if (buckets.Values.Count == 0) {
214                                 // no buckets
215                                 buckets.Add ("none", new Dictionary<string, BuildItemGroup> ());
216                                 AddEmptyGroups (buckets);
217                                 if (buckets ["none"].Values.Count == 0)
218                                         buckets.Remove ("none");
219                         } else {
220                                 AddEmptyGroups (buckets);
221                         }
222
223                         return buckets.Values;
224                 }
225
226                 void AddEmptyGroups (Dictionary<string, Dictionary<string, BuildItemGroup>> buckets)
227                 {
228                         foreach (Dictionary<string, BuildItemGroup> bucket in buckets.Values) {
229                                 foreach (string name in batchedItemsByName.Keys) {
230                                         BuildItemGroup group;
231                                         if (!bucket.TryGetValue (name, out group))
232                                                 bucket [name] = new BuildItemGroup ();
233                                 }
234                         }
235                 }
236
237                public void DumpBuckets (Dictionary<string, Dictionary<string, BuildItemGroup>> buckets)
238                {
239                        foreach (KeyValuePair<string, Dictionary<string, BuildItemGroup>> pair in buckets) {
240                                Console.WriteLine ("Bucket> {0} {", pair.Key);
241                                DumpBucket (pair.Value);
242                                Console.WriteLine ("}");
243                        }
244                }
245
246                 public static void DumpBucket (Dictionary<string, BuildItemGroup> bucket)
247                 {
248                        foreach (KeyValuePair<string, BuildItemGroup> bpair in bucket) {
249                                Console.WriteLine ("\t{0} [", bpair.Key);
250                                foreach (BuildItem item in bpair.Value)
251                                        Console.WriteLine ("\t\t{0} - {1}", item.Name, item.FinalItemSpec);
252                                Console.WriteLine ("\t]");
253                        }
254                 }
255
256
257         }
258 }
259
260 #endif