Skip to content

Commit 23e245b

Browse files
authored
[Xamarin.Android.Build.Tasks] AndroidDotnetToolTask & dotnet path (#7038)
Context: dotnet/java-interop@1bab47d 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. Note: `AndroidDotnetToolTask.Execute()` needs to clear the `ToolExe` and `ToolPath` properties [in order to ensure that `ToolTask`][0] calls our `GenerateFullPathToTool()` method override. [0]: https://github.com/dotnet/msbuild/blob/f1dae6ab690483458d37b8900f1d1e4a5fc72851/src/Utilities/ToolTask.cs#L463-L481
1 parent fb89198 commit 23e245b

File tree

8 files changed

+99
-26
lines changed

8 files changed

+99
-26
lines changed

src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Bindings.ClassParse.targets

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ This file is only used by binding projects.
3131
OutputFile="$(ApiOutputFile).class-parse"
3232
SourceJars="@(EmbeddedJar);@(InputJar)"
3333
DocumentationPaths="@(_AndroidDocumentationPath)"
34+
NetCoreRoot="$(NetCoreRoot)"
3435
ToolPath="$(MonoAndroidToolsDirectory)"
36+
ToolExe="$(ClassParseToolExe)"
3537
/>
3638
<BindingsGenerator
3739
OnlyRunXmlAdjuster="true"
@@ -41,6 +43,7 @@ This file is only used by binding projects.
4143
ApiXmlInput="$(ApiOutputFile).class-parse"
4244
ReferencedManagedLibraries="@(ReferencePath);@(ReferenceDependencyPaths)"
4345
MonoAndroidFrameworkDirectories="$(_XATargetFrameworkDirectories)"
46+
NetCoreRoot="$(NetCoreRoot)"
4447
ToolPath="$(MonoAndroidToolsDirectory)"
4548
ToolExe="$(BindingsGeneratorToolExe)"
4649
Nullable="$(Nullable)"

src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Bindings.Core.targets

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ It is shared between "legacy" binding projects and .NET 5 projects.
2020
<GeneratedOutputPath Condition=" '$(GeneratedOutputPath)' == '' ">$(IntermediateOutputPath)generated\</GeneratedOutputPath>
2121
<AndroidJavadocVerbosity Condition=" '$(AndroidJavadocVerbosity)' == '' ">intellisense</AndroidJavadocVerbosity>
2222
<ApiOutputFile Condition=" '$(ApiOutputFile)' == '' ">$(IntermediateOutputPath)api.xml</ApiOutputFile>
23+
<ClassParseToolExe Condition=" '$(UsingAndroidNETSdk)' == 'true' ">class-parse.dll</ClassParseToolExe>
24+
<ClassParseToolExe Condition=" '$(ClassParseToolExe)' == '' ">class-parse.exe</ClassParseToolExe>
25+
<BindingsGeneratorToolExe Condition=" '$(UsingAndroidNETSdk)' == 'true' ">generator.dll</BindingsGeneratorToolExe>
26+
<BindingsGeneratorToolExe Condition=" '$(BindingsGeneratorToolExe)' == '' ">generator.exe</BindingsGeneratorToolExe>
27+
<JavadocToMdocToolExe Condition=" '$(UsingAndroidNETSdk)' == 'true' ">javadoc-to-mdoc.dll</JavadocToMdocToolExe>
28+
<JavadocToMdocToolExe Condition=" '$(JavadocToMdocToolExe)' == '' ">javadoc-to-mdoc.exe</JavadocToMdocToolExe>
2329
<_GeneratorStampFile>$(_AndroidStampDirectory)generator.stamp</_GeneratorStampFile>
2430
</PropertyGroup>
2531

@@ -73,6 +79,7 @@ It is shared between "legacy" binding projects and .NET 5 projects.
7379
ReferencedManagedLibraries="@(ReferencePath);@(ReferenceDependencyPaths)"
7480
MonoAndroidFrameworkDirectories="$(_XATargetFrameworkDirectories)"
7581
TypeMappingReportFile="$(GeneratedOutputPath)type-mapping.txt"
82+
NetCoreRoot="$(NetCoreRoot)"
7683
ToolPath="$(MonoAndroidToolsDirectory)"
7784
ToolExe="$(BindingsGeneratorToolExe)"
7885
LangVersion="$(LangVersion)"

src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Bindings.Documentation.targets

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ This file is only used by binding projects. .NET 5 can eventually use it, once `
6464
/>
6565
<ImportJavaDoc
6666
ContinueOnError="true"
67+
NetCoreRoot="$(NetCoreRoot)"
6768
ToolPath="$(MonoAndroidToolsDirectory)"
69+
ToolExe="$(JavadocToMdocToolExe)"
6870
JavaDocs="@(JavaDocIndex)"
6971
References="@(ReferencePath);@(ReferenceDependencyPaths)"
7072
Transforms="@(TransformFile)"

src/Xamarin.Android.Build.Tasks/Tasks/AndroidDotnetToolTask.cs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ namespace Xamarin.Android.Tasks
1313
/// </summary>
1414
public abstract class AndroidDotnetToolTask : AndroidToolTask
1515
{
16+
/// <summary>
17+
/// Path to the folder that contains dotnet / dotnet.exe.
18+
/// </summary>
19+
public string NetCoreRoot { get; set; }
20+
1621
/// <summary>
1722
/// If `true`, this task should run `dotnet foo.dll` and `foo.exe` otherwise.
1823
/// </summary>
@@ -31,61 +36,65 @@ public abstract class AndroidDotnetToolTask : AndroidToolTask
3136

3237
public override bool Execute ()
3338
{
34-
if (string.IsNullOrEmpty (ToolExe)) {
35-
ToolExe = $"{BaseToolName}.exe";
36-
}
37-
38-
var assemblyPath = Path.Combine (ToolPath, $"{BaseToolName}.dll");
39-
if (File.Exists (assemblyPath)) {
39+
if (Path.GetExtension (ToolExe) == ".dll") {
4040
NeedsDotnet = true;
41-
AssemblyPath = assemblyPath;
41+
AssemblyPath = Path.Combine (ToolPath, ToolExe);
4242
ToolPath = null;
43+
ToolExe = null;
4344

44-
Log.LogDebugMessage ($"Using: dotnet {AssemblyPath}");
45+
Log.LogDebugMessage ($"Using: {FindDotnet ()} {AssemblyPath}");
4546
} else {
4647
if (!RuntimeInformation.IsOSPlatform (OSPlatform.Windows) &&
4748
!RuntimeInformation.FrameworkDescription.StartsWith ("Mono", StringComparison.OrdinalIgnoreCase)) {
4849
// If not Windows and not running under Mono
4950
NeedsMono = true;
50-
AssemblyPath = Path.Combine (ToolPath, $"{BaseToolName}.exe");
51+
AssemblyPath = Path.Combine (ToolPath, ToolExe);
5152
ToolPath = null;
53+
ToolExe = null;
5254

53-
Log.LogDebugMessage ($"Using: mono {AssemblyPath}");
55+
Log.LogDebugMessage ($"Using: {FindMono ()} {AssemblyPath}");
5456
} else {
5557
// Otherwise running the .exe directly should work
56-
Log.LogDebugMessage ($"Using: {GenerateFullPathToTool ()}");
58+
Log.LogDebugMessage ($"Using: {Path.Combine (ToolPath, ToolExe)}");
5759
}
5860
}
5961

6062
return base.Execute ();
6163
}
6264

63-
/// <summary>
64-
/// The base tool name, such as "generator" for `generator.exe` and `dotnet generator.dll`
65-
/// </summary>
66-
protected abstract string BaseToolName {
67-
get;
68-
}
69-
7065
protected override string ToolName {
7166
get {
7267
if (NeedsDotnet)
7368
return "dotnet";
7469
if (NeedsMono)
7570
return "mono";
76-
return $"{BaseToolName}.exe";
71+
return ToolExe;
7772
}
7873
}
7974

8075
protected override string GenerateFullPathToTool ()
8176
{
8277
if (NeedsDotnet)
83-
return "dotnet";
78+
return FindDotnet ();
8479
if (NeedsMono)
8580
return FindMono ();
8681
return Path.Combine (ToolPath, ToolExe);
8782
}
8883

84+
string FindDotnet ()
85+
{
86+
if (Directory.Exists (NetCoreRoot)) {
87+
var dotnetPath = Path.Combine (NetCoreRoot, (RuntimeInformation.IsOSPlatform (OSPlatform.Windows) ? "dotnet.exe" : "dotnet"));
88+
if (File.Exists (dotnetPath))
89+
return dotnetPath;
90+
}
91+
92+
var dotnetHostPath = Environment.GetEnvironmentVariable ("DOTNET_HOST_PATH");
93+
if (File.Exists (dotnetHostPath))
94+
return dotnetHostPath;
95+
96+
return "dotnet";
97+
}
8998

9099
const RegisteredTaskObjectLifetime Lifetime = RegisteredTaskObjectLifetime.Build;
91100
const string MonoKey = nameof (AndroidDotnetToolTask) + "_Mono";

src/Xamarin.Android.Build.Tasks/Tasks/ClassParse.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ public class ClassParse : AndroidDotnetToolTask
1010
{
1111
public override string TaskPrefix => "CLP";
1212

13-
protected override string BaseToolName => "class-parse";
14-
1513
[Required]
1614
public string OutputFile { get; set; }
1715

src/Xamarin.Android.Build.Tasks/Tasks/Generator.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,6 @@ protected override string GenerateCommandLineCommands ()
241241
return cmd.ToString ();
242242
}
243243

244-
protected override string BaseToolName => "generator";
245-
246244
protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance)
247245
{
248246
base.LogEventsFromTextOutput (singleLine, messageImportance);

src/Xamarin.Android.Build.Tasks/Tasks/ImportJavaDoc.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@ public class ImportJavaDoc : AndroidDotnetToolTask
2424
[Required]
2525
public string OutputDocDirectory { get; set; }
2626

27-
protected override string BaseToolName => "javadoc-to-mdoc";
28-
2927
protected override string GenerateCommandLineCommands ()
3028
{
3129
if (!Directory.Exists (OutputDocDirectory))
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using Microsoft.Build.Framework;
3+
using Microsoft.Build.Utilities;
4+
5+
using NUnit.Framework;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using Xamarin.Android.Tasks;
10+
using Xamarin.ProjectTools;
11+
12+
namespace Xamarin.Android.Build.Tests
13+
{
14+
[TestFixture]
15+
[Category ("Node-5")]
16+
public class AndroidDotnetToolTests : BaseTest
17+
{
18+
MockBuildEngine engine;
19+
List<BuildErrorEventArgs> errors;
20+
List<BuildWarningEventArgs> warnings;
21+
List<BuildMessageEventArgs> messages;
22+
23+
[SetUp]
24+
public void Setup ()
25+
{
26+
engine = new MockBuildEngine (TestContext.Out,
27+
errors: errors = new List<BuildErrorEventArgs> (),
28+
warnings: warnings = new List<BuildWarningEventArgs> (),
29+
messages: messages = new List<BuildMessageEventArgs> ());
30+
}
31+
32+
[Test]
33+
public void ShouldUseFullToolPath ()
34+
{
35+
var dotnetDir = AndroidSdkResolver.GetDotNetPreviewPath ();
36+
var dotnetPath = Path.Combine (dotnetDir, (TestEnvironment.IsWindows ? "dotnet.exe" : "dotnet"));
37+
var classParseTask = new ClassParseTestTask {
38+
BuildEngine = engine,
39+
NetCoreRoot = dotnetDir,
40+
ToolPath = Builder.UseDotNet ? TestEnvironment.DotNetAndroidSdkToolsDirectory : AndroidMSBuildDirectory,
41+
ToolExe = Builder.UseDotNet ? "class-parse.dll" : "class-parse.exe",
42+
};
43+
44+
Assert.True (classParseTask.Execute (), "Task should have succeeded.");
45+
var expectedTool = Builder.UseDotNet ? dotnetPath : Path.Combine (AndroidMSBuildDirectory, "class-parse.exe");
46+
Assert.IsTrue (messages.Any (m => m.Message.StartsWith (expectedTool)), "Task did not use expected tool path.");
47+
}
48+
}
49+
50+
public class ClassParseTestTask : AndroidDotnetToolTask
51+
{
52+
public override string TaskPrefix => "TEST";
53+
protected override string GenerateCommandLineCommands ()
54+
{
55+
return GetCommandLineBuilder ().ToString ();
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)