Skip to content

Commit

Permalink
[build] Cursory JDK 9 support (#1214)
Browse files Browse the repository at this point in the history
We have a VSTS+macOS build machine which has JDK 9 as the default JDK,
thus causing *all* xamarin-android builds to fail, as `gradlew` and
JDK 9 don't *directly* mix:

	Executing: ./gradlew assembleDebug --stacktrace
	...
	org.gradle.api.ProjectConfigurationException: A problem occurred configuring project ':library'.
	  ...
	Caused by: org.gradle.internal.event.ListenerNotificationException:
	Failed to notify project evaluation listener.
	  ...
	Caused by: java.lang.NoClassDefFoundError: javax/xml/bind/annotation/XmlSchema
	  ...
	Caused by: java.lang.ClassNotFoundException: javax.xml.bind.annotation.XmlSchema

When using JDK 9, `gradlew` fails because *something* within it
attempts to use the deprecated type
`javax.xml.bind.annotation.XmlSchema`, and JDK 9 doesn't provide
deprecated modules in the default `$CLASSPATH`.

Knowing that the VSTS machine *also* has JDK 8 installed, we've tried
to "filter out" JDK 9 so that it wouldn't be used; see e.g. a3c4358.
Unfortunately that doesn't work, because on macOS the default JDK used
is [always the JDK with the highest version number][macOS-jdk], and
this can't be easily changed system-wide because changing this
behavior requires "removing" a `Info.plist` file to prevent the JDK
from being "seen" by `/usr/bin/java`:

[macOS-jdk]: https://stackoverflow.com/a/44169445

	# Don't use JDK 9 by default on macOS:
	$ cd /Library/Java/JavaVirtualMachines/jdk-9.0.4.jdk/Contents
	$ mv Info.plist Info.plist.disabled

(Renaming `Info.plist.disabled` to `Info.plist` will restore JDK 9 as
the default JDK on the machine.)

A "proper" fix would presumably involve using and exporting
`$JAVA_HOME` *everywhere*, which isn't currently the case.
[This is something that *should* be explored.][xa-1213]

[xa-1213]: #1213

In the meantime, a question: *Can* we build with JDK 9?
*Is it even possible in the first place*?

Turns out, *yes*, it *is* possible. The above `gradlew` error can be
fixed by providing the (new-in-JDK9) `--add-modules` option, so that
the `java.xml.bind` module is accessible:

	$ ANDROID_HOME=... JAVA_OPTS="--add-modules java.xml.bind" ./gradlew assembleDebug --stacktrace --no-daemon
	# ...works!

Unfortunately, *other* parts xamarin-android get in the way, e.g. the
`<ResolveSdks/>` task doesn't correctly parse the version number out
of `java -version`, and JDK 9 `javac` requires `-source` and `-target`
when `-bootclasspath` is used.

Update the build system and tests so that things *can* be built under
JDK 9.

Additionally, cleanup the internal build system a bit: instead of
having `javac` and `jar` calls strewn throughout *7* different
projects -- all of which would need to be updated to provide usable
`javac -source` and `javac -target` values (JDK 9 doesn't support
`-target 1.6` anymore!), introduce `build-tools\scripts\Jar.targets`
and the new `BuildTestJarFile` target. This takes all
`@(TestJarEntry)` files, compiles them with `javac`, and `jar`s the
compiled files into `%(TestJarEntry.OutputFile)`.

**Note**: This does ***NOT*** mean that building and/or using JDK 9
will be commercially supported in *any* way. (It might not even work!)
  • Loading branch information
jonpryor authored and dellis1972 committed Jan 23, 2018
1 parent 4920650 commit 0e1d1c8
Show file tree
Hide file tree
Showing 20 changed files with 271 additions and 177 deletions.
43 changes: 43 additions & 0 deletions build-tools/scripts/Jar.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="_GetJavacVersions"
DependsOnTargets="AndroidPrepareForBuild">
<PropertyGroup>
<_JavacSourceVersion Condition="$(_JdkVersion.StartsWith ('9'))">1.8</_JavacSourceVersion>
<_JavacSourceVersion Condition=" '$(_JavacSourceVersion)' == '' ">1.5</_JavacSourceVersion>
<_JavacTargetVersion Condition="$(_JdkVersion.StartsWith ('9'))">1.8</_JavacTargetVersion>
<_JavacTargetVersion Condition=" '$(_JavacTargetVersion)' == '' ">1.6</_JavacTargetVersion>
</PropertyGroup>
</Target>
<Target Name="BuildTestJarFile"
DependsOnTargets="_GetJavacVersions"
Inputs="@(TestJarEntry)"
Outputs="%(TestJarEntry.OutputFile)">
<ItemGroup>
<_JavacSource
Include="@(TestJarEntry)"
/>
</ItemGroup>
<PropertyGroup>
<_Javac>"$(JavaCPath)"</_Javac>
<_Jar>"$(JarPath)"</_Jar>
<_Targets>-source $(_JavacSourceVersion) -target $(_JavacTargetVersion)</_Targets>
<_DestDir>$(IntermediateOutputPath)__CreateTestJarFile-bin</_DestDir>
<_AndroidJar>-cp "$(AndroidSdkDirectory)\platforms\android-$(_AndroidApiLevelName)\android.jar"</_AndroidJar>
</PropertyGroup>
<MakeDir Directories="$(_DestDir)" />
<Exec Command="$(_Javac) $(_Targets) -d &quot;$(_DestDir)&quot; $(_AndroidJar) @(_JavacSource->'&quot;%(Identity)&quot;', ' ')" />
<Exec
Command="$(_Jar) cf &quot;classes.jar&quot; ."
WorkingDirectory="$(_DestDir)"
/>
<Copy
SourceFiles="$(_DestDir)\classes.jar"
DestinationFiles="%(TestJarEntry.OutputFile)"
/>
<RemoveDir Directories="$(_DestDir)" />
</Target>
<Target Name="CleanTestJarFile">
<Delete Files="%(TestJarEntry.OutputFile)" />
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@

namespace Xamarin.Android.Tasks
{
public class AdjustJavacVersionArguments : ToolTask
public class AdjustJavacVersionArguments : Task
{
[Required]
public string ToolPath { get; set; }
public string JdkVersion { get; set; }

[Required]
public string DefaultJdkVersion { get; set; }

public bool EnableProguard { get; set; }

Expand All @@ -24,53 +27,32 @@ public class AdjustJavacVersionArguments : ToolTask
[Output]
public string SourceVersion { get; set; }

protected override string ToolName {
get { return OS.IsWindows ? "javac.exe" : "javac"; }
}

public override bool Execute ()
{
Log.LogDebugMessage ("ToolPath: {0}", ToolPath);
Log.LogDebugMessage ("ToolExe: {0}", ToolExe);
Log.LogDebugMessage ($"{nameof (DefaultJdkVersion)}: {DefaultJdkVersion}");
Log.LogDebugMessage ("EnableProguard: {0}", EnableProguard);
Log.LogDebugMessage ("EnableMultiDex: {0}", EnableMultiDex);
Log.LogDebugMessage ($"{nameof (JdkVersion)}: {JdkVersion}");
Log.LogDebugMessage ("SkipJavacVersionCheck: {0}", SkipJavacVersionCheck);

if (JdkVersion.StartsWith ("9", StringComparison.OrdinalIgnoreCase)) {
TargetVersion = SourceVersion = DefaultJdkVersion;
}

if (SkipJavacVersionCheck)
return true;

// so far only proguard matters.
if (!EnableProguard && !EnableMultiDex)
return true;

var psi = new ProcessStartInfo (Path.Combine (ToolPath, ToolExe ?? ToolName), "-version") {
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden,
};
var proc = Process.Start (psi);
proc.WaitForExit ();
var line = proc.StandardError.ReadLine ();
if (!line.StartsWith ("javac "))
// otherwise ignore.
return true;

var version = line.Substring (6);

if (version.StartsWith ("1.8")) {
if (JdkVersion.StartsWith ("1.8", StringComparison.OrdinalIgnoreCase)) {
TargetVersion = SourceVersion = "1.7";
Log.LogDebugMessage ("Javac TargetVersion adjusted to: {0}", TargetVersion);
Log.LogDebugMessage ("Javac SourceVersion adjusted to: {0}", SourceVersion);
}

return true;
}

protected override string GenerateFullPathToTool ()
{
return Path.Combine (ToolPath, ToolExe);
return !Log.HasLoggedErrors;
}
}
}
Expand Down
22 changes: 20 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/ResolveSdksTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ public class ResolveSdks : Task
[Output]
public bool AndroidUseApkSigner { get; set; }

[Output]
public string JdkVersion { get; set; }

[Output]
public string MinimumRequiredJdkVersion { get; set; }

static bool IsWindows = Path.DirectorySeparatorChar == '\\';
static readonly string ZipAlign = IsWindows ? "zipalign.exe" : "zipalign";
static readonly string Aapt = IsWindows ? "aapt.exe" : "aapt";
Expand All @@ -140,6 +146,7 @@ public bool RunTask ()
Log.LogDebugMessage (" AndroidSdkBuildToolsVersion: {0}", AndroidSdkBuildToolsVersion);
Log.LogDebugMessage ($" {nameof (AndroidSdkPath)}: {AndroidSdkPath}");
Log.LogDebugMessage ($" {nameof (AndroidNdkPath)}: {AndroidNdkPath}");
Log.LogDebugMessage ($" {nameof (JavaSdkPath)}: {JavaSdkPath}");
Log.LogDebugTaskItems (" ReferenceAssemblyPaths: ", ReferenceAssemblyPaths);
Log.LogDebugMessage (" TargetFrameworkVersion: {0}", TargetFrameworkVersion);
Log.LogDebugMessage (" UseLatestAndroidPlatformSdk: {0}", UseLatestAndroidPlatformSdk);
Expand Down Expand Up @@ -280,6 +287,8 @@ public bool RunTask ()
Log.LogDebugMessage (" AndroidSdkBuildToolsBinPath: {0}", AndroidSdkBuildToolsBinPath);
Log.LogDebugMessage (" AndroidSdkPath: {0}", AndroidSdkPath);
Log.LogDebugMessage (" JavaSdkPath: {0}", JavaSdkPath);
Log.LogDebugMessage (" JdkVersion: {0}", JdkVersion);
Log.LogDebugMessage (" MinimumRequiredJdkVersion: {0}", MinimumRequiredJdkVersion);
Log.LogDebugMessage (" MonoAndroidBinPath: {0}", MonoAndroidBinPath);
Log.LogDebugMessage (" MonoAndroidToolsPath: {0}", MonoAndroidToolsPath);
Log.LogDebugMessage (" TargetFrameworkVersion: {0}", TargetFrameworkVersion);
Expand Down Expand Up @@ -320,7 +329,10 @@ public bool RunTask ()
return !Log.HasLoggedErrors;
}

static readonly Regex javaVersionRegex = new Regex (@"""(?<version>[\d\.]+)_\d+""");
// `java -version` will produce values such as:
// java version "9.0.4"
// java version "1.8.0_77"
static readonly Regex javaVersionRegex = new Regex (@"version ""(?<version>[\d\.]+)(_\d+)?""");

Version GetJavaVersionForFramework (string targetFrameworkVersion)
{
Expand Down Expand Up @@ -350,6 +362,8 @@ bool ValidateJavaVersion (string targetFrameworkVersion, string buildToolsVersio
Version requiredJavaForBuildTools = GetJavaVersionForBuildTools (buildToolsVersion);

Version required = requiredJavaForFrameworkVersion > requiredJavaForBuildTools ? requiredJavaForFrameworkVersion : requiredJavaForBuildTools;

MinimumRequiredJdkVersion = required.ToString ();

var sb = new StringBuilder ();

Expand All @@ -372,15 +386,19 @@ bool ValidateJavaVersion (string targetFrameworkVersion, string buildToolsVersio
var versionNumberMatch = javaVersionRegex.Match (versionInfo);
Version versionNumber;
if (versionNumberMatch.Success && Version.TryParse (versionNumberMatch.Groups ["version"]?.Value, out versionNumber)) {
JdkVersion = versionNumberMatch.Groups ["version"].Value;
Log.LogMessage (MessageImportance.Normal, $"Found Java SDK version {versionNumber}.");
if (versionNumber < requiredJavaForFrameworkVersion) {
Log.LogError ($"Java SDK {requiredJavaForFrameworkVersion} or above is required when targeting FrameworkVerison {targetFrameworkVersion}.");
}
if (versionNumber < requiredJavaForBuildTools) {
Log.LogError ($"Java SDK {requiredJavaForBuildTools} or above is required when using build-tools {buildToolsVersion}.");
}
if (versionNumber > Version.Parse (LatestSupportedJavaVersion)) {
Log.LogWarning ($"JDK Version `{versionNumber}` is later than latest suppored JDK version `{LatestSupportedJavaVersion}`.");
}
} else
Log.LogWarning ($"Failed to get the Java SDK version. Found {versionInfo} but this does not seem to contain a valid version number.");
Log.LogWarning ($"Failed to get the Java SDK version as it does not appear to contain a valid version number. `javac -version` returned: ```{versionInfo}```");
return !Log.HasLoggedErrors;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ Copyright (C) 2012 Xamarin Inc. All rights reserved.
<Output TaskParameter="JavaSdkPath" PropertyName="JavaSdkDirectory" Condition="'$(JavaSdkDirectory)' == ''" />
<Output TaskParameter="MonoAndroidToolsPath" PropertyName="MonoAndroidToolsDirectory" />
<Output TaskParameter="AndroidSdkBuildToolsBinPath" PropertyName="AndroidSdkBuildToolsBinPath" Condition="'$(AndroidSdkBuildToolsBinPath)' == ''" />
<Output TaskParameter="JdkVersion" PropertyName="_JdkVersion" />
<Output TaskParameter="MinimumRequiredJdkVersion" PropertyName="_DefaultJdkVersion" />
</ResolveSdks>
<CreateProperty Value="$(TargetFrameworkIdentifier),Version=$(TargetFrameworkVersion),Profile=$(TargetFrameworkProfile)">
<Output TaskParameter="Value" PropertyName="TargetFrameworkMoniker"
Expand Down
14 changes: 8 additions & 6 deletions src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,8 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<Output TaskParameter="LintToolPath" PropertyName="LintToolPath" Condition="'$(LintToolPath)' == ''" />
<Output TaskParameter="ApkSignerToolExe" PropertyName="ApkSignerToolExe" Condition="'$(ApkSignerToolExe)' == ''" />
<Output TaskParameter="AndroidUseApkSigner" PropertyName="AndroidUseApkSigner" Condition="'$(AndroidUseApkSigner)' == ''" />
<Output TaskParameter="JdkVersion" PropertyName="_JdkVersion" />
<Output TaskParameter="MinimumRequiredJdkVersion" PropertyName="_DefaultJdkVersion" />
</ResolveSdks>
<CreateProperty Value="$(TargetFrameworkIdentifier),Version=$(_TargetFrameworkVersion),Profile=$(TargetFrameworkProfile)">
<Output TaskParameter="Value" PropertyName="TargetFrameworkMoniker"
Expand Down Expand Up @@ -2007,12 +2009,12 @@ because xbuild doesn't support framework reference assemblies.
<Target Name="_AdjustJavacVersionArguments">

<AdjustJavacVersionArguments
Condition="'$(JavacTargetVersion)'=='' or '$(JavacSourceVersion)' == ''"
ToolPath="$(JavacToolPath)"
ToolExe="$(JavacToolExe)"
SkipJavacVersionCheck="$(AndroidSkipJavacVersionCheck)"
EnableProguard="$(AndroidEnableProguard)"
EnableMultiDex="$(AndroidEnableMultiDex)">
Condition=" '$(JavacTargetVersion)' == '' or '$(JavacSourceVersion)' == '' "
JdkVersion="$(_JdkVersion)"
DefaultJdkVersion="$(_DefaultJdkVersion)"
SkipJavacVersionCheck="$(AndroidSkipJavacVersionCheck)"
EnableProguard="$(AndroidEnableProguard)"
EnableMultiDex="$(AndroidEnableMultiDex)">
<Output TaskParameter="TargetVersion" PropertyName="JavacTargetVersion" />
<Output TaskParameter="SourceVersion" PropertyName="JavacSourceVersion" />
</AdjustJavacVersionArguments>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,22 @@
<InputJar Include="Jars\Cursor.jar" />
</ItemGroup>
<ItemGroup>
<InputJarSource Include="java\test\bindings\Cursor.java" />
<TestJarEntry Include="java\test\bindings\Cursor.java">
<OutputFile>Jars/Cursor.jar</OutputFile>
</TestJarEntry>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
<Import Project="..\..\..\build-tools\scripts\Jar.targets" />
<Import Project="Xamarin.Android.FixJavaAbstractMethod-APIv1Binding.targets" />
<PropertyGroup>
<BuildDependsOn>
BuildJars;
BuildTestJarFile;
$(BuildDependsOn)
</BuildDependsOn>
</PropertyGroup>
<PropertyGroup>
<CleanDependsOn>
CleanTestJarFile;
$(CleanDependsOn);
CleanLocal;
</CleanDependsOn>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildJars"
DependsOnTargets="AndroidPrepareForBuild"
Inputs="@(InputJarSource)"
Outputs="@(InputJar)">
<MakeDir Directories="Jars\classes" />
<Exec Command="&quot;$(JavaCPath)&quot; -source 1.5 -target 1.6 -d &quot;Jars\classes&quot; @(InputJarSource -&gt; '%(Identity)', ' ')" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;@(InputJar)&quot; -C &quot;Jars\classes&quot; ." />
</Target>
<Target Name="CleanLocal">
<RemoveDir Directories="bin;obj;Jars\classes;@(InputJar)"/>
<RemoveDir Directories="bin;obj;"/>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,22 @@
<InputJar Include="Jars\Cursor.jar" />
</ItemGroup>
<ItemGroup>
<InputJarSource Include="java\test\bindings\Cursor.java" />
<TestJarEntry Include="java\test\bindings\Cursor.java">
<OutputFile>Jars/Cursor.jar</OutputFile>
</TestJarEntry>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.Bindings.targets" />
<Import Project="..\..\..\build-tools\scripts\Jar.targets" />
<Import Project="Xamarin.Android.FixJavaAbstractMethod-APIv2Binding.targets" />
<PropertyGroup>
<BuildDependsOn>
BuildJars;
BuildTestJarFile;
$(BuildDependsOn)
</BuildDependsOn>
</PropertyGroup>
<PropertyGroup>
<CleanDependsOn>
CleanTestJarFile;
$(CleanDependsOn);
CleanLocal;
</CleanDependsOn>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildJars"
DependsOnTargets="AndroidPrepareForBuild"
Inputs="@(InputJarSource)"
Outputs="@(InputJar)">
<MakeDir Directories="Jars\classes" />
<Exec Command="&quot;$(JavaCPath)&quot; -source 1.5 -target 1.6 -d &quot;Jars\classes&quot; @(InputJarSource -&gt; '%(Identity)', ' ')" />
<Exec Command="&quot;$(JarPath)&quot; cf &quot;@(InputJar)&quot; -C &quot;Jars\classes&quot; ." />
</Target>
<Target Name="CleanLocal">
<RemoveDir Directories="bin;obj;Jars\classes;@(InputJar)"/>
<RemoveDir Directories="bin;obj"/>
</Target>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,12 @@
DependsOnTargets="AndroidPrepareForBuild"
Inputs="java\JavaLib\project.properties"
Outputs="$(OutputPath)JavaLib.zip">
<PropertyGroup>
<_Jdk9Modules Condition="$(_JdkVersion.StartsWith ('9'))">JAVA_OPTS="--add-modules java.xml.bind"</_Jdk9Modules>
</PropertyGroup>
<Exec
EnvironmentVariables="ANDROID_HOME=$(AndroidSdkDirectory);JAVA_HOME=$(JavaSdkDirectory)"
Command=".\gradlew assembleDebug --stacktrace"
Command="$(_Jdk9Modules) .\gradlew assembleDebug --stacktrace --no-daemon"
WorkingDirectory="$(MSBuildThisFileDirectory)java\JavaLib"
/>
<MakeDir Directories="$(OutputPath)" />
Expand Down
Loading

0 comments on commit 0e1d1c8

Please sign in to comment.