Skip to content

Commit 90d7621

Browse files
[BaseTasks] add ABI detection for RIDs (#121)
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
1 parent 79e3b97 commit 90d7621

File tree

2 files changed

+162
-7
lines changed

2 files changed

+162
-7
lines changed

src/Microsoft.Android.Build.BaseTasks/AndroidRidAbiHelper.cs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,27 @@ public static class AndroidRidAbiHelper
1919
public static string GetNativeLibraryAbi (string lib)
2020
{
2121
// The topmost directory the .so file is contained within
22-
var dir = Path.GetFileName (Path.GetDirectoryName (lib)).ToLowerInvariant ();
23-
if (dir.StartsWith ("interpreter-", StringComparison.Ordinal)) {
24-
dir = dir.Substring (12);
22+
var dir = Directory.GetParent (lib);
23+
var dirName = dir.Name.ToLowerInvariant ();
24+
if (dirName.StartsWith ("interpreter-", StringComparison.Ordinal)) {
25+
dirName = dirName.Substring ("interpreter-".Length);
2526
}
26-
if (ValidAbis.Contains (dir)) {
27-
return dir;
27+
if (ValidAbis.Contains (dirName)) {
28+
return dirName;
2829
}
30+
31+
// Look for a directory with a RID as a name, such as:
32+
// android-arm64/libfoo.so
33+
var abi = RuntimeIdentifierToAbi (dirName);
34+
if (!string.IsNullOrEmpty (abi))
35+
return abi;
36+
37+
// Try one directory higher, such as:
38+
// packages/sqlitepclraw.lib.e_sqlite3.android/1.1.11/runtimes/android-arm64/native/libe_sqlite3.so
39+
abi = RuntimeIdentifierToAbi (dir.Parent.Name.ToLowerInvariant ());
40+
if (!string.IsNullOrEmpty (abi))
41+
return abi;
42+
2943
return null;
3044
}
3145

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

4861
// Check for a RuntimeIdentifier
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
using System.Collections.Generic;
2+
using Microsoft.Android.Build.Tasks;
3+
using Microsoft.Build.Framework;
4+
using Microsoft.Build.Utilities;
5+
using NUnit.Framework;
6+
7+
namespace Microsoft.Android.Build.BaseTasks.Tests
8+
{
9+
[TestFixture]
10+
public class AndroidRidAbiHelperTests
11+
{
12+
static object [] StringValueSource = new object [] {
13+
new[] {
14+
/* input */ "armeabi-v7a/libfoo.so",
15+
/* expected */ "armeabi-v7a"
16+
},
17+
new[] {
18+
/* input */ "arm64-v8a/libfoo.so",
19+
/* expected */ "arm64-v8a"
20+
},
21+
new[] {
22+
/* input */ "x86/libfoo.so",
23+
/* expected */ "x86"
24+
},
25+
new[] {
26+
/* input */ "x86_64/libfoo.so",
27+
/* expected */ "x86_64"
28+
},
29+
new[] {
30+
/* input */ "android-arm/libfoo.so",
31+
/* expected */ "armeabi-v7a"
32+
},
33+
new[] {
34+
/* input */ "android-arm64/libfoo.so",
35+
/* expected */ "arm64-v8a"
36+
},
37+
new[] {
38+
/* input */ "android-x86/libfoo.so",
39+
/* expected */ "x86"
40+
},
41+
new[] {
42+
/* input */ "android-x64/libfoo.so",
43+
/* expected */ "x86_64"
44+
},
45+
new[] {
46+
/* input */ "android-arm/native/libfoo.so",
47+
/* expected */ "armeabi-v7a"
48+
},
49+
new[] {
50+
/* input */ "android-arm64/native/libfoo.so",
51+
/* expected */ "arm64-v8a"
52+
},
53+
new[] {
54+
/* input */ "android-x86/native/libfoo.so",
55+
/* expected */ "x86"
56+
},
57+
new[] {
58+
/* input */ "android-x64/native/libfoo.so",
59+
/* expected */ "x86_64"
60+
},
61+
new[] {
62+
/* input */ "android.21-x64/native/libfoo.so",
63+
/* expected */ "x86_64"
64+
},
65+
new[] {
66+
/* input */ "packages/sqlitepclraw.lib.e_sqlite3.android/1.1.11/runtimes/android-arm64/native/libe_sqlite3.so",
67+
/* expected */ "arm64-v8a"
68+
}
69+
};
70+
71+
[Test]
72+
[TestCaseSource (nameof (StringValueSource))]
73+
public void StringValue (string input, string expected)
74+
{
75+
Assert.AreEqual (expected, AndroidRidAbiHelper.GetNativeLibraryAbi (input));
76+
}
77+
78+
static object [] ITaskItemValueSource = new object [] {
79+
new object [] {
80+
/* input */
81+
new TaskItem("armeabi-v7a/libfoo.so"),
82+
/* expected */
83+
"armeabi-v7a"
84+
},
85+
new object [] {
86+
/* input */
87+
new TaskItem("libabi.so", new Dictionary<string,string> {
88+
{ "Abi", "armeabi-v7a" }
89+
}),
90+
/* expected */
91+
"armeabi-v7a"
92+
},
93+
new object [] {
94+
/* input */
95+
new TaskItem("librid.so", new Dictionary<string,string> {
96+
{ "RuntimeIdentifier", "android-arm" }
97+
}),
98+
/* expected */
99+
"armeabi-v7a"
100+
},
101+
new object [] {
102+
/* input */
103+
new TaskItem("liblink.so", new Dictionary<string,string> {
104+
{ "Link", "armeabi-v7a/libfoo.so" }
105+
}),
106+
/* expected */
107+
"armeabi-v7a"
108+
},
109+
new object [] {
110+
/* input */
111+
new TaskItem("liblink.so", new Dictionary<string,string> {
112+
{ "Link", "x86/libfoo.so" }
113+
}),
114+
/* expected */
115+
"x86"
116+
},
117+
new object [] {
118+
/* input */
119+
new TaskItem("liblink.so", new Dictionary<string,string> {
120+
{ "Link", "x86_64/libfoo.so" }
121+
}),
122+
/* expected */
123+
"x86_64"
124+
},
125+
new object [] {
126+
/* input */
127+
new TaskItem("libridlink.so", new Dictionary<string,string> {
128+
{ "Link", "android-arm/libfoo.so" }
129+
}),
130+
/* expected */
131+
"armeabi-v7a"
132+
},
133+
};
134+
135+
[Test]
136+
[TestCaseSource (nameof (ITaskItemValueSource))]
137+
public void ITaskItemValue (ITaskItem input, string expected)
138+
{
139+
Assert.AreEqual (expected, AndroidRidAbiHelper.GetNativeLibraryAbi (input));
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)