Skip to content

Commit b095ef0

Browse files
committed
Take BUILD_NUMBER into consideration for Version sorting
Fixes: http://work.devdiv.io/661401 The version numbers which the Microsoft OpenJDK packages provide was not consistent with what `JdkInfo` expected: the `release` file would have a `JAVA_VERSION` value which was a three-tuple, and didn't include the `BUILD_NUMBER` value. For example: JAVA_VERSION="1.8.0" BUILD_NUMBER=7 Unfortunately, `JdkInfo.GetJavaVersion()` was only taking `JAVA_VERSION` into consideration, so when multiple different OpenJDK packages were processed which all had the *same* `JAVA_VERSION` value but *different* `BUILD_NUMBER` values, `JdkInfo.GetMacOSMicrosoftJdks()` returned the "wrong" one first. (In actuality, the one it returned first was not knowable, and was probably whatever `Directory.EnumerateDirectories()` returned first.) Simple enough, we just need to take `BUILD_NUMBER` into consideration as part of constructing `JdkInfo.Version`, right? Not so fast. Turns Out™ that the version value held within `JAVA_VERSION` or the `java.version` property -- which need not be identical! -- can also contain `-`, not just `_`. A "Java Version" is *really" (at least) 4 optional parts: JAVA_VERSION : VERSION ('_' UPDATE)? ('-' MILESTONE)? ('-' SUFFIX)? Which means Java version values such as `1.8.0_1-9-microsoft-b00` are possible, from various different locations, e.g. the `version` value vs. the `build` value in `java -version` output: $ bin/java -version openjdk version "1.8.0-9" OpenJDK Runtime Environment (build 1.8.0-9-microsoft-b00) OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode) Instead of trying to top-down parse a version number, go for a "bottom-up" parsing: 1. Replace all *non-digit* characters with `.` 2. Split the value on (1) on `.`, creating an array, and remove all empty values. 3. Separately parse the values in (2) as part of `System.Version` construction. This allows at least a sane-ish, plausible, `Version` construction. `1.8.0_161-b12` will be parsed as `new Version(1, 8, 0, 161)` (as we'll just grab up to the first four values), and we'll gracefully ignore any other non-digit characters in the string. This allows us to better construct a `Version` value for Microsoft OpenJDK packages, allowing us in turn to *sort* the packages, which allows `JdkInfo>GetMacOSMicrosoftJdks()` to return the highest version *first*, as is desired.
1 parent d3de054 commit b095ef0

File tree

4 files changed

+130
-32
lines changed

4 files changed

+130
-32
lines changed

src/Xamarin.Android.Tools.AndroidSdk/AndroidSdkInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ internal static void DefaultConsoleLogger (TraceLevel level, string message)
151151
Console.Error.WriteLine (message);
152152
break;
153153
default:
154-
Console.WriteLine (message);
154+
Console.WriteLine ($"[{level}] {message}");
155155
break;
156156
}
157157
}

src/Xamarin.Android.Tools.AndroidSdk/JdkInfo.cs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Collections.ObjectModel;
44
using System.Diagnostics;
5+
using System.Globalization;
56
using System.IO;
67
using System.Linq;
78
using System.Text;
@@ -53,7 +54,7 @@ public JdkInfo (string homePath)
5354
JavaPath = ProcessUtils.FindExecutablesInDirectory (binPath, "java").FirstOrDefault ();
5455
JavacPath = ProcessUtils.FindExecutablesInDirectory (binPath, "javac").FirstOrDefault ();
5556
JdkJvmPath = OS.IsMac
56-
? FindLibrariesInDirectory (HomePath, "jli").FirstOrDefault ()
57+
? FindLibrariesInDirectory (Path.Combine (HomePath, "jre"), "jli").FirstOrDefault ()
5758
: FindLibrariesInDirectory (Path.Combine (HomePath, "jre"), "jvm").FirstOrDefault ();
5859

5960
ValidateFile ("jar", JarPath);
@@ -115,6 +116,8 @@ public bool GetJavaSettingsPropertyValue (string key, out string value)
115116

116117
static IEnumerable<string> FindLibrariesInDirectory (string dir, string libraryName)
117118
{
119+
if (!Directory.Exists (dir))
120+
return Enumerable.Empty<string> ();
118121
var library = string.Format (OS.NativeLibraryFormat, libraryName);
119122
return Directory.EnumerateFiles (dir, library, SearchOption.AllDirectories);
120123
}
@@ -125,29 +128,44 @@ void ValidateFile (string name, string path)
125128
throw new ArgumentException ($"Could not find required file `{name}` within `{HomePath}`; is this a valid JDK?", "homePath");
126129
}
127130

128-
static Regex VersionExtractor = new Regex (@"(?<version>[\d]+(\.\d+)+)(_(?<patch>\d+))?", RegexOptions.Compiled);
131+
static Regex NonDigitMatcher = new Regex (@"[^\d]");
129132

130133
Version GetJavaVersion ()
131134
{
132135
string version = null;
133-
if (!ReleaseProperties.TryGetValue ("JAVA_VERSION", out version)) {
134-
if (GetJavaSettingsPropertyValue ("java.version", out string vs))
135-
version = vs;
136+
if (ReleaseProperties.TryGetValue ("JAVA_VERSION", out version) && !string.IsNullOrEmpty (version)) {
137+
version = GetParsableVersion (version);
138+
if (ReleaseProperties.TryGetValue ("BUILD_NUMBER", out var build) && !string.IsNullOrEmpty (build))
139+
version += "." + build;
140+
}
141+
else if (GetJavaSettingsPropertyValue ("java.version", out version) && !string.IsNullOrEmpty (version)) {
142+
version = GetParsableVersion (version);
143+
}
144+
if (string.IsNullOrEmpty (version))
145+
throw new NotSupportedException ("Could not determine Java version.");
146+
var normalizedVersion = NonDigitMatcher.Replace (version, ".");
147+
var versionParts = normalizedVersion.Split (new[]{"."}, StringSplitOptions.RemoveEmptyEntries);
148+
149+
try {
150+
if (versionParts.Length < 2)
151+
return null;
152+
if (versionParts.Length == 2)
153+
return new Version (major: int.Parse (versionParts [0]), minor: int.Parse (versionParts [1]));
154+
if (versionParts.Length == 3)
155+
return new Version (major: int.Parse (versionParts [0]), minor: int.Parse (versionParts [1]), build: int.Parse (versionParts [2]));
156+
// We just ignore elements 4+
157+
return new Version (major: int.Parse (versionParts [0]), minor: int.Parse (versionParts [1]), build: int.Parse (versionParts [2]), revision: int.Parse (versionParts [3]));
136158
}
137-
if (version == null)
138-
throw new NotSupportedException ("Could not determine Java version");
139-
var m = VersionExtractor.Match (version);
140-
if (!m.Success)
159+
catch (Exception) {
141160
return null;
142-
version = m.Groups ["version"].Value;
143-
var patch = m.Groups ["patch"].Value;
144-
if (!string.IsNullOrEmpty (patch))
145-
version += "." + patch;
161+
}
162+
}
163+
164+
static string GetParsableVersion (string version)
165+
{
146166
if (!version.Contains ("."))
147167
version += ".0";
148-
if (Version.TryParse (version, out Version v))
149-
return v;
150-
return null;
168+
return version;
151169
}
152170

153171
ReadOnlyDictionary<string, string> GetReleaseProperties ()

src/Xamarin.Android.Tools.AndroidSdk/Tests/AndroidSdkInfoTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public void Constructor_Paths ()
7575
public void Constructor_SetValuesFromPath ()
7676
{
7777
CreateSdks (out string root, out string jdk, out string ndk, out string sdk);
78-
JdkInfoTests.CreateFauxJdk (jdk, "1.8.0");
78+
JdkInfoTests.CreateFauxJdk (jdk, releaseVersion: "1.8.0", releaseBuildNumber: "42", javaVersion: "100.100.100_100");
7979

8080
Action<TraceLevel, string> logger = (level, message) => {
8181
Console.WriteLine ($"[{level}] {message}");
@@ -267,8 +267,8 @@ public void DetectAndSetPreferredJavaSdkPathToLatest ()
267267
return;
268268
}
269269
Assert.Throws<NotSupportedException>(() => AndroidSdkInfo.DetectAndSetPreferredJavaSdkPathToLatest (logger));
270-
var newJdkPath = Path.Combine (PreferredJdksOverridePath, "microsoft_dist_openjdk_1.8.999");
271-
JdkInfoTests.CreateFauxJdk (newJdkPath, "1.8.999");
270+
var newJdkPath = Path.Combine (PreferredJdksOverridePath, "microsoft_dist_openjdk_1.8.999.9");
271+
JdkInfoTests.CreateFauxJdk (newJdkPath, releaseVersion: "1.8.999", releaseBuildNumber: "9", javaVersion: "1.8.999-9");
272272

273273
if (File.Exists (UnixConfigPath))
274274
File.Move (UnixConfigPath, backupConfig);

src/Xamarin.Android.Tools.AndroidSdk/Tests/JdkInfoTests.cs

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,25 @@ public void CreateFauxJdk ()
3535
var dir = Path.GetTempFileName();
3636
File.Delete (dir);
3737

38-
CreateFauxJdk (dir, "1.2.3.4");
38+
CreateFauxJdk (dir, releaseVersion: "1.2.3", releaseBuildNumber: "42", javaVersion: "100.100.100-100");
3939

4040
FauxJdkDir = dir;
4141
}
4242

43-
internal static void CreateFauxJdk (string dir, string version)
43+
internal static void CreateFauxJdk (string dir, string releaseVersion, string releaseBuildNumber, string javaVersion)
4444
{
4545
Directory.CreateDirectory (dir);
4646

4747
using (var release = new StreamWriter (Path.Combine (dir, "release"))) {
48-
release.WriteLine ($"JAVA_VERSION=\"{version}\"");
49-
release.WriteLine ($"BUILD_NUMBER=42");
48+
release.WriteLine ($"JAVA_VERSION=\"{releaseVersion}\"");
49+
release.WriteLine ($"BUILD_NUMBER={releaseBuildNumber}");
5050
release.WriteLine ($"JUST_A_KEY");
5151
}
5252

5353
var bin = Path.Combine (dir, "bin");
5454
var inc = Path.Combine (dir, "include");
55-
var jli = Path.Combine (dir, "jli");
5655
var jre = Path.Combine (dir, "jre");
56+
var jli = Path.Combine (jre, "lib", "jli");
5757

5858
Directory.CreateDirectory (bin);
5959
Directory.CreateDirectory (inc);
@@ -65,15 +65,15 @@ internal static void CreateFauxJdk (string dir, string version)
6565
$"echo Property settings:{Environment.NewLine}" +
6666
$"echo \" java.home = {dir}\"{Environment.NewLine}" +
6767
$"echo \" java.vendor = Xamarin.Android Unit Tests\"{Environment.NewLine}" +
68-
$"echo \" java.version = 100.100.100\"{Environment.NewLine}" +
68+
$"echo \" java.version = {javaVersion}\"{Environment.NewLine}" +
6969
$"echo \" xamarin.multi-line = line the first\"{Environment.NewLine}" +
7070
$"echo \" line the second\"{Environment.NewLine}" +
7171
$"echo \" .\"{Environment.NewLine}";
7272

7373
CreateShellScript (Path.Combine (bin, "jar"), "");
7474
CreateShellScript (Path.Combine (bin, "java"), java);
7575
CreateShellScript (Path.Combine (bin, "javac"), "");
76-
CreateShellScript (Path.Combine (dir, "jli", "libjli.dylib"), "");
76+
CreateShellScript (Path.Combine (jli, "libjli.dylib"), "");
7777
CreateShellScript (Path.Combine (jre, "libjvm.so"), "");
7878
CreateShellScript (Path.Combine (jre, "jvm.dll"), "");
7979
}
@@ -127,9 +127,9 @@ public void PathPropertyValidation ()
127127
public void VersionPrefersRelease ()
128128
{
129129
var jdk = new JdkInfo (FauxJdkDir);
130-
// Note: `release` has JAVA_VERSION=1.2.3.4, while `java` prints java.version=100.100.100.
131-
// We prefer the value within `releas`.
132-
Assert.AreEqual (jdk.Version, new Version ("1.2.3.4"));
130+
// Note: `release` has JAVA_VERSION=1.2.3 + BUILD_NUMBER=42, while `java` prints java.version=100.100.100.
131+
// We prefer the value constructed from `release`.
132+
Assert.AreEqual (jdk.Version, new Version ("1.2.3.42"));
133133
}
134134

135135
[Test]
@@ -138,7 +138,7 @@ public void ReleaseProperties ()
138138
var jdk = new JdkInfo (FauxJdkDir);
139139

140140
Assert.AreEqual (3, jdk.ReleaseProperties.Count);
141-
Assert.AreEqual ("1.2.3.4", jdk.ReleaseProperties ["JAVA_VERSION"]);
141+
Assert.AreEqual ("1.2.3", jdk.ReleaseProperties ["JAVA_VERSION"]);
142142
Assert.AreEqual ("42", jdk.ReleaseProperties ["BUILD_NUMBER"]);
143143
Assert.AreEqual ("", jdk.ReleaseProperties ["JUST_A_KEY"]);
144144
}
@@ -157,7 +157,7 @@ public void JavaSettingsProperties ()
157157
Assert.AreEqual (FauxJdkDir, home);
158158

159159
Assert.IsTrue (jdk.GetJavaSettingsPropertyValue ("java.version", out var version));
160-
Assert.AreEqual ("100.100.100", version);
160+
Assert.AreEqual ("100.100.100-100", version);
161161

162162
Assert.IsTrue (jdk.GetJavaSettingsPropertyValue ("java.vendor", out var vendor));
163163
Assert.AreEqual ("Xamarin.Android Unit Tests", vendor);
@@ -170,5 +170,85 @@ public void JavaSettingsProperties ()
170170
Assert.AreEqual ("line the second", lines.ElementAt (1));
171171
Assert.AreEqual (".", lines.ElementAt (2));
172172
}
173+
174+
[Test]
175+
public void ParseOracleReleaseVersion ()
176+
{
177+
var dir = Path.GetTempFileName();
178+
File.Delete (dir);
179+
180+
try {
181+
CreateFauxJdk (dir, releaseVersion: "1.2.3_4", releaseBuildNumber: "", javaVersion: "100.100.100_100");
182+
var jdk = new JdkInfo (dir);
183+
Assert.AreEqual (new Version (1, 2, 3, 4), jdk.Version);
184+
}
185+
finally {
186+
Directory.Delete (dir, recursive: true);
187+
}
188+
}
189+
190+
[Test]
191+
public void ParseOracleJavaVersion ()
192+
{
193+
var dir = Path.GetTempFileName();
194+
File.Delete (dir);
195+
196+
try {
197+
CreateFauxJdk (dir, releaseVersion: "", releaseBuildNumber: "", javaVersion: "101.102.103_104");
198+
var jdk = new JdkInfo (dir);
199+
Assert.AreEqual (new Version (101, 102, 103, 104), jdk.Version);
200+
}
201+
finally {
202+
Directory.Delete (dir, recursive: true);
203+
}
204+
}
205+
206+
[Test]
207+
public void ParseMicrosoftReleaseVersion ()
208+
{
209+
var dir = Path.GetTempFileName();
210+
File.Delete (dir);
211+
212+
try {
213+
CreateFauxJdk (dir, releaseVersion: "1.2.3", releaseBuildNumber: "4", javaVersion: "100.100.100_100");
214+
var jdk = new JdkInfo (dir);
215+
Assert.AreEqual (new Version (1, 2, 3, 4), jdk.Version);
216+
}
217+
finally {
218+
Directory.Delete (dir, recursive: true);
219+
}
220+
}
221+
222+
[Test]
223+
public void ParseMicrosoftJavaVersion()
224+
{
225+
var dir = Path.GetTempFileName();
226+
File.Delete (dir);
227+
228+
try {
229+
CreateFauxJdk (dir, releaseVersion: "", releaseBuildNumber: "", javaVersion: "1.2.3-4");
230+
var jdk = new JdkInfo (dir);
231+
Assert.AreEqual (new Version (1, 2, 3, 4), jdk.Version);
232+
}
233+
finally {
234+
Directory.Delete (dir, recursive: true);
235+
}
236+
}
237+
238+
[Test]
239+
public void Version_ThrowsNotSupportedException ()
240+
{
241+
var dir = Path.GetTempFileName();
242+
File.Delete (dir);
243+
244+
try {
245+
CreateFauxJdk (dir, releaseVersion: "", releaseBuildNumber: "", javaVersion: "");
246+
var jdk = new JdkInfo (dir);
247+
Assert.Throws<NotSupportedException> (() => { var _ = jdk.Version; });
248+
}
249+
finally {
250+
Directory.Delete (dir, recursive: true);
251+
}
252+
}
173253
}
174254
}

0 commit comments

Comments
 (0)