2 // BatchingImplBase.cs: Base class that implements BatchingAlgorithm from the wiki.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 // Ankit Jain (jankit@novell.com)
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.
35 using System
.Collections
.Generic
;
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
)
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
) {
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
;
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
;
114 consumedMetadataReferences
.Add (mr
);
116 consumedQMetadataReferences
.Add (mr
);
118 consumedUQMetadataReferences
.Add (mr
);
122 ItemReference ir
= o
as ItemReference
;
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
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
))
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
);
183 if (item
.HasMetadata (mr
.MetadataName
))
184 value = item
.GetEvaluatedMetadata (mr
.MetadataName
);
187 key_sb
.AppendFormat ("{0}.{1}:{2},",
188 mr
.IsQualified
? mr
.ItemName
: "",
193 // Every bucket corresponds to a unique _set_ of metadata values
194 // So, every bucket would have itemGroups with same set of metadata
197 string bucket_key
= key_sb
.ToString ();
198 Dictionary
<string, BuildItemGroup
> bucket
;
199 if (!buckets
.TryGetValue (bucket_key
, out 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) {
215 buckets
.Add ("none", new Dictionary
<string, BuildItemGroup
> ());
216 AddEmptyGroups (buckets
);
217 if (buckets
["none"].Values
.Count
== 0)
218 buckets
.Remove ("none");
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]");