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