b0f9af8aacdd35ad5576eb1ebc23f47f30870d8c
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / GetReferenceAssemblyPaths.cs
1 using System;
2 using Microsoft.Build.Framework;
3 using System.IO;
4 using System.Reflection;
5 using System.Xml;
6 using System.Collections.Generic;
7
8 using Mono.XBuild.Utilities;
9
10 #if NET_4_0
11
12 namespace Microsoft.Build.Tasks
13 {
14         public class GetReferenceAssemblyPaths : TaskExtension
15         {
16                 static string framework_base_path;
17                 static string PathSeparatorAsString = Path.PathSeparator.ToString ();
18                 const string MacOSXExternalXBuildDir = "/Library/Frameworks/Mono.framework/External/xbuild-frameworks";
19
20                 public GetReferenceAssemblyPaths ()
21                 {
22                 }
23
24                 public override bool Execute ()
25                 {
26                         FrameworkMoniker moniker = null;
27                         if (!TryParseTargetFrameworkMoniker (TargetFrameworkMoniker, out moniker))
28                                 return false;
29
30                         var framework = GetFrameworkDirectoriesForMoniker (moniker);
31                         if (framework == null) {
32                                 Log.LogWarning ("Unable to find framework corresponding to the target framework moniker '{0}'. " +
33                                                 "Framework assembly references will be resolved from the GAC, which might not be " +
34                                                 "the intended behavior.", TargetFrameworkMoniker);
35                                 return true;
36                         }
37
38                         ReferenceAssemblyPaths = FullFrameworkReferenceAssemblyPaths = framework.Directories;
39                         TargetFrameworkMonikerDisplayName = framework.DisplayName;
40
41                         return true;
42                 }
43
44                 Framework GetFrameworkDirectoriesForMoniker (FrameworkMoniker moniker)
45                 {
46                         string dirs = String.Join (PathSeparatorAsString, new string [] {
47                                                         Environment.GetEnvironmentVariable ("XBUILD_FRAMEWORK_FOLDERS_PATH") ?? String.Empty,
48                                                         MSBuildUtils.RunningOnMac ? MacOSXExternalXBuildDir : String.Empty,
49                                                         RootPath,
50                                                         DefaultFrameworksBasePath });
51
52                         string [] paths = dirs.Split (new char [] {Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries);
53                         foreach (string path in paths) {
54                                 var framework = GetFrameworkDirectoriesForMoniker (moniker, path);
55                                 if (framework != null)
56                                         return framework;
57                         }
58
59                         return null;
60                 }
61
62                 //@base_path must be absolute
63                 Framework GetFrameworkDirectoriesForMoniker (FrameworkMoniker moniker, string base_path)
64                 {
65                         if (String.IsNullOrEmpty (base_path)) {
66                                 Log.LogMessage (MessageImportance.Low, "Invalid *empty* base path, ignoring. " + Environment.StackTrace);
67                                 return null;
68                         }
69
70                         Log.LogMessage (MessageImportance.Low, "Looking for framework '{0}' in root path '{1}'",
71                                         moniker, base_path);
72                         string framework_path = Path.Combine (base_path, Path.Combine (moniker.Identifier, moniker.Version));
73                         if (!String.IsNullOrEmpty (moniker.Profile))
74                                 framework_path = Path.Combine (framework_path, moniker.Profile);
75
76                         string redistlist_dir = Path.Combine (framework_path, "RedistList");
77                         string framework_list = Path.Combine (redistlist_dir, "FrameworkList.xml");
78                         if (!File.Exists (framework_list)) {
79                                 Log.LogMessage (MessageImportance.Low,
80                                                         "Unable to find framework definition file '{0}' for Target Framework Moniker '{1}'",
81                                                         framework_list, moniker);
82                                 return null;
83                         }
84
85                         Log.LogMessage (MessageImportance.Low, "Found framework definition list '{0}' for framework '{1}'",
86                                         framework_list, moniker);
87                         XmlReader xr = XmlReader.Create (framework_list);
88                         try {
89                                 xr.MoveToContent ();
90                                 if (xr.LocalName != "FileList") {
91                                         Log.LogMessage (MessageImportance.Low, "Invalid frameworklist '{0}', expected a 'FileList' root element.",
92                                                         framework_list);
93                                         return null;
94                                 }
95
96                                 var framework = new Framework ();
97                                 framework.DisplayName = xr.GetAttribute ("Name");
98                                 string framework_dir = xr.GetAttribute ("TargetFrameworkDirectory");
99                                 if (String.IsNullOrEmpty (framework_dir))
100                                         framework_dir = Path.Combine (redistlist_dir, "..");
101                                 else
102                                         framework_dir = Path.Combine (redistlist_dir, framework_dir);
103
104                                 var directories = new List<string> ();
105                                 directories.Add (MSBuildUtils.FromMSBuildPath (framework_dir));
106
107                                 string include = xr.GetAttribute ("IncludeFramework");
108                                 if (!String.IsNullOrEmpty (include)) {
109                                         var included_framework = GetFrameworkDirectoriesForMoniker (new FrameworkMoniker (moniker.Identifier, include, null));
110
111                                         if (included_framework != null && included_framework.Directories != null)
112                                                 directories.AddRange (included_framework.Directories);
113                                 }
114
115                                 framework.Directories = directories.ToArray ();
116
117                                 return framework;
118                         } catch (XmlException xe) {
119                                 Log.LogWarning ("Error reading framework definition file '{0}': {1}", framework_list, xe.Message);
120                                 Log.LogMessage (MessageImportance.Low, "Error reading framework definition file '{0}': {1}", framework_list,
121                                                 xe.ToString ());
122                                 return null;
123                         } finally {
124                                 if (xr != null)
125                                         ((IDisposable)xr).Dispose ();
126                         }
127                 }
128
129                 bool TryParseTargetFrameworkMoniker (string moniker_literal, out FrameworkMoniker moniker)
130                 {
131                         moniker = null;
132                         if (String.IsNullOrEmpty (moniker_literal))
133                                 throw new ArgumentException ("Empty moniker string");
134
135                         string [] parts = moniker_literal.Split (new char [] {','}, StringSplitOptions.RemoveEmptyEntries);
136
137                         if (parts.Length < 2 || parts.Length > 3) {
138                                 LogInvalidMonikerError (null, moniker_literal);
139                                 return false;
140                         }
141
142                         string identifier = parts [0];
143                         string version = null;
144                         string profile = null;
145
146                         if (!parts [1].StartsWith ("Version=")) {
147                                 LogInvalidMonikerError ("Invalid framework name", moniker_literal);
148                                 return false;
149                         }
150
151                         version = parts [1].Substring (8);
152                         if (String.IsNullOrEmpty (version)) {
153                                 LogInvalidMonikerError ("Invalid framework version", moniker_literal);
154                                 return false;
155                         }
156
157                         if (parts.Length > 2) {
158                                 if (!parts [2].StartsWith ("Profile=")) {
159                                         LogInvalidMonikerError ("Invalid framework version", moniker_literal);
160                                         return false;
161                                 }
162
163                                 profile = parts [2].Substring (8);
164                                 if (String.IsNullOrEmpty (profile)) {
165                                         LogInvalidMonikerError ("Invalid framework profile", moniker_literal);
166                                         return false;
167                                 }
168                         }
169
170                         moniker = new FrameworkMoniker (identifier, version, profile);
171                         return true;
172                 }
173
174                 void LogInvalidMonikerError (string msg, string moniker_literal)
175                 {
176                         if (msg != null)
177                                 Log.LogError ("{0} in the Target Framework Moniker '{1}'. Expected format: 'Identifier,Version=<version>[,Profile=<profile>]'. " +
178                                                         "It should have either 2 or 3 comma separated components.", msg, moniker_literal);
179                         else
180                                 Log.LogError ("Invalid Target Framework Moniker '{0}'. Expected format: 'Identifier,Version=<version>[,Profile=<profile>]'. " +
181                                                         "It should have either 2 or 3 comma separated components.", moniker_literal);
182                 }
183
184                 [Required]
185                 public string TargetFrameworkMoniker { get; set; }
186
187                 public string RootPath { get; set; }
188
189                 public bool BypassFrameworkInstallChecks { get; set; }
190
191                 [Output]
192                 public string TargetFrameworkMonikerDisplayName { get; set; }
193
194                 [Output]
195                 public string[] ReferenceAssemblyPaths { get; set; }
196
197                 [Output]
198                 public string[] FullFrameworkReferenceAssemblyPaths { get; set; }
199
200                 static string DefaultFrameworksBasePath {
201                         get {
202                                 if (framework_base_path == null) {
203                                         // NOTE: code from mcs/tools/gacutil/driver.cs
204                                         PropertyInfo gac = typeof (System.Environment).GetProperty (
205                                                         "GacPath", BindingFlags.Static | BindingFlags.NonPublic);
206
207                                         if (gac != null) {
208                                                 MethodInfo get_gac = gac.GetGetMethod (true);
209                                                 string gac_path = (string) get_gac.Invoke (null, null);
210                                                 framework_base_path = Path.GetFullPath (Path.Combine (
211                                                                         gac_path, Path.Combine ("..", "xbuild-frameworks")));
212                                         }
213                                 }
214                                 return framework_base_path;
215                         }
216                 }
217         }
218
219         class FrameworkMoniker {
220                 public readonly string Identifier;
221                 public readonly string Version;
222                 public readonly string Profile;
223
224                 public FrameworkMoniker (string identifier, string version, string profile)
225                 {
226                         this.Identifier = identifier;
227                         this.Version = version;
228                         this.Profile = profile;
229                 }
230
231                 public override string ToString ()
232                 {
233                         if (String.IsNullOrEmpty (Profile))
234                                 return String.Format ("{0},Version={1}", Identifier, Version);
235                         return  String.Format ("{0},Version={1},Profile={2}", Identifier, Version, Profile);
236                 }
237         }
238
239         class Framework {
240                 public string Moniker;
241                 public string[] Directories;
242                 public string DisplayName;
243         }
244 }
245
246 #endif