2 // PcFileCacheAssembly.cs
5 // Lluis Sanchez Gual <lluis@novell.com>
7 // Copyright (c) 2009 Novell, Inc (http://www.novell.com)
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:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
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
31 using System.Collections.Generic;
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.
37 namespace Mono.PkgConfig
39 internal class LibraryPcFileCache: PcFileCache<LibraryPackageInfo>
41 Dictionary<string, PackageAssemblyInfo> assemblyLocations;
43 public LibraryPcFileCache (IPcFileCacheContext<LibraryPackageInfo> ctx): base (ctx)
47 protected override string CacheDirectory {
49 string path = Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData);
50 path = Path.Combine (path, "xbuild");
55 // Returns the location of an assembly, given the full name
56 public PackageAssemblyInfo GetAssemblyLocation (string fullName)
58 return GetAssemblyLocation (fullName, null);
61 public PackageAssemblyInfo GetAssemblyLocation (string fullName, IEnumerable<string> searchPaths)
64 if (assemblyLocations == null) {
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;
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);
81 public IEnumerable<PackageAssemblyInfo> ResolveAssemblyName (string name)
83 return ResolveAssemblyName (name, null);
86 public IEnumerable<PackageAssemblyInfo> ResolveAssemblyName (string name, IEnumerable<string> searchPaths)
88 foreach (LibraryPackageInfo pinfo in GetPackages (searchPaths)) {
89 if (pinfo.IsValidPackage) {
90 foreach (PackageAssemblyInfo asm in pinfo.Assemblies) {
98 protected override void WritePackageContent (XmlTextWriter tw, string file, LibraryPackageInfo pinfo)
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
111 protected override void ReadPackageContent (XmlReader tr, LibraryPackageInfo pinfo)
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);
129 protected override void ParsePackageInfo (PcFile file, LibraryPackageInfo pinfo)
131 List<string> fullassemblies = null;
132 bool gacPackageSet = false;
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);
138 fullassemblies = GetAssembliesWithoutLibInfo (file.Libs);
142 string value = file.GetVariable ("Libraries");
143 if (!string.IsNullOrEmpty (value))
144 fullassemblies = GetAssembliesFromLibrariesVar (value);
146 value = file.GetVariable ("GacPackage");
149 string.Equals (value, "yes", StringComparison.OrdinalIgnoreCase) ||
150 string.Equals (value, "true", StringComparison.OrdinalIgnoreCase);
151 gacPackageSet = true;
154 if (fullassemblies == null)
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);
161 List<PackageAssemblyInfo> list = new List<PackageAssemblyInfo> ();
162 foreach (string assembly in fullassemblies) {
164 if (Path.IsPathRooted (assembly))
165 asm = Path.GetFullPath (assembly);
167 if (Path.GetDirectoryName (assembly).Length == 0) {
170 asm = Path.GetFullPath (Path.Combine (pcDir, assembly));
173 if (File.Exists (asm)) {
174 PackageAssemblyInfo pi = new PackageAssemblyInfo ();
176 pi.ParentPackage = pinfo;
177 pi.UpdateFromFile (pi.File);
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;
186 pinfo.Assemblies = list;
189 private List<string> GetAssembliesWithLibInfo (string line)
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 ());
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);
214 static bool IsReferenceParameter (string value)
216 return value.TrimStart ().StartsWith ("/r:", StringComparison.OrdinalIgnoreCase) ||
217 value.TrimStart ().StartsWith ("-r:", StringComparison.OrdinalIgnoreCase);
220 List<string> GetAssembliesFromLibrariesVar (string line)
222 List<string> references = new List<string> ();
223 foreach (string reference in line.Split (' ')) {
224 if (!string.IsNullOrEmpty (reference))
225 references.Add (reference);
230 private List<string> GetAssembliesWithoutLibInfo (string line)
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);
242 public static string NormalizeAsmName (string name)
244 int i = name.IndexOf (", publickeytoken=null", StringComparison.OrdinalIgnoreCase);
246 name = name.Substring (0, i).Trim ();
247 i = name.IndexOf (", processorarchitecture=", StringComparison.OrdinalIgnoreCase);
249 name = name.Substring (0, i).Trim ();
254 internal class LibraryPackageInfo: PackageInfo
256 public bool IsGacPackage {
257 get { return GetData ("gacPackage") != "false"; }
260 RemoveData ("gacPackage");
262 SetData ("gacPackage", "false");
266 internal List<PackageAssemblyInfo> Assemblies { get; set; }
268 internal protected override bool IsValidPackage {
269 get { return Assemblies != null && Assemblies.Count > 0; }
273 internal class PackageAssemblyInfo
275 public string File { get; set; }
279 public string Version;
281 public string Culture;
283 public string PublicKeyToken;
285 public string FullName {
287 string fn = Name + ", Version=" + Version;
288 if (!string.IsNullOrEmpty (Culture))
289 fn += ", Culture=" + Culture;
290 if (!string.IsNullOrEmpty (PublicKeyToken))
291 fn += ", PublicKeyToken=" + PublicKeyToken;
296 public LibraryPackageInfo ParentPackage { get; set; }
298 public void UpdateFromFile (string file)
300 Update (System.Reflection.AssemblyName.GetAssemblyName (file));
303 public void Update (System.Reflection.AssemblyName aname)
306 Version = aname.Version.ToString ();
307 if (aname.CultureInfo != null) {
308 if (aname.CultureInfo.LCID == System.Globalization.CultureInfo.InvariantCulture.LCID)
311 Culture = aname.CultureInfo.Name;
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);