Skip to content

Commit 588bb33

Browse files
committed
[build] Don't use ls(1) to find JDK paths
Fixes: dotnet/android#1493 On some machines, `make prepare` fails: build-tools/scripts/jdk.mk:130: *** missing separator. Stop. make: *** [prepare-external] Error 2 Eventually, we "found" the "cause": This make fragment: $(shell ls -dtr $(_DARWIN_JDK_FALLBACK_DIRS) | sort | tail -1) was dying a terrible horrible no good death: _DARWIN_JDK_ROOT=/Library/Java/JavaVirtualMachines/jdk1.8.0_161.jdk kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkin/sh: 49m: command not found [Turns Out™][0], **ls**(1) should be *avoided*, as its output is *unsafe* (I'm not sure *why*, and I'm not able to repro the above failure on my machine, but it's *clearly* bad). [0]: http://mywiki.wooledge.org/ParsingLs Why are we using **ls**(1)? To sort by timestamp, via `ls -dtr`. What's the recommended replacement? > If you truly need a list of all the files in a directory in order > by mtime so that you can process them in sequence, switch to perl, > and have your perl program do its own directory opening and sorting LOL? Which brings us to the solution: we don't want to use Perl -- we *want* something plausibly cross-platform -- so let's use our existing cross-platform dependency: MSBuild! Add a new `<JdkDirectoryFinder/>` task, to which we provide a "JDKs Root" directory -- the directory which contains JDKs -- and the `<JdkDirectoryFinder/>` task will open that directory, sort the contents, skip the "out of version range" JDK versions (`$(JI_JDK_MAX); commit 55c56f7), and return the "best" JDK root directory from those available.
1 parent b19bc60 commit 588bb33

File tree

4 files changed

+137
-17
lines changed

4 files changed

+137
-17
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ clean:
6464
-rm src/Java.Runtime.Environment/Java.Runtime.Environment.dll.config
6565

6666
include build-tools/scripts/mono.mk
67-
include build-tools/scripts/jdk.mk
6867
include build-tools/scripts/msbuild.mk
68+
include build-tools/scripts/jdk.mk
6969

7070
$(PACKAGES) $(NUNIT_CONSOLE):
7171
nuget restore
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Text.RegularExpressions;
6+
7+
using Microsoft.Build.Utilities;
8+
using Microsoft.Build.Framework;
9+
10+
public class JdkDirectoryFinder : Task {
11+
12+
[Required]
13+
public string JdksRoot { get; set; }
14+
15+
public string MaximumJdkVersion { get; set; }
16+
17+
[Output]
18+
public string PreferredJdkRoot { get; set; }
19+
20+
static Regex VersionExtractor = new Regex (@"(?<version>[\d]+(\.\d+)+)", RegexOptions.Compiled);
21+
22+
public override bool Execute ()
23+
{
24+
var version = GetMaxJdkVersion ();
25+
var dirs = new List<string>();
26+
foreach (var d in Directory.EnumerateDirectories (JdksRoot)) {
27+
var n = Path.GetFileName (d);
28+
var m = VersionExtractor.Match (n);
29+
Version v;
30+
31+
if (!m.Success || version == null || !Version.TryParse (m.Groups ["version"].Value, out v)) {
32+
dirs.Add (d);
33+
continue;
34+
}
35+
if (v <= version) {
36+
dirs.Add (d);
37+
}
38+
}
39+
dirs.Sort (NaturalStringComparer.Default);
40+
41+
if (dirs.Count > 0) {
42+
PreferredJdkRoot = dirs [dirs.Count-1];
43+
}
44+
45+
return true;
46+
}
47+
48+
Version GetMaxJdkVersion ()
49+
{
50+
if (string.IsNullOrEmpty (MaximumJdkVersion))
51+
return null;
52+
if (!MaximumJdkVersion.Contains (".")) {
53+
MaximumJdkVersion += ".0";
54+
}
55+
return new Version (MaximumJdkVersion);
56+
}
57+
}
58+
59+
// https://github.com/cadenza/cadenza/blob/master/src/Cadenza/Cadenza/NaturalStringComparer.cs
60+
sealed class NaturalStringComparer : IComparer<string>, System.Collections.IComparer {
61+
62+
static readonly NaturalStringComparer _default = new NaturalStringComparer ();
63+
64+
private NaturalStringComparer ()
65+
{
66+
}
67+
68+
public static NaturalStringComparer Default {
69+
get {
70+
return _default;
71+
}
72+
}
73+
74+
public int Compare (string x, string y)
75+
{
76+
string left = x ?? "";
77+
string right = y ?? "";
78+
return Regex.Replace (left, @"([\d]+)|([^\d]+)",
79+
m => (m.Value.Length > 0 && char.IsDigit(m.Value[0]))
80+
? m.Value.PadLeft (System.Math.Max(left.Length, right.Length))
81+
: m.Value
82+
).CompareTo (Regex.Replace(right, @"([\d]+)|([^\d]+)",
83+
m => (m.Value.Length > 0 && char.IsDigit(m.Value[0]))
84+
? m.Value.PadLeft (System.Math.Max(left.Length, right.Length))
85+
: m.Value));
86+
}
87+
88+
int System.Collections.IComparer.Compare (object x, object y)
89+
{
90+
return Compare (
91+
x != null ? x.ToString () : "",
92+
y != null ? y.ToString () : "");
93+
}
94+
}

build-tools/scripts/jdk.mk

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,6 @@ JI_JAR_PATH = jar
3232
JI_JDK_BIN_PATH = $(dir $(shell which java))
3333

3434

35-
# Filter on <= JI_MAX_JDK
36-
ifneq ($(JI_MAX_JDK),)
37-
_VERSION_MAX := | awk '$$1 <= $(JI_MAX_JDK)'
38-
endif #JI_MAX_JDK
39-
40-
# Sort numerically on version numbers with `sort -n`, filtering on $(JI_MAX_JDK) if needed
41-
# Replace each line so it starts with a number (sed 's/...'\1 &/), sort on the leading number, then remove the leading number.
42-
# Grab the last path name printed.
43-
_VERSION_SORT := sed 's/[^0-9]*\([0-9.]*\)/\1 &/' $(_VERSION_MAX) | sort -n | sed 's/^[0-9.]* //g' | tail -1
44-
4535
ifeq ($(OS),Darwin)
4636

4737
_MONO_BITNESS = $(shell file `which $(word 1, $(RUNTIME))` | awk 'BEGIN { val = "32-bit" } /64-bit/ { val = "64-bit" } END { print val; }')
@@ -51,6 +41,12 @@ ifeq ($(_MONO_BITNESS),32-bit)
5141
JI_JVM_PATH = /System/Library/Frameworks/JavaVM.framework/JavaVM
5242
endif # 32-bit
5343

44+
# $(call FindJdkDirectory,dir)
45+
define FindJdkDirectory
46+
$(MSBUILD) build-tools/scripts/jdk.targets /nologo /v:minimal /t:GetPreferredJdkRoot \
47+
/p:JdksRoot="$(1)" $(if $(JI_MAX_JDK),"/p:MaximumJdkVersion=$(JI_MAX_JDK)")
48+
endef
49+
5450
# Darwin supports three possible search locations:
5551
#
5652
# 1. `/Library/Java/JavaVirtualMachines/jdk*`
@@ -62,7 +58,7 @@ endif # 32-bit
6258
#
6359
# 3. A "locally" hosted .pkg, in case Xcode.app isn't installed.
6460

65-
_DARWIN_JDK_FALLBACK_DIRS = $(wildcard /Library/Java/JavaVirtualMachines/jdk*)
61+
_DARWIN_JDK_FALLBACK_ROOT = $(wildcard /Library/Java/JavaVirtualMachines)
6662
_DARWIN_JDK_JNI_INCLUDE_DIR = Contents/Home/include
6763
_DARWIN_JDK_JNI_OS_INCLUDE_DIR = $(_DARWIN_JDK_JNI_INCLUDE_DIR)/darwin
6864

@@ -77,8 +73,8 @@ _LOCAL_JDK_HEADERS = LocalJDK/System/Library/Frameworks/JavaVM.fr
7773
# Ancient source for (3)
7874
_APPLE_JDK6_URL = http://adcdownload.apple.com/Developer_Tools/java_for_os_x_2013005_developer_package/java_for_os_x_2013005_dp__11m4609.dmg
7975

80-
ifneq ($(_DARWIN_JDK_FALLBACK_DIRS),)
81-
_DARWIN_JDK_ROOT := $(shell ls -dtr $(_DARWIN_JDK_FALLBACK_DIRS) | $(_VERSION_SORT))
76+
ifneq ($(_DARWIN_JDK_FALLBACK_ROOT),)
77+
_DARWIN_JDK_ROOT := $(shell $(call FindJdkDirectory,$(_DARWIN_JDK_FALLBACK_ROOT)) | tr -d '[[:space:]]')
8278
JI_JDK_BIN_PATH = $(_DARWIN_JDK_ROOT)/Contents/Home/bin
8379
JI_JAVAC_PATH = $(_DARWIN_JDK_ROOT)/Contents/Home/bin/javac
8480
JI_JAR_PATH = $(_DARWIN_JDK_ROOT)/Contents/Home/bin/jar
@@ -117,7 +113,7 @@ ifeq ($(OS),Linux)
117113
# This is for all linux distributions with which and java installed
118114
_DEFAULT_LINUX_JAVA_ROOT = $(shell java -XshowSettings:properties -help 2>&1 | grep java.home | sed 's/^.*java.home = //g')/../
119115
_DEFAULT_LINUX_JAVA_INCLUDE_DIRS = $(_DEFAULT_LINUX_JAVA_ROOT)/include/
120-
_LINUX_JAVA_FALLBACK_DIRS = /usr/lib/jvm/java*
116+
_LINUX_JAVA_FALLBACK_ROOT = $(wildcard /usr/lib/jvm)
121117
_LINUX_JAVA_JNI_INCLUDE_DIR = include
122118
_LINUX_JAVA_ROOT = $(_DEFAULT_LINUX_JAVA_ROOT)
123119
_LINUX_JAVA_ARCH_64 = amd64
@@ -130,8 +126,8 @@ _DESKTOP_JAVA_INCLUDE_DIRS = $(wildcard $(JAVA_HOME)/include)
130126
_LINUX_JAVA_ROOT = $(JAVA_HOME)
131127
endif # No default Java location, $JAVA_HOME check
132128

133-
ifeq ($(wildcard $(_DESKTOP_JAVA_INCLUDE_DIRS)),)
134-
LATEST_JDK := $(shell ls -dtr $(_LINUX_JAVA_FALLBACK_DIRS) | $(_VERSION_SORT))
129+
ifeq ($(wildcard $(_LINUX_JAVA_FALLBACK_ROOT)),)
130+
LATEST_JDK := $(call FindJdkDirectory,$(_LINUX_JAVA_FALLBACK_ROOT) | tr -d '[[:space:]]')
135131
_DESKTOP_JAVA_INCLUDE_DIRS = $(LATEST_JDK)/$(_LINUX_JAVA_JNI_INCLUDE_DIR)
136132
_LINUX_JAVA_ROOT = $(LATEST_JDK)
137133
endif # No $JAVA_HOME, find the latest version

build-tools/scripts/jdk.targets

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<_TasksAssembly Condition=" Exists('$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll') ">$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll</_TasksAssembly>
5+
<_TasksAssembly Condition=" '$(_TasksAssembly)' == '' ">$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll</_TasksAssembly>
6+
</PropertyGroup>
7+
<UsingTask TaskName="JdkDirectoryFinder"
8+
TaskFactory="CodeTaskFactory"
9+
AssemblyFile="$(_TasksAssembly)">
10+
<ParameterGroup>
11+
<JdksRoot Required="True" />
12+
<MaximumJdkVersion />
13+
<PreferredJdkRoot Output="True" />
14+
</ParameterGroup>
15+
<Task>
16+
<Code Language="cs" Source="JdkDirectoryFinder.cs" Type="Class" />
17+
</Task>
18+
</UsingTask>
19+
<Target Name="GetPreferredJdkRoot">
20+
<JdkDirectoryFinder
21+
JdksRoot="$(JdksRoot)"
22+
MaximumJdkVersion="$(MaximumJdkVersion)">
23+
<Output TaskParameter="PreferredJdkRoot" PropertyName="_JdkRoot"/>
24+
</JdkDirectoryFinder>
25+
<Message
26+
Text="$(_JdkRoot)"
27+
Importance="High"
28+
/>
29+
</Target>
30+
</Project>

0 commit comments

Comments
 (0)