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