/
AndroidSdkWindows.cs
343 lines (290 loc) · 14.2 KB
/
AndroidSdkWindows.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
namespace Xamarin.Android.Tools
{
class AndroidSdkWindows : AndroidSdkBase
{
const string MDREG_KEY = @"SOFTWARE\Novell\Mono for Android";
const string MDREG_ANDROID_SDK = "AndroidSdkDirectory";
const string MDREG_ANDROID_NDK = "AndroidNdkDirectory";
const string MDREG_JAVA_SDK = "JavaSdkDirectory";
const string ANDROID_INSTALLER_PATH = @"SOFTWARE\Android SDK Tools";
const string ANDROID_INSTALLER_KEY = "Path";
const string XAMARIN_ANDROID_INSTALLER_PATH = @"SOFTWARE\Xamarin\MonoAndroid";
const string XAMARIN_ANDROID_INSTALLER_KEY = "PrivateAndroidSdkPath";
public AndroidSdkWindows (Action<TraceLevel, string> logger)
: base (logger)
{
}
static readonly string _JarSigner = "jarsigner.exe";
public override string ZipAlign { get; protected set; } = "zipalign.exe";
public override string JarSigner { get; protected set; } = _JarSigner;
public override string KeyTool { get; protected set; } = "keytool.exe";
public override string NdkHostPlatform32Bit { get { return "windows"; } }
public override string NdkHostPlatform64Bit { get { return "windows-x86_64"; } }
public override string Javac { get; protected set; } = "javac.exe";
public override string PreferedAndroidSdkPath {
get {
var wow = RegistryEx.Wow64.Key32;
var regKey = GetMDRegistryKey ();
if (CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_SDK, wow, "platform-tools", Adb))
return RegistryEx.GetValueString (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_SDK, wow);
return null;
}
}
public override string PreferedAndroidNdkPath {
get {
var wow = RegistryEx.Wow64.Key32;
var regKey = GetMDRegistryKey ();
if (CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_NDK, wow, ".", NdkStack))
return RegistryEx.GetValueString (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_NDK, wow);
return null;
}
}
public override string PreferedJavaSdkPath {
get {
var wow = RegistryEx.Wow64.Key32;
var regKey = GetMDRegistryKey ();
if (CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, regKey, MDREG_JAVA_SDK, wow, "bin", JarSigner))
return RegistryEx.GetValueString (RegistryEx.CurrentUser, regKey, MDREG_JAVA_SDK, wow);
return null;
}
}
static string GetMDRegistryKey ()
{
var regKey = Environment.GetEnvironmentVariable ("XAMARIN_ANDROID_REGKEY");
return string.IsNullOrWhiteSpace (regKey) ? MDREG_KEY : regKey;
}
protected override IEnumerable<string> GetAllAvailableAndroidSdks ()
{
var roots = new[] { RegistryEx.CurrentUser, RegistryEx.LocalMachine };
var wow = RegistryEx.Wow64.Key32;
var regKey = GetMDRegistryKey ();
Logger (TraceLevel.Info, "Looking for Android SDK...");
// Check for the key the user gave us in the VS/addin options
foreach (var root in roots)
if (CheckRegistryKeyForExecutable (root, regKey, MDREG_ANDROID_SDK, wow, "platform-tools", Adb))
yield return RegistryEx.GetValueString (root, regKey, MDREG_ANDROID_SDK, wow);
// Check for the key written by the Xamarin installer
if (CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, XAMARIN_ANDROID_INSTALLER_PATH, XAMARIN_ANDROID_INSTALLER_KEY, wow, "platform-tools", Adb))
yield return RegistryEx.GetValueString (RegistryEx.CurrentUser, XAMARIN_ANDROID_INSTALLER_PATH, XAMARIN_ANDROID_INSTALLER_KEY, wow);
// Check for the key written by the Android SDK installer
foreach (var root in roots)
if (CheckRegistryKeyForExecutable (root, ANDROID_INSTALLER_PATH, ANDROID_INSTALLER_KEY, wow, "platform-tools", Adb))
yield return RegistryEx.GetValueString (root, ANDROID_INSTALLER_PATH, ANDROID_INSTALLER_KEY, wow);
// Check some hardcoded paths for good measure
var paths = new string [] {
Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData), "Xamarin", "MonoAndroid", "android-sdk-windows"),
Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "Android", "android-sdk"),
Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "Android", "android-sdk-windows"),
!string.IsNullOrEmpty (Environment.GetEnvironmentVariable ("ProgramW6432"))
? Path.Combine (Environment.GetEnvironmentVariable ("ProgramW6432"), "Android", "android-sdk")
: Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFiles), "Android", "android-sdk"),
Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData), "Android", "android-sdk"),
Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Android", "android-sdk"),
@"C:\android-sdk-windows"
};
foreach (var basePath in paths)
if (Directory.Exists (basePath))
if (ValidateAndroidSdkLocation (basePath))
yield return basePath;
}
protected override string GetJavaSdkPath ()
{
var jdk = GetJdkInfos (Logger).FirstOrDefault ();
return jdk?.HomePath;
}
internal static IEnumerable<JdkInfo> GetJdkInfos (Action<TraceLevel, string> logger)
{
JdkInfo TryGetJdkInfo (string path, string locator)
{
JdkInfo jdk = null;
try {
jdk = new JdkInfo (path, locator);
}
catch (Exception e) {
logger (TraceLevel.Warning, $"Not a valid JDK directory: `{path}`; via category: {locator}");
logger (TraceLevel.Verbose, e.ToString ());
}
return jdk;
}
IEnumerable<JdkInfo> ToJdkInfos (IEnumerable<string> paths, string locator)
{
return paths.Select (p => TryGetJdkInfo (p, locator))
.Where (jdk => jdk != null)
.OrderByDescending (jdk => jdk, JdkInfoVersionComparer.Default);
}
return ToJdkInfos (GetPreferredJdkPaths (), "Preferred Registry")
.Concat (ToJdkInfos (GetOpenJdkPaths (), "OpenJDK"))
.Concat (ToJdkInfos (GetKnownOpenJdkPaths (), "Well-known OpenJDK paths"))
.Concat (ToJdkInfos (GetOracleJdkPaths (), "Oracle JDK"))
.Concat (ToJdkInfos (GetEnvironmentJdkPaths (), "Environment Variables"));
}
private static IEnumerable<string> GetEnvironmentJdkPaths ()
{
var environment = new [] { "JAVA_HOME" };
foreach (var key in environment) {
var value = Environment.GetEnvironmentVariable (key);
if (!string.IsNullOrEmpty (value)) {
yield return value;
}
}
}
private static IEnumerable<string> GetPreferredJdkPaths ()
{
// check the user specified path
var roots = new[] { RegistryEx.CurrentUser, RegistryEx.LocalMachine };
const RegistryEx.Wow64 wow = RegistryEx.Wow64.Key32;
var regKey = GetMDRegistryKey ();
foreach (var root in roots) {
if (CheckRegistryKeyForExecutable (root, regKey, MDREG_JAVA_SDK, wow, "bin", _JarSigner))
yield return RegistryEx.GetValueString (root, regKey, MDREG_JAVA_SDK, wow);
}
}
private static IEnumerable<string> GetOpenJdkPaths ()
{
var root = RegistryEx.LocalMachine;
var wows = new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 };
var subKey = @"SOFTWARE\Microsoft\VisualStudio\Android";
var valueName = "JavaHome";
foreach (var wow in wows) {
if (CheckRegistryKeyForExecutable (root, subKey, valueName, wow, "bin", _JarSigner))
yield return RegistryEx.GetValueString (root, subKey, valueName, wow);
}
}
/// <summary>
/// Locate OpenJDK installations by well known path.
/// </summary>
/// <returns>List of valid OpenJDK paths in version descending order.</returns>
private static IEnumerable<string> GetKnownOpenJdkPaths ()
{
string JdkFolderNamePattern = "microsoft_dist_openjdk_";
var paths = new List<Tuple<string, Version>> ();
var rootPaths = new List<string> {
Path.Combine (Environment.ExpandEnvironmentVariables ("%ProgramW6432%"), "Android", "jdk"),
Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.ProgramFilesX86), "Android", "jdk"),
};
foreach (var rootPath in rootPaths) {
if (Directory.Exists (rootPath)) {
foreach (var directoryName in Directory.EnumerateDirectories (rootPath, $"{JdkFolderNamePattern}*").ToList ()) {
var versionString = directoryName.Replace ($"{rootPath}\\{JdkFolderNamePattern}", string.Empty);
if (Version.TryParse (versionString, out Version ver)) {
paths.Add (new Tuple<string, Version>(directoryName, ver));
}
}
}
}
return paths.OrderByDescending (v => v.Item2)
.Where (openJdk => ProcessUtils.FindExecutablesInDirectory (Path.Combine (openJdk.Item1, "bin"), _JarSigner).Any ())
.Select (openJdk => openJdk.Item1);
}
private static IEnumerable<string> GetOracleJdkPaths ()
{
string subkey = @"SOFTWARE\JavaSoft\Java Development Kit";
foreach (var wow64 in new[] { RegistryEx.Wow64.Key32, RegistryEx.Wow64.Key64 }) {
string key_name = string.Format (@"{0}\{1}\{2}", "HKLM", subkey, "CurrentVersion");
var currentVersion = RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey, "CurrentVersion", wow64);
if (!string.IsNullOrEmpty (currentVersion)) {
// No matter what the CurrentVersion is, look for 1.6 or 1.7 or 1.8
if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64, "bin", _JarSigner))
yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.8", "JavaHome", wow64);
if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.7", "JavaHome", wow64, "bin", _JarSigner))
yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.7", "JavaHome", wow64);
if (CheckRegistryKeyForExecutable (RegistryEx.LocalMachine, subkey + "\\" + "1.6", "JavaHome", wow64, "bin", _JarSigner))
yield return RegistryEx.GetValueString (RegistryEx.LocalMachine, subkey + "\\" + "1.6", "JavaHome", wow64);
}
}
}
protected override IEnumerable<string> GetAllAvailableAndroidNdks ()
{
var roots = new[] { RegistryEx.CurrentUser, RegistryEx.LocalMachine };
var wow = RegistryEx.Wow64.Key32;
var regKey = GetMDRegistryKey ();
Logger (TraceLevel.Info, "Looking for Android NDK...");
// Check for the "ndk-bundle" directory inside the SDK directories
string ndk;
var sdks = GetAllAvailableAndroidSdks().ToList();
if (!string.IsNullOrEmpty(AndroidSdkPath))
sdks.Add(AndroidSdkPath);
foreach(var sdk in sdks.Distinct())
if (Directory.Exists(ndk = Path.Combine(sdk, "ndk-bundle")))
if (ValidateAndroidNdkLocation(ndk))
yield return ndk;
// Check for the key the user gave us in the VS/addin options
foreach (var root in roots)
if (CheckRegistryKeyForExecutable (root, regKey, MDREG_ANDROID_NDK, wow, ".", NdkStack))
yield return RegistryEx.GetValueString (root, regKey, MDREG_ANDROID_NDK, wow);
/*
// Check for the key written by the Xamarin installer
if (CheckRegistryKeyForExecutable (RegistryEx.CurrentUser, XAMARIN_ANDROID_INSTALLER_PATH, XAMARIN_ANDROID_INSTALLER_KEY, wow, "platform-tools", Adb))
yield return RegistryEx.GetValueString (RegistryEx.CurrentUser, XAMARIN_ANDROID_INSTALLER_PATH, XAMARIN_ANDROID_INSTALLER_KEY, wow);
*/
// Check some hardcoded paths for good measure
var xamarin_private = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.LocalApplicationData), "Xamarin", "MonoAndroid");
var vs_default = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Microsoft", "AndroidNDK");
var vs_default32bit = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Microsoft", "AndroidNDK32");
var vs_2017_default = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData), "Microsoft", "AndroidNDK64");
var android_default = Path.Combine (OS.ProgramFilesX86, "Android");
var cdrive_default = @"C:\";
foreach (var basePath in new string [] {xamarin_private, android_default, vs_default, vs_default32bit, vs_2017_default, cdrive_default})
if (Directory.Exists (basePath))
foreach (var dir in Directory.GetDirectories (basePath, "android-ndk-r*"))
if (ValidateAndroidNdkLocation (dir))
yield return dir;
}
protected override string GetShortFormPath (string path)
{
return KernelEx.GetShortPathName (path);
}
public override void SetPreferredAndroidSdkPath (string path)
{
var regKey = GetMDRegistryKey ();
RegistryEx.SetValueString (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_SDK, path ?? "", RegistryEx.Wow64.Key32);
}
public override void SetPreferredJavaSdkPath (string path)
{
var regKey = GetMDRegistryKey ();
RegistryEx.SetValueString (RegistryEx.CurrentUser, regKey, MDREG_JAVA_SDK, path ?? "", RegistryEx.Wow64.Key32);
}
public override void SetPreferredAndroidNdkPath (string path)
{
var regKey = GetMDRegistryKey ();
RegistryEx.SetValueString (RegistryEx.CurrentUser, regKey, MDREG_ANDROID_NDK, path ?? "", RegistryEx.Wow64.Key32);
}
#region Helper Methods
private static bool CheckRegistryKeyForExecutable (UIntPtr key, string subkey, string valueName, RegistryEx.Wow64 wow64, string subdir, string exe)
{
string key_name = string.Format (@"{0}\{1}\{2}", key == RegistryEx.CurrentUser ? "HKCU" : "HKLM", subkey, valueName);
var path = NullIfEmpty (RegistryEx.GetValueString (key, subkey, valueName, wow64));
if (path == null) {
return false;
}
if (!ProcessUtils.FindExecutablesInDirectory (Path.Combine (path, subdir), exe).Any ()) {
return false;
}
return true;
}
#endregion
public override void Initialize (string androidSdkPath = null, string androidNdkPath = null, string javaSdkPath = null)
{
base.Initialize (androidSdkPath, androidNdkPath, javaSdkPath);
var jdkPath = JavaSdkPath;
if (!string.IsNullOrEmpty (jdkPath)) {
var cur = Environment.GetEnvironmentVariable ("JAVA_HOME");
if (!string.IsNullOrEmpty (cur))
Environment.SetEnvironmentVariable ("JAVA_HOME", jdkPath);
var javaBinPath = this.JavaBinPath;
if (!string.IsNullOrEmpty (javaBinPath)) {
var environmentPath = Environment.GetEnvironmentVariable ("PATH");
if (!environmentPath.Contains (javaBinPath)) {
var processPath = string.Concat (javaBinPath, Path.PathSeparator, environmentPath);
Environment.SetEnvironmentVariable ("PATH", processPath);
}
}
}
}
}
}