diff --git a/src/Microsoft.Android.Build.Tasks/Tasks/GetAndroidPackageName.cs b/src/Microsoft.Android.Build.Tasks/Tasks/GetAndroidPackageName.cs
index d4c6ddebde4..535a3066f24 100644
--- a/src/Microsoft.Android.Build.Tasks/Tasks/GetAndroidPackageName.cs
+++ b/src/Microsoft.Android.Build.Tasks/Tasks/GetAndroidPackageName.cs
@@ -36,7 +36,8 @@
namespace Microsoft.Android.Tasks
{
- public class GetAndroidPackageName : AndroidTask
+ [MSBuildMultiThreadableTask]
+ public class GetAndroidPackageName : MultithreadedAndroidTask
{
public override string TaskPrefix => "GAP";
@@ -52,13 +53,16 @@ public class GetAndroidPackageName : AndroidTask
public override bool RunTask ()
{
- if (!ManifestFile.IsNullOrEmpty () && File.Exists (ManifestFile)) {
- using var stream = File.OpenRead (ManifestFile);
- using var reader = XmlReader.Create (stream);
- if (reader.MoveToContent () == XmlNodeType.Element) {
- var package = reader.GetAttribute ("package");
- if (!package.IsNullOrEmpty ()) {
- PackageName = ManifestPlaceholderResolver.Replace (ManifestPlaceholders, package);
+ if (!ManifestFile.IsNullOrEmpty ()) {
+ var manifestFile = TaskEnvironment.GetAbsolutePath (ManifestFile);
+ if (File.Exists (manifestFile)) {
+ using var stream = File.OpenRead (manifestFile);
+ using var reader = XmlReader.Create (stream);
+ if (reader.MoveToContent () == XmlNodeType.Element) {
+ var package = reader.GetAttribute ("package");
+ if (!package.IsNullOrEmpty ()) {
+ PackageName = ManifestPlaceholderResolver.Replace (ManifestPlaceholders, package);
+ }
}
}
}
diff --git a/src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/GetAndroidPackageNameTests.cs b/src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/GetAndroidPackageNameTests.cs
index 2d805af152e..b1e199b0cd4 100644
--- a/src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/GetAndroidPackageNameTests.cs
+++ b/src/Microsoft.Android.Build.Tasks/Tests/Microsoft.Android.Build.Tasks.Tests/GetAndroidPackageNameTests.cs
@@ -1,4 +1,7 @@
+using System;
using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
using Microsoft.Build.Framework;
using NUnit.Framework;
using Microsoft.Android.Tasks;
@@ -9,6 +12,69 @@ namespace Xamarin.Android.Build.Tests {
[Parallelizable (ParallelScope.Children)]
public class GetAndroidPackageNameTests {
+ [TestCase (false, true, "com.application.id", "com.example.some_name")]
+ [TestCase (true, true, null, "com.example.some_name")]
+ [TestCase (false, false, null, "Example.Example")]
+ [TestCase (true, false, "com.application.id", "com.application.id")]
+ public void ManifestPaths (bool absolutePath, bool exists, string? packageName, string expected)
+ {
+ string directory = CreateProjectDirectory ();
+ try {
+ string path = Path.Combine (directory, "AndroidManifest.xml");
+ if (exists) {
+ File.WriteAllText (path, "");
+ }
+ string manifestFile = absolutePath ? path : "AndroidManifest.xml";
+ var task = new GetAndroidPackageName {
+ BuildEngine = new MockBuildEngine (TestContext.Out),
+ TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment (directory, new Dictionary ()),
+ ManifestFile = manifestFile,
+ AssemblyName = "Example",
+ PackageName = packageName,
+ ManifestPlaceholders = ["prefix=com.example"],
+ };
+
+ Assert.IsTrue (task.Execute ());
+ Assert.AreEqual (expected, task.PackageName);
+ Assert.AreEqual (manifestFile, task.ManifestFile, "Rooting file I/O must not change the public input.");
+ } finally {
+ Directory.Delete (directory, recursive: true);
+ }
+ }
+
+ [Test]
+ public void ConcurrentProjectDirectories ()
+ {
+ string root = CreateProjectDirectory ();
+ try {
+ Parallel.For (0, 2, i => {
+ string directory = Path.Combine (root, $"project{i}");
+ string package = $"com.project{i}";
+ Directory.CreateDirectory (directory);
+ File.WriteAllText (Path.Combine (directory, "AndroidManifest.xml"), $"");
+ var task = new GetAndroidPackageName {
+ BuildEngine = new MockBuildEngine (TextWriter.Null),
+ TaskEnvironment = TaskEnvironment.CreateWithProjectDirectoryAndEnvironment (directory, new Dictionary ()),
+ ManifestFile = "AndroidManifest.xml",
+ AssemblyName = "Fallback",
+ };
+
+ Assert.IsTrue (task.Execute ());
+ Assert.AreEqual (package, task.PackageName);
+ });
+ } finally {
+ Directory.Delete (root, recursive: true);
+ }
+ }
+
+ static string CreateProjectDirectory ()
+ {
+ string directory = Path.Combine (TestContext.CurrentContext.WorkDirectory, "GetAndroidPackageName", Guid.NewGuid ().ToString ("N"));
+ Directory.CreateDirectory (directory);
+ Assert.AreNotEqual (Directory.GetCurrentDirectory (), directory);
+ return directory;
+ }
+
[TestCase ("com.example.for", "for")]
[TestCase ("com.class.example", "class")]
[TestCase ("com.example.true", "true")]