2010-04-06 Jb Evain <jbevain@novell.com>
[mcs.git] / class / Microsoft.Build.Engine / Microsoft.Build.BuildEngine / BatchingImplBase.cs
blob1d0a1d86b25498db8f3090e98458b87237e8599b
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)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
31 #if NET_2_0
33 using System;
34 using System.IO;
35 using System.Collections.Generic;
36 using System.Text;
37 using System.Xml;
39 using Microsoft.Build.Framework;
41 namespace Microsoft.Build.BuildEngine {
42 internal class BatchingImplBase {
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;
51 protected Project project;
52 protected ICollection<Dictionary<string, BuildItemGroup>> buckets;
54 protected BatchingImplBase (Project project)
56 if (project == null)
57 throw new ArgumentNullException ("project");
59 this.project = project;
62 protected void Init ()
64 // all referenced item lists
65 consumedItemsByName = new Dictionary<string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
67 // all referenced metadata
68 consumedMetadataReferences = new List<MetadataReference> ();
69 consumedQMetadataReferences = new List<MetadataReference> ();
70 consumedUQMetadataReferences = new List<MetadataReference> ();
73 protected void BatchAndPrepareBuckets ()
75 batchedItemsByName = new Dictionary<string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
77 // These will passed as is for every batch
78 commonItemsByName = new Dictionary<string, BuildItemGroup> (StringComparer.InvariantCultureIgnoreCase);
80 ValidateUnqualifiedMetadataReferences ();
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;
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;
96 // CommonItemNames = ConsumedItemNames - BatchedItemNames
97 foreach (KeyValuePair<string, BuildItemGroup> pair in consumedItemsByName) {
98 if (!batchedItemsByName.ContainsKey (pair.Key))
99 commonItemsByName [pair.Key] = pair.Value;
102 // Bucketizing
103 buckets = Bucketize ();
106 protected void ParseAttribute (string value)
108 Expression expr = new Expression ();
109 expr.Parse (value, ParseOptions.AllowItemsMetadataAndSplit);
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;
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 ();
129 consumedItemsByName [ir.ItemName] = group;
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 ()
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)");
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;
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));
159 ICollection<Dictionary<string, BuildItemGroup>> Bucketize ()
161 var buckets = new Dictionary<string, Dictionary<string, BuildItemGroup>> (
162 StringComparer.InvariantCultureIgnoreCase);
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.
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;
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);
187 key_sb.AppendFormat ("{0}.{1}:{2},",
188 mr.IsQualified ? mr.ItemName : "",
189 mr.MetadataName,
190 value);
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
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.InvariantCultureIgnoreCase);
204 string itemGroup_key = item.Name;
205 BuildItemGroup itemGroup;
206 if (!bucket.TryGetValue (itemGroup_key, out itemGroup))
207 bucket [itemGroup_key] = itemGroup = new BuildItemGroup ();
209 itemGroup.AddItem (item);
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);
223 return buckets.Values;
226 void AddEmptyGroups (Dictionary<string, Dictionary<string, BuildItemGroup>> buckets)
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 ();
237 public void DumpBuckets (Dictionary<string, Dictionary<string, BuildItemGroup>> buckets)
239 foreach (KeyValuePair<string, Dictionary<string, BuildItemGroup>> pair in buckets) {
240 Console.WriteLine ("Bucket> {0} {", pair.Key);
241 DumpBucket (pair.Value);
242 Console.WriteLine ("}");
246 public static void DumpBucket (Dictionary<string, BuildItemGroup> bucket)
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]");
260 #endif