2 // ResolveAssemblyReference.cs: Searches for assembly files.
5 // Marek Sieradzki (marek.sieradzki@gmail.com)
6 // Ankit Jain (jankit@novell.com)
8 // (C) 2006 Marek Sieradzki
9 // Copyright 2009 Novell, Inc (http://www.novell.com)
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
31 using System.Collections.Generic;
32 using System.Globalization;
35 using System.Reflection;
36 using System.Security;
37 using Microsoft.Build.Framework;
38 using Microsoft.Build.Utilities;
40 namespace Microsoft.Build.Tasks {
41 public class ResolveAssemblyReference : TaskExtension {
44 ITaskItem[] assemblyFiles;
45 ITaskItem[] assemblies;
47 string[] allowedAssemblyExtensions;
48 string[] allowedRelatedFileExtensions;
49 string[] candidateAssemblyFiles;
50 ITaskItem[] copyLocalFiles;
51 ITaskItem[] filesWritten;
52 bool findDependencies;
53 bool findRelatedFiles;
55 bool findSerializationAssemblies;
56 ITaskItem[] relatedFiles;
57 ITaskItem[] resolvedDependencyFiles;
58 ITaskItem[] resolvedFiles;
59 ITaskItem[] satelliteFiles;
60 ITaskItem[] scatterFiles;
62 ITaskItem[] serializationAssemblyFiles;
65 ITaskItem[] suggestedRedirects;
66 string[] targetFrameworkDirectories;
67 string targetProcessorArchitecture;
68 static string [] default_assembly_extensions;
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;
80 //FIXME: construct and use a graph of the dependencies, useful across projects
82 static ResolveAssemblyReference ()
84 default_assembly_extensions = new string [] { ".dll", ".exe" };
87 public ResolveAssemblyReference ()
89 assembly_resolver = new AssemblyResolver ();
92 //FIXME: make this reusable
93 public override bool Execute ()
95 if (assemblies == null && assemblyFiles == null)
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> ();
108 primaryReferences = new List<PrimaryReference> ();
109 assemblyNameToResolvedRef = new Dictionary<string, ResolvedReference> ();
110 conflictWarningsCache = new Dictionary<string, string> ();
112 ResolveAssemblies ();
113 ResolveAssemblyFiles ();
114 resolvedFiles = tempResolvedFiles.ToArray ();
116 alreadyScannedAssemblyNames = new Dictionary<string, string> ();
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);
122 // resolve dependencies
123 foreach (PrimaryReference pref in primaryReferences)
124 ResolveAssemblyFileDependencies (pref.TaskItem, pref.ParentCopyLocal);
126 copyLocalFiles = tempCopyLocalFiles.Values.ToArray ();
127 satelliteFiles = tempSatelliteFiles.Values.ToArray ();
128 relatedFiles = tempRelatedFiles.Values.ToArray ();
129 resolvedDependencyFiles = tempResolvedDepFiles.Values.ToArray ();
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;
145 void ResolveAssemblies ()
147 if (assemblies == null || assemblies.Length == 0)
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);
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);
161 if (resolved_ref == null) {
162 Log.LogWarning ("Reference '{0}' not resolved", item.ItemSpec);
163 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Normal);
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"));
170 Log.LogMessage (MessageImportance.Low,
171 "\tReference found at search path {0}",
172 resolved_ref.FoundInSearchPathAsString);
174 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
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")));
188 ResolvedReference ResolveWithAlternateName (ITaskItem item, string[] extensions)
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);
200 // Use @search_paths to resolve the reference
201 ResolvedReference ResolveReference (ITaskItem item, IEnumerable<string> search_paths, bool set_copy_local)
203 ResolvedReference resolved = null;
204 bool specific_version;
206 assembly_resolver.ResetSearchLogger ();
208 if (!TryGetSpecificVersionValue (item, out specific_version))
212 foreach (string spath in search_paths) {
213 if (string.IsNullOrEmpty (spath))
215 assembly_resolver.LogSearchMessage ("For searchpath {0}", spath);
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))
226 Path.GetFullPath (spath).TrimEnd (Path.DirectorySeparatorChar),
227 Path.GetFullPath (fpath).TrimEnd (Path.DirectorySeparatorChar),
228 StringComparison.InvariantCulture) != 0)
231 resolved = assembly_resolver.FindInTargetFramework (item,
232 fpath, specific_version);
237 if (resolved != null)
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)
246 foreach (string fpath in targetFrameworkDirectories) {
247 resolved = assembly_resolver.FindInTargetFramework (item,
248 fpath, specific_version);
249 if (resolved != null)
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?
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);
266 resolved = assembly_resolver.FindInDirectory (
268 allowedAssemblyExtensions ?? default_assembly_extensions,
272 if (resolved != null)
276 if (resolved != null && set_copy_local)
277 SetCopyLocal (resolved.TaskItem, resolved.CopyLocal.ToString ());
282 bool TryGetSpecificVersionValue (ITaskItem item, out bool specific_version)
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);
292 // msbuild seems to just look for a ',' in the name :/
293 specific_version = item.ItemSpec.IndexOf (',') >= 0;
297 if (Boolean.TryParse (value, out specific_version))
300 Log.LogError ("Item '{0}' has attribute SpecificVersion with invalid value '{1}' " +
301 "which could not be converted to a boolean.", item.ItemSpec, value);
305 //FIXME: Consider CandidateAssemblyFiles also here
306 void ResolveAssemblyFiles ()
308 if (assemblyFiles == null)
311 foreach (ITaskItem item in assemblyFiles) {
312 assembly_resolver.ResetSearchLogger ();
314 if (!File.Exists (item.ItemSpec)) {
315 LogWithPrecedingNewLine (MessageImportance.Low,
316 "Primary Reference from AssemblyFiles {0}, file not found. Ignoring",
321 LogWithPrecedingNewLine (MessageImportance.Low, "Primary Reference from AssemblyFiles {0}", item.ItemSpec);
325 if (!assembly_resolver.TryGetAssemblyNameFromFile (item.ItemSpec, out aname)) {
326 Log.LogWarning ("Reference '{0}' not resolved", item.ItemSpec);
327 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Normal);
331 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
333 ResolvedReference rr = assembly_resolver.GetResolvedReference (item, item.ItemSpec, aname, true,
334 SearchPath.RawFileName);
335 copy_local = rr.CopyLocal.ToString ();
337 if (!TryAddNewReference (tempResolvedFiles, rr))
341 SetCopyLocal (rr.TaskItem, copy_local);
343 FindAndAddRelatedFiles (item.ItemSpec, copy_local);
344 FindAndAddSatellites (item.ItemSpec, copy_local);
346 if (FindDependencies && !IsFromGacOrTargetFramework (rr) &&
347 rr.FoundInSearchPath != SearchPath.PkgConfig)
348 primaryReferences.Add (new PrimaryReference (item, copy_local));
352 // Tries to resolve assemblies referenced by @item
353 // Skips gac references
355 void ResolveAssemblyFileDependencies (ITaskItem item, string parent_copy_local)
357 Queue<string> dependencies = new Queue<string> ();
358 dependencies.Enqueue (item.ItemSpec);
360 while (dependencies.Count > 0) {
361 string filename = Path.GetFullPath (dependencies.Dequeue ());
362 Assembly asm = Assembly.ReflectionOnlyLoadFrom (filename);
363 if (alreadyScannedAssemblyNames.ContainsKey (asm.FullName))
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);
370 foreach (AssemblyName aname in asm.GetReferencedAssemblies ()) {
371 if (alreadyScannedAssemblyNames.ContainsKey (aname.FullName))
374 ResolvedReference resolved_ref = ResolveDependencyByAssemblyName (
375 aname, asm.FullName, parent_copy_local);
377 if (IncludeDependencies (resolved_ref, aname.FullName)) {
378 tempResolvedDepFiles[resolved_ref.AssemblyName.FullName] = resolved_ref.TaskItem;
379 dependencies.Enqueue (resolved_ref.TaskItem.ItemSpec);
382 alreadyScannedAssemblyNames.Add (asm.FullName, String.Empty);
386 // Resolves by looking dependency_search_paths
387 // which is dir of parent reference file, and
389 ResolvedReference ResolveDependencyByAssemblyName (AssemblyName aname, string parent_asm_name,
390 string parent_copy_local)
392 // This will check for compatible assembly name/version
393 ResolvedReference resolved_ref;
394 if (TryGetResolvedReferenceByAssemblyName (aname, false, out resolved_ref))
397 LogWithPrecedingNewLine (MessageImportance.Low, "Dependency {0}", aname);
398 Log.LogMessage (MessageImportance.Low, "\tRequired by {0}", parent_asm_name);
400 ITaskItem item = new TaskItem (aname.FullName);
401 item.SetMetadata ("SpecificVersion", "false");
402 resolved_ref = ResolveReference (item, dependency_search_paths, false);
404 if (resolved_ref != null) {
405 Log.LogMessage (MessageImportance.Low, "\tReference {0} resolved to {1}.",
406 aname, resolved_ref.TaskItem.ItemSpec);
408 Log.LogMessage (MessageImportance.Low,
409 "\tReference found at search path {0}",
410 resolved_ref.FoundInSearchPathAsString);
412 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
414 if (resolved_ref.FoundInSearchPath == SearchPath.Directory) {
415 // override CopyLocal with parent's val
416 SetCopyLocal (resolved_ref.TaskItem, parent_copy_local);
418 Log.LogMessage (MessageImportance.Low,
419 "\tThis is CopyLocal {0} as parent item has this value",
422 if (TryAddNewReference (tempResolvedFiles, resolved_ref)) {
423 FindAndAddRelatedFiles (resolved_ref.TaskItem.ItemSpec, parent_copy_local);
424 FindAndAddSatellites (resolved_ref.TaskItem.ItemSpec, parent_copy_local);
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.");
432 TryAddNewReference (tempResolvedFiles, resolved_ref);
435 Log.LogMessage (MessageImportance.Low, "Could not resolve the assembly \"{0}\".", aname);
436 assembly_resolver.LogSearchLoggerMessages (MessageImportance.Low);
442 void FindAndAddRelatedFiles (string filename, string parent_copy_local)
444 if (!findRelatedFiles || allowedRelatedFileExtensions == null)
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);
453 tempRelatedFiles.AddUniqueFile (item);
458 void FindAndAddSatellites (string filename, string parent_copy_local)
463 string basepath = Path.GetDirectoryName (filename);
464 string resource = String.Format ("{0}{1}{2}",
465 Path.GetFileNameWithoutExtension (filename),
467 Path.GetExtension (filename));
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))
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);
485 // returns true is it was new
486 bool TryAddNewReference (List<ITaskItem> file_list, ResolvedReference key_ref)
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);
498 void SetCopyLocal (ITaskItem item, string copy_local)
500 item.SetMetadata ("CopyLocal", copy_local);
502 // Assumed to be valid value
503 if (Boolean.Parse (copy_local))
504 tempCopyLocalFiles.AddUniqueFile (item);
507 bool TryGetResolvedReferenceByAssemblyName (AssemblyName key_aname, bool is_primary, out ResolvedReference found_ref)
510 // Match by just name
511 if (!assemblyNameToResolvedRef.TryGetValue (key_aname.Name, out found_ref))
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
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);
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);
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
535 assembly_resolver.LogSearchMessage ("Choosing '{0}' as it is a primary reference.",
536 found_ref.AssemblyName.FullName);
538 // If we can successfully use the primary reference, don't log a warning. It's too
540 //LogConflictWarning (found_ref.AssemblyName.FullName, key_aname.FullName);
545 void LogWithPrecedingNewLine (MessageImportance importance, string format, params object [] args)
547 Log.LogMessage (importance, String.Empty);
548 Log.LogMessage (importance, format, args);
551 // conflict b/w @main and @conflicting, picking @main
552 void LogConflictWarning (string main, string conflicting)
554 string key = main + ":" + conflicting;
555 if (!conflictWarningsCache.ContainsKey (key)) {
556 Log.LogWarning ("Found a conflict between : '{0}' and '{1}'. Using '{0}' reference.",
558 conflictWarningsCache [key] = key;
562 bool IsFromGacOrTargetFramework (ResolvedReference rr)
564 return rr.FoundInSearchPath == SearchPath.Gac ||
565 rr.FoundInSearchPath == SearchPath.TargetFrameworkDirectory;
568 bool IncludeDependencies (ResolvedReference rr, string aname)
572 if (aname.Equals ("System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"))
574 return !IsFromGacOrTargetFramework (rr) && rr.FoundInSearchPath != SearchPath.PkgConfig;
577 void LogTaskParameters ()
579 Log.LogMessage (MessageImportance.Low, "TargetFrameworkDirectories:");
580 if (TargetFrameworkDirectories != null)
581 foreach (string dir in TargetFrameworkDirectories)
582 Log.LogMessage (MessageImportance.Low, "\t{0}", dir);
584 Log.LogMessage (MessageImportance.Low, "SearchPaths:");
585 if (SearchPaths != null)
586 foreach (string path in SearchPaths)
587 Log.LogMessage (MessageImportance.Low, "\t{0}", path);
590 public bool AutoUnify {
591 get { return autoUnify; }
592 set { autoUnify = value; }
595 public ITaskItem[] AssemblyFiles {
596 get { return assemblyFiles; }
597 set { assemblyFiles = value; }
600 public ITaskItem[] Assemblies {
601 get { return assemblies; }
602 set { assemblies = value; }
605 public string AppConfigFile {
606 get { return appConfigFile; }
607 set { appConfigFile = value; }
610 public string[] AllowedAssemblyExtensions {
611 get { return allowedAssemblyExtensions; }
612 set { allowedAssemblyExtensions = value; }
615 public string[] AllowedRelatedFileExtensions {
616 get { return allowedRelatedFileExtensions; }
617 set { allowedRelatedFileExtensions = value; }
620 public string[] CandidateAssemblyFiles {
621 get { return candidateAssemblyFiles; }
622 set { candidateAssemblyFiles = value; }
626 public ITaskItem[] CopyLocalFiles {
627 get { return copyLocalFiles; }
631 public ITaskItem[] FilesWritten {
632 get { return filesWritten; }
633 set { filesWritten = value; }
636 public bool FindDependencies {
637 get { return findDependencies; }
638 set { findDependencies = value; }
641 public bool FindRelatedFiles {
642 get { return findRelatedFiles; }
643 set { findRelatedFiles = value; }
646 public bool FindSatellites {
647 get { return findSatellites; }
648 set { findSatellites = value; }
651 public bool FindSerializationAssemblies {
652 get { return findSerializationAssemblies; }
653 set { findSerializationAssemblies = value; }
658 InstalledAssemblyTables { get; set; }
661 public ITaskItem[] RelatedFiles {
662 get { return relatedFiles; }
666 public ITaskItem[] ResolvedDependencyFiles {
667 get { return resolvedDependencyFiles; }
671 public ITaskItem[] ResolvedFiles {
672 get { return resolvedFiles; }
676 public ITaskItem[] SatelliteFiles {
677 get { return satelliteFiles; }
681 public ITaskItem[] ScatterFiles {
682 get { return scatterFiles; }
686 public string[] SearchPaths {
687 get { return searchPaths; }
688 set { searchPaths = value; }
692 public ITaskItem[] SerializationAssemblyFiles {
693 get { return serializationAssemblyFiles; }
697 get { return silent; }
698 set { silent = value; }
701 public string StateFile {
702 get { return stateFile; }
703 set { stateFile = value; }
707 public ITaskItem[] SuggestedRedirects {
708 get { return suggestedRedirects; }
711 public string TargetFrameworkMoniker { get; set; }
713 public string TargetFrameworkMonikerDisplayName { get; set; }
715 public string TargetFrameworkVersion { get; set; }
717 public string[] TargetFrameworkDirectories {
718 get { return targetFrameworkDirectories; }
719 set { targetFrameworkDirectories = value; }
722 public string TargetProcessorArchitecture {
723 get { return targetProcessorArchitecture; }
724 set { targetProcessorArchitecture = value; }
728 static Dictionary<string, string> cultureNamesTable;
729 static Dictionary<string, string> CultureNamesTable {
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;
737 return cultureNamesTable;
742 static class ResolveAssemblyReferenceHelper {
743 public static void AddUniqueFile (this Dictionary<string, ITaskItem> dic, ITaskItem item)
746 throw new ArgumentNullException ("dic");
748 throw new ArgumentNullException ("item");
750 string fullpath = Path.GetFullPath (item.ItemSpec);
751 if (!dic.ContainsKey (fullpath))
752 dic [fullpath] = item;
756 struct PrimaryReference {
757 public ITaskItem TaskItem;
758 public string ParentCopyLocal;
760 public PrimaryReference (ITaskItem item, string parent_copy_local)
763 ParentCopyLocal = parent_copy_local;