Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mcs / class / Microsoft.Build.Tasks / Microsoft.Build.Tasks / GetReferenceAssemblyPaths.cs
1 //
2 // GetReferenceAssemblyPaths.cs: Gets the target framework directories corresponding
3 // to target framework moniker
4 //
5 // Author:
6 //   Ankit Jain (jankit@novell.com)
7 //
8 // Copyright 2011 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
29 using System;
30 using Microsoft.Build.Framework;
31 using System.IO;
32 using System.Reflection;
33 using System.Xml;
34 using System.Collections.Generic;
35
36 using Mono.XBuild.Utilities;
37
38
39 namespace Microsoft.Build.Tasks
40 {
41         public class GetReferenceAssemblyPaths : TaskExtension
42         {
43                 static string framework_base_path;
44                 static string PathSeparatorAsString = Path.PathSeparator.ToString ();
45                 const string MacOSXExternalXBuildDir = "/Library/Frameworks/Mono.framework/External/xbuild-frameworks";
46
47                 public GetReferenceAssemblyPaths ()
48                 {
49                 }
50
51                 public override bool Execute ()
52                 {
53                         FrameworkMoniker moniker = null;
54                         if (!TryParseTargetFrameworkMoniker (TargetFrameworkMoniker, out moniker))
55                                 return false;
56
57                         var framework = GetFrameworkDirectoriesForMoniker (moniker);
58                         if (framework == null) {
59                                 Log.LogWarning ("Unable to find framework corresponding to the target framework moniker '{0}'. " +
60                                                 "Framework assembly references will be resolved from the GAC, which might not be " +
61                                                 "the intended behavior.", TargetFrameworkMoniker);
62                                 if (moniker.Identifier.Equals (".NETPortable"))
63                                         return CheckPclReferenceAssemblies (moniker);
64                                 return true;
65                         }
66
67                         ReferenceAssemblyPaths = FullFrameworkReferenceAssemblyPaths = framework.Directories;
68                         TargetFrameworkMonikerDisplayName = framework.DisplayName;
69
70                         return true;
71                 }
72
73                 bool CheckPclReferenceAssemblies (FrameworkMoniker moniker)
74                 {
75                         // Check for a supported profile
76                         var check = new FrameworkMoniker (".NETPortable", "v4.0", "Profile24");
77                         if (GetFrameworkDirectoriesForMoniker (check) != null)
78                                 Log.LogError ("Unsupported PCL Profile '{0}'.", moniker);
79                         else
80                                 Log.LogError ("PCL Reference Assemblies not installed.");
81                         return false;
82                 }
83
84                 Framework GetFrameworkDirectoriesForMoniker (FrameworkMoniker moniker)
85                 {
86                         string dirs = String.Join (PathSeparatorAsString, new string [] {
87                                                         Environment.GetEnvironmentVariable ("XBUILD_FRAMEWORK_FOLDERS_PATH") ?? String.Empty,
88                                                         MSBuildUtils.RunningOnMac ? MacOSXExternalXBuildDir : String.Empty,
89                                                         RootPath,
90                                                         DefaultFrameworksBasePath });
91
92                         string [] paths = dirs.Split (new char [] {Path.PathSeparator}, StringSplitOptions.RemoveEmptyEntries);
93                         foreach (string path in paths) {
94                                 var framework = GetFrameworkDirectoriesForMoniker (moniker, path);
95                                 if (framework != null)
96                                         return framework;
97                         }
98
99                         return null;
100                 }
101
102                 //@base_path must be absolute
103                 Framework GetFrameworkDirectoriesForMoniker (FrameworkMoniker moniker, string base_path)
104                 {
105                         if (String.IsNullOrEmpty (base_path)) {
106                                 Log.LogMessage (MessageImportance.Low, "Invalid *empty* base path, ignoring. " + Environment.StackTrace);
107                                 return null;
108                         }
109
110                         Log.LogMessage (MessageImportance.Low, "Looking for framework '{0}' in root path '{1}'",
111                                         moniker, base_path);
112                         string framework_path = Path.Combine (base_path, Path.Combine (moniker.Identifier, moniker.Version));
113                         if (!String.IsNullOrEmpty (moniker.Profile))
114                                 framework_path = Path.Combine (framework_path, "Profile", moniker.Profile);
115
116                         string redistlist_dir = Path.Combine (framework_path, "RedistList");
117                         string framework_list = Path.Combine (redistlist_dir, "FrameworkList.xml");
118                         if (!File.Exists (framework_list)) {
119                                 Log.LogMessage (MessageImportance.Low,
120                                                         "Unable to find framework definition file '{0}' for Target Framework Moniker '{1}'",
121                                                         framework_list, moniker);
122                                 return null;
123                         }
124
125                         Log.LogMessage (MessageImportance.Low, "Found framework definition list '{0}' for framework '{1}'",
126                                         framework_list, moniker);
127                         XmlReader xr = XmlReader.Create (framework_list);
128                         try {
129                                 xr.MoveToContent ();
130                                 if (xr.LocalName != "FileList") {
131                                         Log.LogMessage (MessageImportance.Low, "Invalid frameworklist '{0}', expected a 'FileList' root element.",
132                                                         framework_list);
133                                         return null;
134                                 }
135
136                                 var framework = new Framework ();
137                                 framework.DisplayName = xr.GetAttribute ("Name");
138                                 string framework_dir = xr.GetAttribute ("TargetFrameworkDirectory");
139                                 if (String.IsNullOrEmpty (framework_dir))
140                                         framework_dir = Path.Combine (redistlist_dir, "..");
141                                 else
142                                         framework_dir = Path.Combine (redistlist_dir, framework_dir);
143
144                                 var directories = new List<string> ();
145
146                                 //MSBuild has a trailing slash on this value
147                                 directories.Add (MSBuildUtils.FromMSBuildPath (framework_dir) + Path.DirectorySeparatorChar);
148
149                                 string include = xr.GetAttribute ("IncludeFramework");
150                                 if (!String.IsNullOrEmpty (include)) {
151                                         var included_framework = GetFrameworkDirectoriesForMoniker (new FrameworkMoniker (moniker.Identifier, include, null));
152
153                                         if (included_framework != null && included_framework.Directories != null)
154                                                 directories.AddRange (included_framework.Directories);
155                                 }
156
157                                 framework.Directories = directories.ToArray ();
158
159                                 return framework;
160                         } catch (XmlException xe) {
161                                 Log.LogWarning ("Error reading framework definition file '{0}': {1}", framework_list, xe.Message);
162                                 Log.LogMessage (MessageImportance.Low, "Error reading framework definition file '{0}': {1}", framework_list,
163                                                 xe.ToString ());
164                                 return null;
165                         } finally {
166                                 if (xr != null)
167                                         ((IDisposable)xr).Dispose ();
168                         }
169                 }
170
171                 bool TryParseTargetFrameworkMoniker (string moniker_literal, out FrameworkMoniker moniker)
172                 {
173                         moniker = null;
174                         if (String.IsNullOrEmpty (moniker_literal))
175                                 throw new ArgumentException ("Empty moniker string");
176
177                         string [] parts = moniker_literal.Split (new char [] {','}, StringSplitOptions.RemoveEmptyEntries);
178
179                         if (parts.Length < 2 || parts.Length > 3) {
180                                 LogInvalidMonikerError (null, moniker_literal);
181                                 return false;
182                         }
183
184                         string identifier = parts [0];
185                         string version = null;
186                         string profile = null;
187
188                         if (!parts [1].StartsWith ("Version=")) {
189                                 LogInvalidMonikerError ("Invalid framework name", moniker_literal);
190                                 return false;
191                         }
192
193                         version = parts [1].Substring (8);
194                         if (String.IsNullOrEmpty (version)) {
195                                 LogInvalidMonikerError ("Invalid framework version", moniker_literal);
196                                 return false;
197                         }
198
199                         if (parts.Length > 2) {
200                                 if (!parts [2].StartsWith ("Profile=")) {
201                                         LogInvalidMonikerError ("Invalid framework version", moniker_literal);
202                                         return false;
203                                 }
204
205                                 profile = parts [2].Substring (8);
206                                 if (String.IsNullOrEmpty (profile)) {
207                                         LogInvalidMonikerError ("Invalid framework profile", moniker_literal);
208                                         return false;
209                                 }
210                         }
211
212                         moniker = new FrameworkMoniker (identifier, version, profile);
213                         return true;
214                 }
215
216                 void LogInvalidMonikerError (string msg, string moniker_literal)
217                 {
218                         if (msg != null)
219                                 Log.LogError ("{0} in the Target Framework Moniker '{1}'. Expected format: 'Identifier,Version=<version>[,Profile=<profile>]'. " +
220                                                         "It should have either 2 or 3 comma separated components.", msg, moniker_literal);
221                         else
222                                 Log.LogError ("Invalid Target Framework Moniker '{0}'. Expected format: 'Identifier,Version=<version>[,Profile=<profile>]'. " +
223                                                         "It should have either 2 or 3 comma separated components.", moniker_literal);
224                 }
225
226                 public string TargetFrameworkMoniker { get; set; }
227
228                 public string RootPath { get; set; }
229
230                 public bool BypassFrameworkInstallChecks { get; set; }
231
232                 [Output]
233                 public string TargetFrameworkMonikerDisplayName { get; set; }
234
235                 [Output]
236                 public string[] ReferenceAssemblyPaths { get; private set; }
237
238                 [Output]
239                 public string[] FullFrameworkReferenceAssemblyPaths { get; private set; }
240
241                 static string DefaultFrameworksBasePath {
242                         get {
243                                 if (framework_base_path == null)
244                                         framework_base_path = Path.Combine (Path.GetDirectoryName (typeof (object).Assembly.Location),
245                                                                 Path.Combine ("..", "xbuild-frameworks"));
246                                 return framework_base_path;
247                         }
248                 }
249         }
250
251         class FrameworkMoniker {
252                 public readonly string Identifier;
253                 public readonly string Version;
254                 public readonly string Profile;
255
256                 public FrameworkMoniker (string identifier, string version, string profile)
257                 {
258                         this.Identifier = identifier;
259                         this.Version = version;
260                         this.Profile = profile;
261                 }
262
263                 public override string ToString ()
264                 {
265                         if (String.IsNullOrEmpty (Profile))
266                                 return String.Format ("{0},Version={1}", Identifier, Version);
267                         return  String.Format ("{0},Version={1},Profile={2}", Identifier, Version, Profile);
268                 }
269         }
270
271         class Framework {
272                 public string[] Directories;
273                 public string DisplayName;
274         }
275 }
276