Skip to content

Commit

Permalink
[AndroidDotnetToolTask] Use full path to dotnet
Browse files Browse the repository at this point in the history
Context: dotnet/java-interop#988

A recent attempt to update Java.Interop to target net7.0 produced some
test failures on the Xamarin.Android side:

   Using "ClassParse" task from assembly "/Users/runner/work/1/s/xamarin-android/bin/Release/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.0-ci.pr.gh7028.23/tools/Xamarin.Android.Build.Tasks.dll".
   Task "ClassParse" (TaskId:139)
     Task Parameter:ToolPath=/Users/runner/work/1/s/xamarin-android/bin/Release/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.0-ci.pr.gh7028.23/tools (TaskId:139)
     Task Parameter:OutputFile=obj/Debug/api.xml.class-parse (TaskId:139)
     Task Parameter:SourceJars=/Users/runner/work/1/s/xamarin-android/bin/Release/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.0-ci.pr.gh7028.23/tools/android-support-multidex.jar (TaskId:139)
     Using: dotnet /Users/runner/work/1/s/xamarin-android/bin/Release/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.0-ci.pr.gh7028.23/tools/class-parse.dll (TaskId:139)
     [class-parse] response file: obj/Debug/class-parse.rsp (TaskId:139)
     --o="obj/Debug/api.xml.class-parse" (TaskId:139)
     "/Users/runner/work/1/s/xamarin-android/bin/Release/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.0-ci.pr.gh7028.23/tools/android-support-multidex.jar" (TaskId:139)
     /Users/runner/.dotnet/dotnet /Users/runner/work/1/s/xamarin-android/bin/Release/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.0-ci.pr.gh7028.23/tools/class-parse.dll "@obj/Debug/class-parse.rsp"  (TaskId:139)
     It was not possible to find any compatible framework version (TaskId:139)
     The framework 'Microsoft.NETCore.App', version '7.0.0-preview.5.22271.4' (x64) was not found. (TaskId:139)
       - The following frameworks were found: (TaskId:139)
           3.1.1 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           3.1.3 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           3.1.6 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           3.1.25 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           5.0.2 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           5.0.5 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           5.0.8 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           5.0.17 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
           6.0.5 at [/Users/runner/.dotnet/shared/Microsoft.NETCore.App] (TaskId:139)
      (TaskId:139)
     You can resolve the problem by installing the specified framework and/or SDK. (TaskId:139)
      (TaskId:139)
     The specified framework can be found at: (TaskId:139)
       - https://aka.ms/dotnet-core-applaunch?framework=Microsoft.NETCore.App&framework_version=7.0.0-preview.5.22271.4&arch=x64&rid=osx.12-x64 (TaskId:139)
    1:7>/Users/runner/work/1/s/xamarin-android/bin/Release/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.0-ci.pr.gh7028.23/tools/Xamarin.Android.Bindings.ClassParse.targets(30,5): error MSB6006: "dotnet" exited with code 150. [/Users/runner/work/1/a/TestRelease/05-25_01.03.40/temp/BuildWithExternalJavaLibrary/BuildWithExternalJavaLibraryBinding/BuildWithExternalJavaLibraryBinding.csproj]

Commit ff7f467 removed the need to have a global .NET 7 preview install
to build and test Xamarin.Android.  This appears to expose an issue in
`AndroidDotnetToolTask`, which uses the latest `dotnet` in $PATH to run
some of our tools.  After upgrading some of these tools (class-parse,
generator) to net7.0, we are no longer able to run them as there is no
net7.0 runtime installed globally.

Improve the `AndroidDotnetToolTask` to better handle "sandboxed" .NET
installations by using a full path to `dotnet` if one is found.
  • Loading branch information
pjcollins committed May 31, 2022
1 parent 06ff656 commit 84d51b2
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ This file is only used by binding projects.
OutputFile="$(ApiOutputFile).class-parse"
SourceJars="@(EmbeddedJar);@(InputJar)"
DocumentationPaths="@(_AndroidDocumentationPath)"
NetCoreRoot="$(NetCoreRoot)"
ToolPath="$(MonoAndroidToolsDirectory)"
ToolExe="$(ClassParseToolExe)"
/>
<BindingsGenerator
OnlyRunXmlAdjuster="true"
Expand All @@ -41,6 +43,7 @@ This file is only used by binding projects.
ApiXmlInput="$(ApiOutputFile).class-parse"
ReferencedManagedLibraries="@(ReferencePath);@(ReferenceDependencyPaths)"
MonoAndroidFrameworkDirectories="$(_XATargetFrameworkDirectories)"
NetCoreRoot="$(NetCoreRoot)"
ToolPath="$(MonoAndroidToolsDirectory)"
ToolExe="$(BindingsGeneratorToolExe)"
Nullable="$(Nullable)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ It is shared between "legacy" binding projects and .NET 5 projects.
<GeneratedOutputPath Condition=" '$(GeneratedOutputPath)' == '' ">$(IntermediateOutputPath)generated\</GeneratedOutputPath>
<AndroidJavadocVerbosity Condition=" '$(AndroidJavadocVerbosity)' == '' ">intellisense</AndroidJavadocVerbosity>
<ApiOutputFile Condition=" '$(ApiOutputFile)' == '' ">$(IntermediateOutputPath)api.xml</ApiOutputFile>
<ClassParseToolExe Condition=" '$(UsingAndroidNETSdk)' == 'true' ">class-parse.dll</ClassParseToolExe>
<ClassParseToolExe Condition=" '$(ClassParseToolExe)' == '' ">class-parse.exe</ClassParseToolExe>
<BindingsGeneratorToolExe Condition=" '$(UsingAndroidNETSdk)' == 'true' ">generator.dll</BindingsGeneratorToolExe>
<BindingsGeneratorToolExe Condition=" '$(BindingsGeneratorToolExe)' == '' ">generator.exe</BindingsGeneratorToolExe>
<JavadocToMdocToolExe Condition=" '$(UsingAndroidNETSdk)' == 'true' ">javadoc-to-mdoc.dll</JavadocToMdocToolExe>
<JavadocToMdocToolExe Condition=" '$(JavadocToMdocToolExe)' == '' ">javadoc-to-mdoc.exe</JavadocToMdocToolExe>
<_GeneratorStampFile>$(_AndroidStampDirectory)generator.stamp</_GeneratorStampFile>
</PropertyGroup>

Expand Down Expand Up @@ -73,6 +79,7 @@ It is shared between "legacy" binding projects and .NET 5 projects.
ReferencedManagedLibraries="@(ReferencePath);@(ReferenceDependencyPaths)"
MonoAndroidFrameworkDirectories="$(_XATargetFrameworkDirectories)"
TypeMappingReportFile="$(GeneratedOutputPath)type-mapping.txt"
NetCoreRoot="$(NetCoreRoot)"
ToolPath="$(MonoAndroidToolsDirectory)"
ToolExe="$(BindingsGeneratorToolExe)"
LangVersion="$(LangVersion)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ This file is only used by binding projects. .NET 5 can eventually use it, once `
/>
<ImportJavaDoc
ContinueOnError="true"
NetCoreRoot="$(NetCoreRoot)"
ToolPath="$(MonoAndroidToolsDirectory)"
ToolExe="$(JavadocToMdocToolExe)"
JavaDocs="@(JavaDocIndex)"
References="@(ReferencePath);@(ReferenceDependencyPaths)"
Transforms="@(TransformFile)"
Expand Down
46 changes: 26 additions & 20 deletions src/Xamarin.Android.Build.Tasks/Tasks/AndroidDotnetToolTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ namespace Xamarin.Android.Tasks
/// </summary>
public abstract class AndroidDotnetToolTask : AndroidToolTask
{
/// <summary>
/// Path to the folder that contains dotnet / dotnet.exe.
/// </summary>
public string NetCoreRoot { get; set; }

/// <summary>
/// If `true`, this task should run `dotnet foo.dll` and `foo.exe` otherwise.
/// </summary>
Expand All @@ -31,61 +36,62 @@ public abstract class AndroidDotnetToolTask : AndroidToolTask

public override bool Execute ()
{
if (string.IsNullOrEmpty (ToolExe)) {
ToolExe = $"{BaseToolName}.exe";
}

var assemblyPath = Path.Combine (ToolPath, $"{BaseToolName}.dll");
if (File.Exists (assemblyPath)) {
if (Path.GetExtension (ToolExe) == ".dll") {
NeedsDotnet = true;
AssemblyPath = assemblyPath;
AssemblyPath = Path.Combine (ToolPath, ToolExe);
ToolPath = null;
ToolExe = null;

Log.LogDebugMessage ($"Using: dotnet {AssemblyPath}");
Log.LogDebugMessage ($"Using: {FindDotnet ()} {AssemblyPath}");
} else {
if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows) &&
!RuntimeInformation.FrameworkDescription.StartsWith ("Mono", StringComparison.OrdinalIgnoreCase)) {
// If not Windows and not running under Mono
NeedsMono = true;
AssemblyPath = Path.Combine (ToolPath, $"{BaseToolName}.exe");
AssemblyPath = Path.Combine (ToolPath, ToolExe);
ToolPath = null;
ToolExe = null;

Log.LogDebugMessage ($"Using: mono {AssemblyPath}");
Log.LogDebugMessage ($"Using: {FindMono ()} {AssemblyPath}");
} else {
// Otherwise running the .exe directly should work
Log.LogDebugMessage ($"Using: {GenerateFullPathToTool ()}");
Log.LogDebugMessage ($"Using: {Path.Combine (ToolPath, ToolExe)}");
}
}

return base.Execute ();
}

/// <summary>
/// The base tool name, such as "generator" for `generator.exe` and `dotnet generator.dll`
/// </summary>
protected abstract string BaseToolName {
get;
}

protected override string ToolName {
get {
if (NeedsDotnet)
return "dotnet";
if (NeedsMono)
return "mono";
return $"{BaseToolName}.exe";
return ToolExe;
}
}

protected override string GenerateFullPathToTool ()
{
if (NeedsDotnet)
return "dotnet";
return FindDotnet ();
if (NeedsMono)
return FindMono ();
return Path.Combine (ToolPath, ToolExe);
}

string FindDotnet ()
{
var dotnetPath = Environment.GetEnvironmentVariable ("DOTNET_HOST_PATH");
if (File.Exists (dotnetPath))
return dotnetPath;

if (Directory.Exists (NetCoreRoot))
return Path.Combine (NetCoreRoot, (RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ? "dotnet.exe" : "dotnet"));

return "dotnet";
}

const RegisteredTaskObjectLifetime Lifetime = RegisteredTaskObjectLifetime.Build;
const string MonoKey = nameof (AndroidDotnetToolTask) + "_Mono";
Expand Down
2 changes: 0 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/ClassParse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ public class ClassParse : AndroidDotnetToolTask
{
public override string TaskPrefix => "CLP";

protected override string BaseToolName => "class-parse";

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

Expand Down
2 changes: 0 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ protected override string GenerateCommandLineCommands ()
return cmd.ToString ();
}

protected override string BaseToolName => "generator";

protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance)
{
base.LogEventsFromTextOutput (singleLine, messageImportance);
Expand Down
2 changes: 0 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/ImportJavaDoc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ public class ImportJavaDoc : AndroidDotnetToolTask
[Required]
public string OutputDocDirectory { get; set; }

protected override string BaseToolName => "javadoc-to-mdoc";

protected override string GenerateCommandLineCommands ()
{
if (!Directory.Exists (OutputDocDirectory))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

using NUnit.Framework;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Xamarin.Android.Tasks;
using Xamarin.ProjectTools;

namespace Xamarin.Android.Build.Tests
{
[TestFixture]
[Category ("Node-5")]
public class AndroidDotnetToolTests : BaseTest
{
MockBuildEngine engine;
List<BuildErrorEventArgs> errors;
List<BuildWarningEventArgs> warnings;
List<BuildMessageEventArgs> messages;

[SetUp]
public void Setup ()
{
engine = new MockBuildEngine (TestContext.Out,
errors: errors = new List<BuildErrorEventArgs> (),
warnings: warnings = new List<BuildWarningEventArgs> (),
messages: messages = new List<BuildMessageEventArgs> ());
}

[Test]
public void ShouldUseFullToolPath ()
{
var dotnetDir = AndroidSdkResolver.GetDotNetPreviewPath ();
var dotnetPath = Path.Combine (dotnetDir, (TestEnvironment.IsWindows ? "dotnet.exe" : "dotnet"));
var classParseTask = new ClassParseTestTask {
BuildEngine = engine,
NetCoreRoot = dotnetDir,
ToolPath = Builder.UseDotNet ? TestEnvironment.DotNetAndroidSdkToolsDirectory : AndroidMSBuildDirectory,
ToolExe = Builder.UseDotNet ? "class-parse.dll" : "class-parse.exe",
};

Assert.True (classParseTask.Execute (), "Task should have succeeded.");
var expectedTool = Builder.UseDotNet ? dotnetPath : Path.Combine (AndroidMSBuildDirectory, "class-parse.exe");
Assert.IsTrue (messages.Any (m => m.Message.StartsWith (expectedTool)), "Task did not use expected tool path.");
}
}

public class ClassParseTestTask : AndroidDotnetToolTask
{
public override string TaskPrefix => "TEST";
protected override string GenerateCommandLineCommands ()
{
return GetCommandLineBuilder ().ToString ();
}
}
}

0 comments on commit 84d51b2

Please sign in to comment.