Skip to content

Commit

Permalink
[BaseTasks] add ABI detection for RIDs (#121)
Browse files Browse the repository at this point in the history
Context: dotnet/android#5432

Two cases currently do not work in .NET 6:

 1. When an Android App project includes native libraries which are
    in directory names consisting of .NET runtime identifiers instead
    of Android ABI names, e.g.

        android-arm/libfoo.so
        android-arm64/libfoo.so
        android-x86/libfoo.so
        android-x64/libfoo.so

 2. When a NuGet package places native libraries into a `native`
    directory *between* the `$(RuntimeIdentifier)` directory and the
    native library, a'la [`SQLitePCLRaw.lib.e_sqlite3.linux`][0]:

        runtimes/linux-arm/native/libe_sqlite3.so

Fix case (1) by checking using
`AndroidRidAbiHelper.RuntimeIdentifierToAbi()` on the directory name
to determine the Android ABI of the library.

Fix case (2) by also checking the native library's parent parent
directory name against Android ABI names and Runtime Identifiers.
This allows us to correctly associate

	runtimes/android-arm64/native/libe_sqlite3.so

as an arm64-v8a native library.

I implemented these two cases as fallbacks to the existing logic.

I think this will be fine for the behavior to be in "legacy"
Xamarin.Android as well as .NET 6.

I added tests for `AndroidRidAbiHelper`, since we had none before.

[0]: https://www.nuget.org/packages/SQLitePCLRaw.lib.e_sqlite3.linux/1.1.14
  • Loading branch information
jonathanpeppers authored Jun 4, 2021
1 parent 79e3b97 commit 90d7621
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 7 deletions.
27 changes: 20 additions & 7 deletions src/Microsoft.Android.Build.BaseTasks/AndroidRidAbiHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,27 @@ public static class AndroidRidAbiHelper
public static string GetNativeLibraryAbi (string lib)
{
// The topmost directory the .so file is contained within
var dir = Path.GetFileName (Path.GetDirectoryName (lib)).ToLowerInvariant ();
if (dir.StartsWith ("interpreter-", StringComparison.Ordinal)) {
dir = dir.Substring (12);
var dir = Directory.GetParent (lib);
var dirName = dir.Name.ToLowerInvariant ();
if (dirName.StartsWith ("interpreter-", StringComparison.Ordinal)) {
dirName = dirName.Substring ("interpreter-".Length);
}
if (ValidAbis.Contains (dir)) {
return dir;
if (ValidAbis.Contains (dirName)) {
return dirName;
}

// Look for a directory with a RID as a name, such as:
// android-arm64/libfoo.so
var abi = RuntimeIdentifierToAbi (dirName);
if (!string.IsNullOrEmpty (abi))
return abi;

// Try one directory higher, such as:
// packages/sqlitepclraw.lib.e_sqlite3.android/1.1.11/runtimes/android-arm64/native/libe_sqlite3.so
abi = RuntimeIdentifierToAbi (dir.Parent.Name.ToLowerInvariant ());
if (!string.IsNullOrEmpty (abi))
return abi;

return null;
}

Expand All @@ -41,8 +55,7 @@ public static string GetNativeLibraryAbi (ITaskItem lib)
// First, try nominal "Link" path.
var link = lib.GetMetadata ("Link");
if (!string.IsNullOrWhiteSpace (link)) {
var linkdirs = link.ToLowerInvariant ().Split ('/', '\\');
lib_abi = ValidAbis.Where (p => linkdirs.Contains (p)).FirstOrDefault ();
lib_abi = GetNativeLibraryAbi (link);
}

// Check for a RuntimeIdentifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
using System.Collections.Generic;
using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NUnit.Framework;

namespace Microsoft.Android.Build.BaseTasks.Tests
{
[TestFixture]
public class AndroidRidAbiHelperTests
{
static object [] StringValueSource = new object [] {
new[] {
/* input */ "armeabi-v7a/libfoo.so",
/* expected */ "armeabi-v7a"
},
new[] {
/* input */ "arm64-v8a/libfoo.so",
/* expected */ "arm64-v8a"
},
new[] {
/* input */ "x86/libfoo.so",
/* expected */ "x86"
},
new[] {
/* input */ "x86_64/libfoo.so",
/* expected */ "x86_64"
},
new[] {
/* input */ "android-arm/libfoo.so",
/* expected */ "armeabi-v7a"
},
new[] {
/* input */ "android-arm64/libfoo.so",
/* expected */ "arm64-v8a"
},
new[] {
/* input */ "android-x86/libfoo.so",
/* expected */ "x86"
},
new[] {
/* input */ "android-x64/libfoo.so",
/* expected */ "x86_64"
},
new[] {
/* input */ "android-arm/native/libfoo.so",
/* expected */ "armeabi-v7a"
},
new[] {
/* input */ "android-arm64/native/libfoo.so",
/* expected */ "arm64-v8a"
},
new[] {
/* input */ "android-x86/native/libfoo.so",
/* expected */ "x86"
},
new[] {
/* input */ "android-x64/native/libfoo.so",
/* expected */ "x86_64"
},
new[] {
/* input */ "android.21-x64/native/libfoo.so",
/* expected */ "x86_64"
},
new[] {
/* input */ "packages/sqlitepclraw.lib.e_sqlite3.android/1.1.11/runtimes/android-arm64/native/libe_sqlite3.so",
/* expected */ "arm64-v8a"
}
};

[Test]
[TestCaseSource (nameof (StringValueSource))]
public void StringValue (string input, string expected)
{
Assert.AreEqual (expected, AndroidRidAbiHelper.GetNativeLibraryAbi (input));
}

static object [] ITaskItemValueSource = new object [] {
new object [] {
/* input */
new TaskItem("armeabi-v7a/libfoo.so"),
/* expected */
"armeabi-v7a"
},
new object [] {
/* input */
new TaskItem("libabi.so", new Dictionary<string,string> {
{ "Abi", "armeabi-v7a" }
}),
/* expected */
"armeabi-v7a"
},
new object [] {
/* input */
new TaskItem("librid.so", new Dictionary<string,string> {
{ "RuntimeIdentifier", "android-arm" }
}),
/* expected */
"armeabi-v7a"
},
new object [] {
/* input */
new TaskItem("liblink.so", new Dictionary<string,string> {
{ "Link", "armeabi-v7a/libfoo.so" }
}),
/* expected */
"armeabi-v7a"
},
new object [] {
/* input */
new TaskItem("liblink.so", new Dictionary<string,string> {
{ "Link", "x86/libfoo.so" }
}),
/* expected */
"x86"
},
new object [] {
/* input */
new TaskItem("liblink.so", new Dictionary<string,string> {
{ "Link", "x86_64/libfoo.so" }
}),
/* expected */
"x86_64"
},
new object [] {
/* input */
new TaskItem("libridlink.so", new Dictionary<string,string> {
{ "Link", "android-arm/libfoo.so" }
}),
/* expected */
"armeabi-v7a"
},
};

[Test]
[TestCaseSource (nameof (ITaskItemValueSource))]
public void ITaskItemValue (ITaskItem input, string expected)
{
Assert.AreEqual (expected, AndroidRidAbiHelper.GetNativeLibraryAbi (input));
}
}
}

0 comments on commit 90d7621

Please sign in to comment.