Merge pull request #1225 from strawd/bug22307
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / ResolveAssemblyReference.cs
1 //
2 // ResolveAssemblyReference.cs: Searches for assembly files.
3 //
4 // Author:
5 //   Marek Sieradzki (marek.sieradzki@gmail.com)
6 //   Ankit Jain (jankit@novell.com)
7 // 
8 // (C) 2006 Marek Sieradzki
9 // Copyright 2009 Novell, Inc (http://www.novell.com)
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 //
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 //
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29
30 using System;
31 using System.Collections.Generic;
32 using System.Globalization;
33 using System.IO;
34 using System.Linq;
35 using System.Reflection;
36 using System.Security;
37 using Microsoft.Build.Framework;
38 using Microsoft.Build.Utilities;
39
40 namespace Microsoft.Build.Tasks {
41         public class ResolveAssemblyReference : TaskExtension {
42         
43                 bool            autoUnify;
44                 ITaskItem[]     assemblyFiles;
45                 ITaskItem[]     assemblies;
46                 string          appConfigFile;
47                 string[]        allowedAssemblyExtensions;
48                 string[]        allowedRelatedFileExtensions;
49                 string[]        candidateAssemblyFiles;
50                 ITaskItem[]     copyLocalFiles;
51                 ITaskItem[]     filesWritten;
52                 bool            findDependencies;
53                 bool            findRelatedFiles;
54                 bool            findSatellites;
55                 bool            findSerializationAssemblies;
56                 ITaskItem[]     relatedFiles;
57                 ITaskItem[]     resolvedDependencyFiles;
58                 ITaskItem[]     resolvedFiles;
59                 ITaskItem[]     satelliteFiles;
60                 ITaskItem[]     scatterFiles;
61                 string[]        searchPaths;
62                 ITaskItem[]     serializationAssemblyFiles;
63                 bool            silent;
64                 string          stateFile;
65                 ITaskItem[]     suggestedRedirects;
66                 string[]        targetFrameworkDirectories;
67                 string          targetProcessorArchitecture;
68                 static string []        default_assembly_extensions;
69
70                 AssemblyResolver        assembly_resolver;
71                 List<string> dependency_search_paths;
72                 Dictionary<string, ResolvedReference> assemblyNameToResolvedRef;
73                 Dictionary<string, ITaskItem>   tempSatelliteFiles, tempRelatedFiles,
74                         tempResolvedDepFiles, tempCopyLocalFiles;
75                 List<ITaskItem> tempResolvedFiles;
76                 List<PrimaryReference> primaryReferences;
77                 Dictionary<string, string> alreadyScannedAssemblyNames;
78                 Dictionary<string, string> conflictWarningsCache;
79
80                 //FIXME: construct and use a graph of the dependencies, useful across projects
81
82                 static ResolveAssemblyReference ()
83                 {
84                         default_assembly_extensions = new string [] { ".dll", ".exe" };
85                 }
86
87                 public ResolveAssemblyReference ()
88                 {
89                         assembly_resolver = new AssemblyResolver ();
90                 }
91
92                 //FIXME: make this reusable
93                 public override bool Execute ()
94                 {
95                         if (assemblies == null && assemblyFiles == null)
96                                 // nothing to resolve
97                                 return true;
98
99                         LogTaskParameters ();
100
101                         assembly_resolver.Log = Log;
102                         tempResolvedFiles = new List<ITaskItem> ();
103                         tempCopyLocalFiles = new Dictionary<string, ITaskItem> ();
104                         tempSatelliteFiles = new Dictionary<string, ITaskItem> ();
105                         tempRelatedFiles = new Dictionary<string, ITaskItem> ();
106                         tempResolvedDepFiles = new Dictionary<string, ITaskItem> ();
107
108                         primaryReferences = new List<PrimaryReference> ();
109                         assemblyNameToResolvedRef = new Dictionary<string, ResolvedReference> ();
110                         conflictWarningsCache = new Dictionary<string, string> ();
111
112                         ResolveAssemblies ();
113                         ResolveAssemblyFiles ();
114                         resolvedFiles = tempResolvedFiles.ToArray ();
115
116                         alreadyScannedAssemblyNames = new Dictionary<string, string> ();
117
118                         // the first element is place holder for parent assembly's dir
119                         dependency_search_paths = new List<string> () { String.Empty };
120                         dependency_search_paths.AddRange (searchPaths);
121
122                         // resolve dependencies
123                         foreach (PrimaryReference pref in primaryReferences)
124                                 ResolveAssemblyFileDependencies (pref.TaskItem, pref.ParentCopyLocal);
125
126                         copyLocalFiles = tempCopyLocalFiles.Values.ToArray ();
127                         satelliteFiles = tempSatelliteFiles.Values.ToArray ();
128                         relatedFiles = tempRelatedFiles.Values.ToArray ();
129                         resolvedDependencyFiles = tempResolvedDepFiles.Values.ToArray ();
130
131                         tempResolvedFiles.Clear ();
132                         tempCopyLocalFiles.Clear ();
133                         tempSatelliteFiles.Clear ();
134                         tempRelatedFiles.Clear ();
135                         tempResolvedDepFiles.Clear ();
136                         alreadyScannedAssemblyNames.Clear ();
137                         primaryReferences.Clear ();
138                         assemblyNameToResolvedRef.Clear ();
139                         conflictWarningsCache.Clear ();
140                         dependency_search_paths = null;
141
142                         return true;
143                 }
144
145                 void ResolveAssemblies ()
146                 {
147                         if (assemblies == null || assemblies.Length == 0)
148                                 return;
149
150                         foreach (ITaskItem item in assemblies) {
151                                 if (!String.IsNullOrEmpty (item.GetMetadata ("SubType"))) {
152                                         Log.LogWarning ("Reference '{0}' has non-empty SubType. Ignoring.", item.ItemSpec);
153                                         continue;
154                                 }
155
156                                 LogWithPrecedingNewLine (MessageImportance.Low, "Primary Reference {0}", item.ItemSpec);
157                                 ResolvedReference resolved_ref = ResolveReference (item, searchPaths, true);
158                                 if (resolved_ref == null)
159                                         resolved_ref = ResolveWithAlternateName (item, allowedAssemblyExtensions ?? default_assembly_extensions);
160
161                                 if (resolved_ref == null) {
162                                         Log.LogWarning ("Reference '{0}' not resolved", item.ItemSpec);
163                                         assembly_resolver.LogSearchLoggerMessages (MessageImportance.Normal);
164                                 } else {
165                                         Log.LogMessage (MessageImportance.Low,
166                                                         "\tReference {0} resolved to {1}. CopyLocal = {2}",
167                                                         item.ItemSpec, resolved_ref.TaskItem,
168                                                         resolved_ref.TaskItem.GetMetadata ("CopyLocal"));
169
170                                         Log.LogMessage (MessageImportance.Low,
171                                                         "\tReference found at search path {0}",
172                                                         resolved_ref.FoundInSearchPathAsString);
173
174                                         assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
175
176                                         if (TryAddNewReference (tempResolvedFiles, resolved_ref) &&
177                                                 !IsFromGacOrTargetFramework (resolved_ref) &&
178                                                 resolved_ref.FoundInSearchPath != SearchPath.PkgConfig) {
179                                                 primaryReferences.Add (new PrimaryReference (
180                                                                 resolved_ref.TaskItem,
181                                                                 resolved_ref.TaskItem.GetMetadata ("CopyLocal")));
182                                         }
183                                 }
184                         }
185                 }
186
187
188                 ResolvedReference ResolveWithAlternateName (ITaskItem item, string[] extensions)
189                 {
190                         foreach (string extn in extensions) {
191                                 if (item.ItemSpec.EndsWith (extn)) {
192                                         ITaskItem altitem = new TaskItem (item.ItemSpec.Substring (0, item.ItemSpec.Length - extn.Length));
193                                         item.CopyMetadataTo (altitem);
194                                         return ResolveReference (altitem, searchPaths, true);
195                                 }
196                         }
197                         return null;
198                 }
199
200                 // Use @search_paths to resolve the reference
201                 ResolvedReference ResolveReference (ITaskItem item, IEnumerable<string> search_paths, bool set_copy_local)
202                 {
203                         ResolvedReference resolved = null;
204                         bool specific_version;
205
206                         assembly_resolver.ResetSearchLogger ();
207
208                         if (!TryGetSpecificVersionValue (item, out specific_version))
209                                 return null;
210
211                         var spath_index  = 0;
212                         foreach (string spath in search_paths) {
213                                 if (string.IsNullOrEmpty (spath))
214                                         continue;
215                                 assembly_resolver.LogSearchMessage ("For searchpath {0}", spath);
216
217                                 // The first value of search_paths can be the parent assembly directory.
218                                 // In that case the value would be treated as a directory.
219                                 // This code checks if we should treat the value as a TargetFramework assembly.
220                                 // Doing so avoids CopyLocal beeing set to true.
221                                 if (spath_index++ == 0 && targetFrameworkDirectories != null) {
222                                         foreach (string fpath in targetFrameworkDirectories) {
223                                                 if (string.IsNullOrEmpty (fpath))
224                                                         continue;
225                                                 if (String.Compare (
226                                                                 Path.GetFullPath (spath).TrimEnd (Path.DirectorySeparatorChar),
227                                                                 Path.GetFullPath (fpath).TrimEnd (Path.DirectorySeparatorChar),
228                                                                 StringComparison.InvariantCulture) != 0)
229                                                         continue;
230
231                                                 resolved = assembly_resolver.FindInTargetFramework (item,
232                                                         fpath, specific_version);
233
234                                                 break;
235                                         }
236
237                                         if  (resolved != null)
238                                                 break;
239                                 }
240
241                                 if (String.Compare (spath, "{HintPathFromItem}") == 0) {
242                                         resolved = assembly_resolver.ResolveHintPathReference (item, specific_version);
243                                 } else if (String.Compare (spath, "{TargetFrameworkDirectory}") == 0) {
244                                         if (targetFrameworkDirectories == null)
245                                                 continue;
246                                         foreach (string fpath in targetFrameworkDirectories) {
247                                                 resolved = assembly_resolver.FindInTargetFramework (item,
248                                                                 fpath, specific_version);
249                                                 if (resolved != null)
250                                                         break;
251                                         }
252                                 } else if (String.Compare (spath, "{GAC}") == 0) {
253                                         resolved = assembly_resolver.ResolveGacReference (item, specific_version);
254                                 } else if (String.Compare (spath, "{RawFileName}") == 0) {
255                                         //FIXME: identify assembly names, as extract the name, and try with that?
256                                         AssemblyName aname;
257                                         if (assembly_resolver.TryGetAssemblyNameFromFile (item.ItemSpec, out aname))
258                                                 resolved = assembly_resolver.GetResolvedReference (item, item.ItemSpec, aname, true,
259                                                                 SearchPath.RawFileName);
260                                 } else if (String.Compare (spath, "{CandidateAssemblyFiles}") == 0) {
261                                         assembly_resolver.LogSearchMessage (
262                                                         "Warning: {{CandidateAssemblyFiles}} not supported currently");
263                                 } else if (String.Compare (spath, "{PkgConfig}") == 0) {
264                                         resolved = assembly_resolver.ResolvePkgConfigReference (item, specific_version);
265                                 } else {
266                                         resolved = assembly_resolver.FindInDirectory (
267                                                         item, spath,
268                                                         allowedAssemblyExtensions ?? default_assembly_extensions,
269                                                         specific_version);
270                                 }
271
272                                 if (resolved != null)
273                                         break;
274                         }
275
276                         if (resolved != null && set_copy_local)
277                                 SetCopyLocal (resolved.TaskItem, resolved.CopyLocal.ToString ());
278
279                         return resolved;
280                 }
281
282                 bool TryGetSpecificVersionValue (ITaskItem item, out bool specific_version)
283                 {
284                         specific_version = true;
285                         string value = item.GetMetadata ("SpecificVersion");
286                         if (String.IsNullOrEmpty (value)) {
287                                 //AssemblyName name = new AssemblyName (item.ItemSpec);
288                                 // If SpecificVersion is not specified, then
289                                 // it is true if the Include is a strong name else false
290                                 //specific_version = assembly_resolver.IsStrongNamed (name);
291
292                                 // msbuild seems to just look for a ',' in the name :/
293                                 specific_version = item.ItemSpec.IndexOf (',') >= 0;
294                                 return true;
295                         }
296
297                         if (Boolean.TryParse (value, out specific_version))
298                                 return true;
299
300                         Log.LogError ("Item '{0}' has attribute SpecificVersion with invalid value '{1}' " +
301                                         "which could not be converted to a boolean.", item.ItemSpec, value);
302                         return false;
303                 }
304
305                 //FIXME: Consider CandidateAssemblyFiles also here
306                 void ResolveAssemblyFiles ()
307                 {
308                         if (assemblyFiles == null)
309                                 return;
310
311                         foreach (ITaskItem item in assemblyFiles) {
312                                 assembly_resolver.ResetSearchLogger ();
313
314                                 if (!File.Exists (item.ItemSpec)) {
315                                         LogWithPrecedingNewLine (MessageImportance.Low,
316                                                         "Primary Reference from AssemblyFiles {0}, file not found. Ignoring",
317                                                         item.ItemSpec);
318                                         continue;
319                                 }
320
321                                 LogWithPrecedingNewLine (MessageImportance.Low, "Primary Reference from AssemblyFiles {0}", item.ItemSpec);
322                                 string copy_local;
323
324                                 AssemblyName aname;
325                                 if (!assembly_resolver.TryGetAssemblyNameFromFile (item.ItemSpec, out aname)) {
326                                         Log.LogWarning ("Reference '{0}' not resolved", item.ItemSpec);
327                                         assembly_resolver.LogSearchLoggerMessages (MessageImportance.Normal);
328                                         continue;
329                                 }
330
331                                 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
332
333                                 ResolvedReference rr = assembly_resolver.GetResolvedReference (item, item.ItemSpec, aname, true,
334                                                 SearchPath.RawFileName);
335                                 copy_local = rr.CopyLocal.ToString ();
336
337                                 if (!TryAddNewReference (tempResolvedFiles, rr))
338                                         // already resolved
339                                         continue;
340
341                                 SetCopyLocal (rr.TaskItem, copy_local);
342
343                                 FindAndAddRelatedFiles (item.ItemSpec, copy_local);
344                                 FindAndAddSatellites (item.ItemSpec, copy_local);
345
346                                 if (FindDependencies && !IsFromGacOrTargetFramework (rr) &&
347                                                 rr.FoundInSearchPath != SearchPath.PkgConfig)
348                                         primaryReferences.Add (new PrimaryReference (item, copy_local));
349                         }
350                 }
351
352                 // Tries to resolve assemblies referenced by @item
353                 // Skips gac references
354                 // @item : filename
355                 void ResolveAssemblyFileDependencies (ITaskItem item, string parent_copy_local)
356                 {
357                         Queue<string> dependencies = new Queue<string> ();
358                         dependencies.Enqueue (item.ItemSpec);
359
360                         while (dependencies.Count > 0) {
361                                 string filename = Path.GetFullPath (dependencies.Dequeue ());
362                                 Assembly asm = Assembly.ReflectionOnlyLoadFrom (filename);
363                                 if (alreadyScannedAssemblyNames.ContainsKey (asm.FullName))
364                                         continue;
365
366                                 // set the 1st search path to this ref's base path
367                                 // Will be used for resolving the dependencies
368                                 dependency_search_paths [0] = Path.GetDirectoryName (filename);
369
370                                 foreach (AssemblyName aname in asm.GetReferencedAssemblies ()) {
371                                         if (alreadyScannedAssemblyNames.ContainsKey (aname.FullName))
372                                                 continue;
373
374                                         ResolvedReference resolved_ref = ResolveDependencyByAssemblyName (
375                                                 aname, asm.FullName, parent_copy_local);
376
377                                         if (IncludeDependencies (resolved_ref, aname.FullName)) {
378                                                 tempResolvedDepFiles[resolved_ref.AssemblyName.FullName] = resolved_ref.TaskItem;
379                                                 dependencies.Enqueue (resolved_ref.TaskItem.ItemSpec);
380                                         }
381                                 }
382                                 alreadyScannedAssemblyNames.Add (asm.FullName, String.Empty);
383                         }
384                 }
385
386                 // Resolves by looking dependency_search_paths
387                 // which is dir of parent reference file, and
388                 // SearchPaths
389                 ResolvedReference ResolveDependencyByAssemblyName (AssemblyName aname, string parent_asm_name,
390                                 string parent_copy_local)
391                 {
392                         // This will check for compatible assembly name/version
393                         ResolvedReference resolved_ref;
394                         if (TryGetResolvedReferenceByAssemblyName (aname, false, out resolved_ref))
395                                 return resolved_ref;
396
397                         LogWithPrecedingNewLine (MessageImportance.Low, "Dependency {0}", aname);
398                         Log.LogMessage (MessageImportance.Low, "\tRequired by {0}", parent_asm_name);
399
400                         ITaskItem item = new TaskItem (aname.FullName);
401                         item.SetMetadata ("SpecificVersion", "false");
402                         resolved_ref = ResolveReference (item, dependency_search_paths, false);
403
404                         if (resolved_ref != null) {
405                                 Log.LogMessage (MessageImportance.Low, "\tReference {0} resolved to {1}.",
406                                         aname, resolved_ref.TaskItem.ItemSpec);
407
408                                 Log.LogMessage (MessageImportance.Low,
409                                                 "\tReference found at search path {0}",
410                                                 resolved_ref.FoundInSearchPathAsString);
411
412                                 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
413
414                                 if (resolved_ref.FoundInSearchPath == SearchPath.Directory) {
415                                         // override CopyLocal with parent's val
416                                         SetCopyLocal (resolved_ref.TaskItem, parent_copy_local);
417
418                                         Log.LogMessage (MessageImportance.Low,
419                                                         "\tThis is CopyLocal {0} as parent item has this value",
420                                                         parent_copy_local);
421
422                                         if (TryAddNewReference (tempResolvedFiles, resolved_ref)) {
423                                                 FindAndAddRelatedFiles (resolved_ref.TaskItem.ItemSpec, parent_copy_local);
424                                                 FindAndAddSatellites (resolved_ref.TaskItem.ItemSpec, parent_copy_local);
425                                         }
426                                 } else {
427                                         //gac or tgtfmwk
428                                         Log.LogMessage (MessageImportance.Low,
429                                                         "\tThis is CopyLocal false as it is in the GAC," +
430                                                         "target framework directory or provided by a package.");
431
432                                         TryAddNewReference (tempResolvedFiles, resolved_ref);
433                                 }
434                         } else {
435                                 Log.LogMessage (MessageImportance.Low, "Could not resolve the assembly \"{0}\".", aname);
436                                 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
437                         }
438
439                         return resolved_ref;
440                 }
441
442                 void FindAndAddRelatedFiles (string filename, string parent_copy_local)
443                 {
444                         if (!findRelatedFiles || allowedRelatedFileExtensions == null)
445                                 return;
446
447                         foreach (string ext in allowedRelatedFileExtensions) {
448                                 string rfile = Path.ChangeExtension (filename, ext);
449                                 if (File.Exists (rfile)) {
450                                         ITaskItem item = new TaskItem (rfile);
451                                         SetCopyLocal (item, parent_copy_local);
452
453                                         tempRelatedFiles.AddUniqueFile (item);
454                                 }
455                         }
456                 }
457
458                 void FindAndAddSatellites (string filename, string parent_copy_local)
459                 {
460                         if (!FindSatellites)
461                                 return;
462
463                         string basepath = Path.GetDirectoryName (filename);
464                         string resource = String.Format ("{0}{1}{2}",
465                                         Path.GetFileNameWithoutExtension (filename),
466                                         ".resources",
467                                         Path.GetExtension (filename));
468
469                         string dir_sep = Path.DirectorySeparatorChar.ToString ();
470                         foreach (string dir in Directory.GetDirectories (basepath)) {
471                                 string culture = Path.GetFileName (dir);
472                                 if (!CultureNamesTable.ContainsKey (culture))
473                                         continue;
474
475                                 string res_path = Path.Combine (dir, resource);
476                                 if (File.Exists (res_path)) {
477                                         ITaskItem item = new TaskItem (res_path);
478                                         SetCopyLocal (item, parent_copy_local);
479                                         item.SetMetadata ("DestinationSubdirectory", culture + dir_sep);
480                                         tempSatelliteFiles.AddUniqueFile (item);
481                                 }
482                         }
483                 }
484
485                 // returns true is it was new
486                 bool TryAddNewReference (List<ITaskItem> file_list, ResolvedReference key_ref)
487                 {
488                         ResolvedReference found_ref;
489                         if (!TryGetResolvedReferenceByAssemblyName (key_ref.AssemblyName, key_ref.IsPrimary, out found_ref)) {
490                                 assemblyNameToResolvedRef [key_ref.AssemblyName.Name] = key_ref;
491                                 file_list.Add (key_ref.TaskItem);
492
493                                 return true;
494                         }
495                         return false;
496                 }
497
498                 void SetCopyLocal (ITaskItem item, string copy_local)
499                 {
500                         item.SetMetadata ("CopyLocal", copy_local);
501
502                         // Assumed to be valid value
503                         if (Boolean.Parse (copy_local))
504                                 tempCopyLocalFiles.AddUniqueFile (item);
505                 }
506
507                 bool TryGetResolvedReferenceByAssemblyName (AssemblyName key_aname, bool is_primary, out ResolvedReference found_ref)
508                 {
509                         found_ref = null;
510                         // Match by just name
511                         if (!assemblyNameToResolvedRef.TryGetValue (key_aname.Name, out found_ref))
512                                 // not there
513                                 return false;
514
515                         // match for full name
516                         if (AssemblyResolver.AssemblyNamesCompatible (key_aname, found_ref.AssemblyName, true, false))
517                                 // exact match, so its already there, dont add anything
518                                 return true;
519
520                         // we have a name match, but version mismatch!
521                         assembly_resolver.LogSearchMessage ("A conflict was detected between '{0}' and '{1}'",
522                                         key_aname.FullName, found_ref.AssemblyName.FullName);
523
524                         if (is_primary == found_ref.IsPrimary) {
525                                 assembly_resolver.LogSearchMessage ("Unable to choose between the two. " +
526                                                 "Choosing '{0}' arbitrarily.", found_ref.AssemblyName.FullName);
527                                 return true;
528                         }
529
530                         // since all dependencies are processed after
531                         // all primary refererences, the one in the cache
532                         // has to be a primary
533                         // Prefer a primary reference over a dependency
534
535                         assembly_resolver.LogSearchMessage ("Choosing '{0}' as it is a primary reference.",
536                                         found_ref.AssemblyName.FullName);
537
538                         // If we can successfully use the primary reference, don't log a warning. It's too
539                         // verbose.
540                         //LogConflictWarning (found_ref.AssemblyName.FullName, key_aname.FullName);
541
542                         return true;
543                 }
544
545                 void LogWithPrecedingNewLine (MessageImportance importance, string format, params object [] args)
546                 {
547                         Log.LogMessage (importance, String.Empty);
548                         Log.LogMessage (importance, format, args);
549                 }
550
551                 // conflict b/w @main and @conflicting, picking @main
552                 void LogConflictWarning (string main, string conflicting)
553                 {
554                         string key = main + ":" + conflicting;
555                         if (!conflictWarningsCache.ContainsKey (key)) {
556                                 Log.LogWarning ("Found a conflict between : '{0}' and '{1}'. Using '{0}' reference.",
557                                                 main, conflicting);
558                                 conflictWarningsCache [key] = key;
559                         }
560                 }
561
562                 bool IsFromGacOrTargetFramework (ResolvedReference rr)
563                 {
564                         return rr.FoundInSearchPath == SearchPath.Gac ||
565                                 rr.FoundInSearchPath == SearchPath.TargetFrameworkDirectory;
566                 }
567
568                 bool IncludeDependencies (ResolvedReference rr, string aname)
569                 {
570                         if (rr == null)
571                                 return false;
572                         if (aname.Equals ("System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"))
573                                 return true;
574                         return !IsFromGacOrTargetFramework (rr) && rr.FoundInSearchPath != SearchPath.PkgConfig;
575                 }
576
577                 void LogTaskParameters ()
578                 {
579                         Log.LogMessage (MessageImportance.Low, "TargetFrameworkDirectories:");
580                         if (TargetFrameworkDirectories != null)
581                                 foreach (string dir in TargetFrameworkDirectories)
582                                         Log.LogMessage (MessageImportance.Low, "\t{0}", dir);
583
584                         Log.LogMessage (MessageImportance.Low, "SearchPaths:");
585                         if (SearchPaths != null)
586                                 foreach (string path in SearchPaths)
587                                         Log.LogMessage (MessageImportance.Low, "\t{0}", path);
588                 }
589
590                 public bool AutoUnify {
591                         get { return autoUnify; }
592                         set { autoUnify = value; }
593                 }
594                 
595                 public ITaskItem[] AssemblyFiles {
596                         get { return assemblyFiles; }
597                         set { assemblyFiles = value; }
598                 }
599                 
600                 public ITaskItem[] Assemblies {
601                         get { return assemblies; }
602                         set { assemblies = value; }
603                 }
604                 
605                 public string AppConfigFile {
606                         get { return appConfigFile; }
607                         set { appConfigFile = value; }
608                 }
609                 
610                 public string[] AllowedAssemblyExtensions {
611                         get { return allowedAssemblyExtensions; }
612                         set { allowedAssemblyExtensions = value; }
613                 }
614
615                 public string[] AllowedRelatedFileExtensions {
616                         get { return allowedRelatedFileExtensions; }
617                         set { allowedRelatedFileExtensions = value; }
618                 }
619                 
620                 public string[] CandidateAssemblyFiles {
621                         get { return candidateAssemblyFiles; }
622                         set { candidateAssemblyFiles = value; }
623                 }
624                 
625                 [Output]
626                 public ITaskItem[] CopyLocalFiles {
627                         get { return copyLocalFiles; }
628                 }
629                 
630                 [Output]
631                 public ITaskItem[] FilesWritten {
632                         get { return filesWritten; }
633                         set { filesWritten = value; }
634                 }
635                 
636                 public bool FindDependencies {
637                         get { return findDependencies; }
638                         set { findDependencies = value; }
639                 }
640                 
641                 public bool FindRelatedFiles {
642                         get { return findRelatedFiles; }
643                         set { findRelatedFiles = value; }
644                 }
645                 
646                 public bool FindSatellites {
647                         get { return findSatellites; }
648                         set { findSatellites = value; }
649                 }
650                 
651                 public bool FindSerializationAssemblies {
652                         get { return findSerializationAssemblies; }
653                         set { findSerializationAssemblies = value; }
654                 }
655
656                 public
657                 ITaskItem[]
658                 InstalledAssemblyTables { get; set; }
659
660                 [Output]
661                 public ITaskItem[] RelatedFiles {
662                         get { return relatedFiles; }
663                 }
664                 
665                 [Output]
666                 public ITaskItem[] ResolvedDependencyFiles {
667                         get { return resolvedDependencyFiles; }
668                 }
669                 
670                 [Output]
671                 public ITaskItem[] ResolvedFiles {
672                         get { return resolvedFiles; }
673                 }
674                 
675                 [Output]
676                 public ITaskItem[] SatelliteFiles {
677                         get { return satelliteFiles; }
678                 }
679                 
680                 [Output]
681                 public ITaskItem[] ScatterFiles {
682                         get { return scatterFiles; }
683                 }
684                 
685                 [Required]
686                 public string[] SearchPaths {
687                         get { return searchPaths; }
688                         set { searchPaths = value; }
689                 }
690                 
691                 [Output]
692                 public ITaskItem[] SerializationAssemblyFiles {
693                         get { return serializationAssemblyFiles; }
694                 }
695                 
696                 public bool Silent {
697                         get { return silent; }
698                         set { silent = value; }
699                 }
700                 
701                 public string StateFile {
702                         get { return stateFile; }
703                         set { stateFile = value; }
704                 }
705                 
706                 [Output]
707                 public ITaskItem[] SuggestedRedirects {
708                         get { return suggestedRedirects; }
709                 }
710
711                 public string TargetFrameworkMoniker { get; set; }
712
713                 public string TargetFrameworkMonikerDisplayName { get; set; }
714
715                 public string TargetFrameworkVersion { get; set; }
716
717                 public string[] TargetFrameworkDirectories {
718                         get { return targetFrameworkDirectories; }
719                         set { targetFrameworkDirectories = value; }
720                 }
721                 
722                 public string TargetProcessorArchitecture {
723                         get { return targetProcessorArchitecture; }
724                         set { targetProcessorArchitecture = value; }
725                 }
726
727
728                 static Dictionary<string, string> cultureNamesTable;
729                 static Dictionary<string, string> CultureNamesTable {
730                         get {
731                                 if (cultureNamesTable == null) {
732                                         cultureNamesTable = new Dictionary<string, string> ();
733                                         foreach (CultureInfo ci in CultureInfo.GetCultures (CultureTypes.AllCultures))
734                                                 cultureNamesTable [ci.Name] = ci.Name;
735                                 }
736
737                                 return cultureNamesTable;
738                         }
739                 }
740         }
741
742         static class ResolveAssemblyReferenceHelper {
743                 public static void AddUniqueFile (this Dictionary<string, ITaskItem> dic, ITaskItem item)
744                 {
745                         if (dic == null)
746                                 throw new ArgumentNullException ("dic");
747                         if (item == null)
748                                 throw new ArgumentNullException ("item");
749
750                         string fullpath = Path.GetFullPath (item.ItemSpec);
751                         if (!dic.ContainsKey (fullpath))
752                                 dic [fullpath] = item;
753                 }
754         }
755
756         struct PrimaryReference {
757                 public ITaskItem TaskItem;
758                 public string ParentCopyLocal;
759
760                 public PrimaryReference (ITaskItem item, string parent_copy_local)
761                 {
762                         TaskItem = item;
763                         ParentCopyLocal = parent_copy_local;
764                 }
765         }
766
767 }