Add unit test for AggregateException.GetBaseException that works on .net but is broke...
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / LibraryPcFileCache.cs
1 // 
2 // PcFileCacheAssembly.cs
3 //  
4 // Author:
5 //       Lluis Sanchez Gual <lluis@novell.com>
6 // 
7 // Copyright (c) 2009 Novell, Inc (http://www.novell.com)
8 // 
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 // 
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 // 
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 using System;
28 using System.Text;
29 using System.Xml;
30 using System.IO;
31 using System.Collections.Generic;
32
33 // IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
34 // This code is shared with xbuild, which has to build with .NET 2.0,
35 // so no c# 3.0 syntax is allowed here.
36
37 namespace Mono.PkgConfig
38 {
39         internal class LibraryPcFileCache: PcFileCache<LibraryPackageInfo>
40         {
41                 Dictionary<string, PackageAssemblyInfo> assemblyLocations;
42                 
43                 public LibraryPcFileCache (IPcFileCacheContext<LibraryPackageInfo> ctx): base (ctx)
44                 {
45                 }
46                 
47                 protected override string CacheDirectory {
48                         get {
49                                 string path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
50                                 path = Path.Combine (path, "xbuild");
51                                 return path;
52                         }
53                 }
54                 
55                 // Returns the location of an assembly, given the full name
56                 public PackageAssemblyInfo GetAssemblyLocation (string fullName)
57                 {
58                         return GetAssemblyLocation (fullName, null);
59                 }
60                 
61                 public PackageAssemblyInfo GetAssemblyLocation (string fullName, IEnumerable<string> searchPaths)
62                 {
63                         lock (SyncRoot) {
64                                 if (assemblyLocations == null) {
65                                         // Populate on demand
66                                         assemblyLocations = new Dictionary<string, PackageAssemblyInfo> ();
67                                         foreach (LibraryPackageInfo info in GetPackages (searchPaths)) {
68                                                 if (info.IsValidPackage) {
69                                                         foreach (PackageAssemblyInfo asm in info.Assemblies)
70                                                                 assemblyLocations [NormalizeAsmName (asm.FullName)] = asm;
71                                                 }
72                                         }
73                                 }
74                         }
75                         // This collection is read-only once built, so there is no need for a lock
76                         PackageAssemblyInfo pasm;
77                         assemblyLocations.TryGetValue (NormalizeAsmName (fullName), out pasm);
78                         return pasm;
79                 }
80                 
81                 public IEnumerable<PackageAssemblyInfo> ResolveAssemblyName (string name)
82                 {
83                         return ResolveAssemblyName (name, null);
84                 }
85                 
86                 public IEnumerable<PackageAssemblyInfo> ResolveAssemblyName (string name, IEnumerable<string> searchPaths)
87                 {
88                         foreach (LibraryPackageInfo pinfo in GetPackages (searchPaths)) {
89                                 if (pinfo.IsValidPackage) {
90                                         foreach (PackageAssemblyInfo asm in pinfo.Assemblies) {
91                                                 if (asm.Name == name)
92                                                         yield return asm;
93                                         }
94                                 }
95                         }
96                 }
97                 
98                 protected override void WritePackageContent (XmlTextWriter tw, string file, LibraryPackageInfo pinfo)
99                 {
100                         foreach (PackageAssemblyInfo asm in pinfo.Assemblies) {
101                                 tw.WriteStartElement ("Assembly");
102                                 tw.WriteAttributeString ("name", asm.Name);
103                                 tw.WriteAttributeString ("version", asm.Version);
104                                 tw.WriteAttributeString ("culture", asm.Culture);
105                                 tw.WriteAttributeString ("publicKeyToken", asm.PublicKeyToken);
106                                 tw.WriteAttributeString ("file", asm.File);
107                                 tw.WriteEndElement (); // Assembly
108                         }
109                 }
110                 
111                 protected override void ReadPackageContent (XmlReader tr, LibraryPackageInfo pinfo)
112                 {
113                         while (tr.NodeType == XmlNodeType.Element) {
114                                 PackageAssemblyInfo asm = new PackageAssemblyInfo ();
115                                 asm.Name = tr.GetAttribute ("name");
116                                 asm.Version = tr.GetAttribute ("version");
117                                 asm.Culture = tr.GetAttribute ("culture");
118                                 asm.PublicKeyToken = tr.GetAttribute ("publicKeyToken");
119                                 asm.File = tr.GetAttribute ("file");
120                                 if (pinfo.Assemblies == null)
121                                         pinfo.Assemblies = new List<PackageAssemblyInfo> ();
122                                 asm.ParentPackage = pinfo;
123                                 pinfo.Assemblies.Add (asm);
124                                 tr.Read ();
125                                 tr.MoveToContent ();
126                         }
127                 }
128                 
129                 protected override void ParsePackageInfo (PcFile file, LibraryPackageInfo pinfo)
130                 {
131                         List<string> fullassemblies = null;
132                         bool gacPackageSet = false;
133                         
134                         if (file.Libs != null && file.Libs.IndexOf (".dll") != -1) {
135                                 if (file.Libs.IndexOf ("-lib:") != -1 || file.Libs.IndexOf ("/lib:") != -1) {
136                                         fullassemblies = GetAssembliesWithLibInfo (file.Libs);
137                                 } else {
138                                         fullassemblies = GetAssembliesWithoutLibInfo (file.Libs);
139                                 }
140                         }
141                         
142                         string value = file.GetVariable ("Libraries");
143                         if (!string.IsNullOrEmpty (value))
144                                 fullassemblies = GetAssembliesFromLibrariesVar (value);
145                         
146                         value = file.GetVariable ("GacPackage");
147                         if (value != null) {
148                                 pinfo.IsGacPackage = 
149                                         string.Equals (value, "yes", StringComparison.OrdinalIgnoreCase) ||
150                                         string.Equals (value, "true", StringComparison.OrdinalIgnoreCase);
151                                 gacPackageSet = true;
152                         }
153         
154                         if (fullassemblies == null)
155                                 return;
156                         
157                         string pcDir = Path.GetDirectoryName (file.FilePath);
158                         string monoPrefix = Path.GetDirectoryName (Path.GetDirectoryName (pcDir));
159                         monoPrefix = Path.GetFullPath (monoPrefix + Path.DirectorySeparatorChar + "lib" + Path.DirectorySeparatorChar + "mono" + Path.DirectorySeparatorChar);
160
161                         List<PackageAssemblyInfo> list = new List<PackageAssemblyInfo> ();
162                         foreach (string assembly in fullassemblies) {
163                                 string asm;
164                                 if (Path.IsPathRooted (assembly))
165                                         asm = Path.GetFullPath (assembly);
166                                 else {
167                                         if (Path.GetDirectoryName (assembly).Length == 0) {
168                                                 asm = assembly;
169                                         } else {
170                                                 asm = Path.GetFullPath (Path.Combine (pcDir, assembly));
171                                         }
172                                 }
173                                 if (File.Exists (asm)) {
174                                         PackageAssemblyInfo pi = new PackageAssemblyInfo ();
175                                         pi.File = asm;
176                                         pi.ParentPackage = pinfo;
177                                         pi.UpdateFromFile (pi.File);
178                                         list.Add (pi);
179                                         if (!gacPackageSet && !asm.StartsWith (monoPrefix) && Path.IsPathRooted (asm)) {
180                                                 // Assembly installed outside $(prefix)/lib/mono. It is most likely not a gac package.
181                                                 gacPackageSet = true;
182                                                 pinfo.IsGacPackage = false;
183                                         }
184                                 }
185                         }
186                         pinfo.Assemblies = list;
187                 }
188                 
189                 private List<string> GetAssembliesWithLibInfo (string line)
190                 {
191                         List<string> references = new List<string> ();
192                         List<string> libdirs = new List<string> ();
193                         List<string> retval = new List<string> ();
194                         foreach (string piece in line.Split (' ')) {
195                                 if (IsReferenceParameter (piece)) {
196                                         references.Add (piece.Substring (3).Trim ());
197                                 } else if (piece.TrimStart ().StartsWith ("/lib:", StringComparison.OrdinalIgnoreCase) ||
198                                                 piece.TrimStart ().StartsWith ("-lib:", StringComparison.OrdinalIgnoreCase)) {
199                                         libdirs.Add (piece.Substring (5).Trim ());
200                                 }
201                         }
202         
203                         foreach (string refrnc in references) {
204                                 foreach (string libdir in libdirs) {
205                                         if (File.Exists (libdir + Path.DirectorySeparatorChar + refrnc)) {
206                                                 retval.Add (libdir + Path.DirectorySeparatorChar + refrnc);
207                                         }
208                                 }
209                         }
210         
211                         return retval;
212                 }
213
214                 static bool IsReferenceParameter (string value)
215                 {
216                         return value.TrimStart ().StartsWith ("/r:", StringComparison.OrdinalIgnoreCase) ||
217                                 value.TrimStart ().StartsWith ("-r:", StringComparison.OrdinalIgnoreCase);
218                 }
219                 
220                 List<string> GetAssembliesFromLibrariesVar (string line)
221                 {
222                         List<string> references = new List<string> ();
223                         foreach (string reference in line.Split (' ')) {
224                                 if (!string.IsNullOrEmpty (reference))
225                                         references.Add (reference);
226                         }
227                         return references;
228                 }
229         
230                 private List<string> GetAssembliesWithoutLibInfo (string line)
231                 {
232                         List<string> references = new List<string> ();
233                         foreach (string reference in line.Split (' ')) {
234                                 if (IsReferenceParameter (reference)) {
235                                         string final_ref = reference.Substring (3).Trim ();
236                                         references.Add (final_ref);
237                                 }
238                         }
239                         return references;
240                 }
241                 
242                 public static string NormalizeAsmName (string name)
243                 {
244                         int i = name.IndexOf (", publickeytoken=null", StringComparison.OrdinalIgnoreCase);
245                         if (i != -1)
246                                 name = name.Substring (0, i).Trim ();
247                         i = name.IndexOf (", processorarchitecture=", StringComparison.OrdinalIgnoreCase);
248                         if (i != -1)
249                                 name = name.Substring (0, i).Trim ();
250                         return name;
251                 }
252         }
253         
254         internal class LibraryPackageInfo: PackageInfo
255         {
256                 public bool IsGacPackage {
257                         get { return GetData ("gacPackage") != "false"; }
258                         set {
259                                 if (value)
260                                         RemoveData ("gacPackage");
261                                 else
262                                         SetData ("gacPackage", "false");
263                         }
264                 }
265                 
266                 internal List<PackageAssemblyInfo> Assemblies { get; set; }
267                 
268                 internal protected override bool IsValidPackage {
269                         get { return Assemblies != null && Assemblies.Count > 0; }
270                 }
271         }
272         
273         internal class PackageAssemblyInfo
274         {
275                 public string File { get; set; }
276                 
277                 public string Name;
278                 
279                 public string Version;
280                 
281                 public string Culture;
282                 
283                 public string PublicKeyToken;
284                 
285                 public string FullName {
286                         get {
287                                 string fn = Name + ", Version=" + Version;
288                                 if (!string.IsNullOrEmpty (Culture))
289                                         fn += ", Culture=" + Culture;
290                                 if (!string.IsNullOrEmpty (PublicKeyToken))
291                                         fn += ", PublicKeyToken=" + PublicKeyToken;
292                                 return fn;
293                         }
294                 }
295                 
296                 public LibraryPackageInfo ParentPackage { get; set; }
297                 
298                 public void UpdateFromFile (string file)
299                 {
300                         Update (System.Reflection.AssemblyName.GetAssemblyName (file));
301                 }
302                 
303                 public void Update (System.Reflection.AssemblyName aname)
304                 {
305                         Name = aname.Name;
306                         Version = aname.Version.ToString ();
307                         if (aname.CultureInfo != null) {
308                                 if (aname.CultureInfo.LCID == System.Globalization.CultureInfo.InvariantCulture.LCID)
309                                         Culture = "neutral";
310                                 else
311                                         Culture = aname.CultureInfo.Name;
312                         }
313                         string fn = aname.ToString ();
314                         string key = "publickeytoken=";
315                         int i = fn.IndexOf (key, StringComparison.OrdinalIgnoreCase) + key.Length;
316                         int j = fn.IndexOf (',', i);
317                         if (j == -1) j = fn.Length;
318                         PublicKeyToken = fn.Substring (i, j - i);
319                 }
320         }
321 }