diff --git a/.external b/.external
index 8d6cb0be54a..8d72a200199 100644
--- a/.external
+++ b/.external
@@ -1 +1 @@
-xamarin/monodroid:main@848d1277b76a599d8a280d58ec06e95477b4a7e5
+xamarin/monodroid:main@cb01503327f7723ec138ec4cc051610fecee1bf7
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 777abde3cf6..8917bb93a8a 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -44,6 +44,12 @@
"port": 10000,
"preLaunchTask": "run-sample-under-dotnet",
},
+ {
+ "name": "Attach to Process",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "${input:processid}"
+ }
],
"inputs": [
{
@@ -52,6 +58,12 @@
"default": "Debug",
"description": "The Build Configuration",
"options": [ "Debug", "Release"]
- }
+ },
+ {
+ "id": "processid",
+ "type": "promptString",
+ "default": "0",
+ "description": "Enter dotnet build process id reported when setting the env var MSBUILDDEBUGONSTART=2",
+ },
]
}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
index e79dfcacc8d..4b652be3076 100644
--- a/.vscode/tasks.json
+++ b/.vscode/tasks.json
@@ -127,8 +127,8 @@
"label": "build-sample-under-dotnet",
"type": "shell",
"windows": { "command": "dotnet-local.cmd build ${input:project} -p:Configuration=${input:configuration} -t:${input:target} -bl:${input:target}.binlog", },
- "linux": { "command": "./dotnet-local.sh build ${input:project} -p:Configuration=${input:configuration} -t:${input:target} -bl:${input:target}.binlog",},
- "osx": { "command": "./dotnet-local.sh build ${input:project} -p:Configuration=${input:configuration} -t:${input:target} -bl:${input:target}.binlog",},
+ "linux": { "command": "${input:debugbuildtasks} ./dotnet-local.sh build ${input:project} -p:Configuration=${input:configuration} -t:${input:target} -bl:${input:target}.binlog",},
+ "osx": { "command": "${input:debugbuildtasks} ./dotnet-local.sh build ${input:project} -p:Configuration=${input:configuration} -t:${input:target} -bl:${input:target}.binlog",},
"group": {
"kind": "build",
"isDefault": true
@@ -141,8 +141,8 @@
"label": "run-sample-under-dotnet",
"type": "shell",
"windows": { "command": "dotnet-local.cmd build ${input:project} \"-t:Run\" --no-restore -p:TargetFramework=${input:targetframework} -p:Configuration=${input:configuration} -p:AndroidAttachDebugger=${input:attach} -bl:run.binlog", },
- "linux": { "command": "./dotnet-local.sh build ${input:project} \"-t:Run\" --no-restore -p:TargetFramework=${input:targetframework} -p:Configuration=${input:configuration} -p:AndroidAttachDebugger=${input:attach} -bl:run.binlog",},
- "osx": { "command": "./dotnet-local.sh build ${input:project} \"-t:Run\" --no-restore -p:TargetFramework=${input:targetframework} -p:Configuration=${input:configuration} -p:AndroidAttachDebugger=${input:attach} -bl:run.binlog",},
+ "linux": { "command": "${input:debugbuildtasks} ./dotnet-local.sh build ${input:project} \"-t:Run\" --no-restore -p:TargetFramework=${input:targetframework} -p:Configuration=${input:configuration} -p:AndroidAttachDebugger=${input:attach} -bl:run.binlog",},
+ "osx": { "command": "${input:debugbuildtasks} ./dotnet-local.sh build ${input:project} \"-t:Run\" --no-restore -p:TargetFramework=${input:targetframework} -p:Configuration=${input:configuration} -p:AndroidAttachDebugger=${input:attach} -bl:run.binlog",},
"group": {
"kind": "build",
"isDefault": true
@@ -220,5 +220,15 @@
"Everything",
]
},
+ {
+ "id": "debugbuildtasks",
+ "type": "pickString",
+ "default": "",
+ "description": "Debug Build Tasks?",
+ "options": [
+ "",
+ "MSBUILDDEBUGONSTART=2"
+ ]
+ },
]
}
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index eb9b8dfd3c9..3f855aae763 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -36,7 +36,7 @@
* Bump first digit of the patch version for feature releases (and reset the first two digits to 0)
-->
34.99.0
- preview.2
+ preview.3
diff --git a/Documentation/guides/MSBuildBestPractices.md b/Documentation/guides/MSBuildBestPractices.md
index bca45d30b5e..69c1b49d057 100644
--- a/Documentation/guides/MSBuildBestPractices.md
+++ b/Documentation/guides/MSBuildBestPractices.md
@@ -5,6 +5,74 @@ This guide is a work-in-progress, but really has two main goals:
- What are good MSBuild practice in relation to what we already have
going on in Xamarin.Android MSBuild targets?
+## Debugging MSBuild Tasks
+
+One thing that is very useful is the ability to debug your Tasks while
+they are being run on a build process. This is possible thanks to the
+`MSBUILDDEBUGONSTART` environment variable. When set to `2` this will
+force MSBuild to wait for a debugger connection before continuing.
+You will see the following prompt.
+
+```dotnetcli
+Waiting for debugger to attach (dotnet PID 13001). Press enter to continue...
+```
+
+You can then use VS or VSCode to attach to this process and debug you tasks.
+
+In the case of .NET Android we need to do a couple of thing first though. Firstly
+we need to disable the use of `ILRepacker` on the `Xamarin.Android.Build.Tasks`
+assembly. This is because `ILRepacker` does NOT handle debug symbols very well.
+Assemblies it generates seem to be JIT optimized so the debugger will not load
+the symbols. A new MSBuild property has been introduced to disable this feature
+while debugging. `_ILRepackEnabled` can be set as an environment variable which
+MSBuild will pickup. You will also need to build the `Debug` Configuration.
+
+```dotnetcli
+export CONFIGURATION=Debug
+make prepare && _ILRepackEnabled=false make jenkins
+```
+
+This will disable the `ILRepacker` for the build.
+
+You can then start your test app with the `dotnet-local` script (so it uses your build)
+
+### [MacOS](#tab/macos)
+
+```dotnetcli
+MSBUILDDEBUGONSTART=2 ~//dotnet-local.sh build -m:1
+```
+
+### [Linux](#tab/linux)
+
+```dotnetcli
+MSBUILDDEBUGONSTART=2 ~//dotnet-local.sh build -m:1
+```
+
+### [Windows](#tab/windows)
+
+```dotnetcli
+set MSBUILDDEBUGONSTART=2
+~//dotnet-local.cmd build -m:1
+```
+
+---
+
+Note: the `-m:1` is important as it restricts MSBuild to 1 node.
+
+Once MSBuild starts it will print the following
+
+```dotnetcli
+Waiting for debugger to attach (dotnet PID xxxx). Press enter to continue...
+```
+
+You need to copy the PID value so we can use this in the IDE. For Visual Studio you can use the `Attach to Process` menu option, while you have the Xamarin.Android.sln solution open. For VSCode open the workspace then use the `Attach to Process` Run and Debug option. You will be prompted for the PID and it will then connect.
+
+Once connected go back to your command prompt and press ENTER so that the MSBuild process can continue.
+
+You will be able to set breakpoints in Tasks (but not Targets) and step through code from this point on.
+
+If you want to test in-tree using the same the `build-sample-under-dotnet` command will ask you if you want to debug MSBuild tasks and fill in the `MSBUILDDEBUGONSTART` for you. The PID text will appear in the `Terminal` window in VSCode. In addition the `run-sample-under-dotnet` command will ask the same.
+
## Naming
MSBuild targets, properties, and item groups are prefixed with an
diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml
index 7a77f691e7c..611da9535ef 100644
--- a/build-tools/automation/azure-pipelines.yaml
+++ b/build-tools/automation/azure-pipelines.yaml
@@ -317,7 +317,7 @@ stages:
- stage: maui_tests
displayName: MAUI Tests
dependsOn: mac_build
- condition: and(eq(dependencies.mac_build.result, 'Succeeded'), eq(variables['System.PullRequest.TargetBranch'], 'main'))
+ condition: and(eq(dependencies.mac_build.result, 'Succeeded'), eq(variables['RunMAUITestJob'], 'true'))
jobs:
# Check - "Xamarin.Android (MAUI Tests MAUI Integration)"
- job: maui_tests_integration
diff --git a/build-tools/automation/yaml-templates/variables.yaml b/build-tools/automation/yaml-templates/variables.yaml
index 07b09b01c8a..13a1f774a18 100644
--- a/build-tools/automation/yaml-templates/variables.yaml
+++ b/build-tools/automation/yaml-templates/variables.yaml
@@ -52,3 +52,5 @@ variables:
value: 34
- name: ExcludedNightlyNUnitCategories
value: 'cat != SystemApplication & cat != TimeZoneInfo & cat != Localization'
+- name: RunMAUITestJob
+ value: true
diff --git a/build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs b/build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs
index 90295cc8504..67dc9cdc971 100644
--- a/build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs
+++ b/build-tools/xaprepare/xaprepare/Application/RegexProgramVersionParser.cs
@@ -12,78 +12,78 @@ namespace Xamarin.Android.Prepare
/// is set to any other value only that line is taken into consideration. This is
/// done to make processing less ambiguous and faster.
///
- class RegexProgramVersionParser : ProgramVersionParser
- {
- const string VersionGroupName = "Version";
- static readonly char[] LineSeparator = new [] { '\n' };
+ class RegexProgramVersionParser : ProgramVersionParser
+ {
+ public const string VersionGroupName = "Version";
+ public static readonly char[] LineSeparator = new [] { '\n' };
- Regex rx;
+ Regex rx;
- public RegexProgramVersionParser (string programName, string versionArguments, Regex regex, uint versionOutputLine = 0, Log? log = null)
- : base (programName, versionArguments, versionOutputLine, log)
- {
- if (regex == null)
- throw new ArgumentNullException (nameof (regex));
- rx = regex;
- }
+ public RegexProgramVersionParser (string programName, string versionArguments, Regex regex, uint versionOutputLine = 0, Log? log = null)
+ : base (programName, versionArguments, versionOutputLine, log)
+ {
+ if (regex == null)
+ throw new ArgumentNullException (nameof (regex));
+ rx = regex;
+ }
- public RegexProgramVersionParser (string programName, string versionArguments, string regex, uint versionOutputLine = 0, Log? log = null)
- : base (programName, versionArguments, versionOutputLine, log)
- {
- if (String.IsNullOrEmpty (regex))
- throw new ArgumentException ("must not be null or empty", nameof (regex));
+ public RegexProgramVersionParser (string programName, string versionArguments, string regex, uint versionOutputLine = 0, Log? log = null)
+ : base (programName, versionArguments, versionOutputLine, log)
+ {
+ if (String.IsNullOrEmpty (regex))
+ throw new ArgumentException ("must not be null or empty", nameof (regex));
- rx = new Regex (regex, RegexOptions.Compiled);
- }
+ rx = new Regex (regex, RegexOptions.Compiled);
+ }
- protected override string ParseVersion (string programOutput)
- {
- string output = programOutput.Trim ();
- if (String.IsNullOrEmpty (output)) {
- Log.WarningLine ($"Unable to parse version of {ProgramName} because version output was empty");
- return DefaultVersionString;
- }
+ protected override string ParseVersion (string programOutput)
+ {
+ string output = programOutput.Trim ();
+ if (String.IsNullOrEmpty (output)) {
+ Log.WarningLine ($"Unable to parse version of {ProgramName} because version output was empty");
+ return DefaultVersionString;
+ }
- string ret = String.Empty;
- string[] lines = programOutput.Split (LineSeparator);
- if (VersionOutputLine > 0) {
- if (lines.Length < VersionOutputLine) {
- Log.WarningLine ($"Not enough lines in version output of {ProgramName}: version number was supposed to be found on line {VersionOutputLine} but there are only {lines.Length} lines");
- return DefaultVersionString;
- }
+ string ret = String.Empty;
+ string[] lines = programOutput.Split (LineSeparator);
+ if (VersionOutputLine > 0) {
+ if (lines.Length < VersionOutputLine) {
+ Log.WarningLine ($"Not enough lines in version output of {ProgramName}: version number was supposed to be found on line {VersionOutputLine} but there are only {lines.Length} lines");
+ return DefaultVersionString;
+ }
- if (TryMatch (lines [VersionOutputLine - 1], out ret) && !String.IsNullOrEmpty (ret)) {
- return ret;
- }
+ if (TryMatch (rx, lines [VersionOutputLine - 1], out ret) && !String.IsNullOrEmpty (ret)) {
+ return ret;
+ }
- return DefaultVersionString;
- }
+ return DefaultVersionString;
+ }
- foreach (string line in lines) {
- if (TryMatch (line, out ret))
- break;
- }
+ foreach (string line in lines) {
+ if (TryMatch (rx, line, out ret))
+ break;
+ }
- return ret ?? DefaultVersionString;
- }
+ return ret ?? DefaultVersionString;
+ }
- bool TryMatch (string line, out string version)
- {
- version = String.Empty;
+ public static bool TryMatch (Regex regex, string line, out string version)
+ {
+ version = String.Empty;
- Match match = rx.Match (line);
- if (!match.Success || match.Groups.Count <= 0) {
- return false;
- }
+ Match match = regex.Match (line);
+ if (!match.Success || match.Groups.Count <= 0) {
+ return false;
+ }
- foreach (Group group in match.Groups) {
- if (String.Compare (group.Name, VersionGroupName, StringComparison.OrdinalIgnoreCase) == 0) {
- version = group.Value;
- return true;
- }
- }
+ foreach (Group group in match.Groups) {
+ if (String.Compare (group.Name, VersionGroupName, StringComparison.OrdinalIgnoreCase) == 0) {
+ version = group.Value;
+ return true;
+ }
+ }
- return false;
- }
- }
+ return false;
+ }
+ }
}
diff --git a/build-tools/xaprepare/xaprepare/Application/SevenZipVersionParser.cs b/build-tools/xaprepare/xaprepare/Application/SevenZipVersionParser.cs
new file mode 100644
index 00000000000..86bc518071a
--- /dev/null
+++ b/build-tools/xaprepare/xaprepare/Application/SevenZipVersionParser.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Text.RegularExpressions;
+
+namespace Xamarin.Android.Prepare;
+
+class SevenZipVersionParser : ProgramVersionParser
+{
+ const string VersionArgument = "--help";
+ readonly Regex fallbackRegex;
+ readonly Regex modernRegex;
+
+ public SevenZipVersionParser (string programName, Regex fallbackRegex, Log? log = null)
+ : base (programName, VersionArgument, 0, log)
+ {
+ this.fallbackRegex = fallbackRegex;
+ modernRegex = VersionFetchers.MakeRegex (@"^7-Zip (\(a\) ){0,1}(?[\d]+\.[\d]+)");
+ }
+
+ protected override string ParseVersion (string programOutput)
+ {
+ string output = programOutput.Trim ();
+ if (String.IsNullOrEmpty (output)) {
+ Log.WarningLine ($"Unable to parse version of {ProgramName} because version output was empty");
+ return DefaultVersionString;
+ }
+
+ string ret = String.Empty;
+ string[] lines = programOutput.Split (RegexProgramVersionParser.LineSeparator);
+
+ // First try to find the official 7zip release version
+ foreach (string l in lines) {
+ string line = l.Trim ();
+
+ if (line.Length == 0) {
+ continue;
+ }
+
+ if (line.StartsWith ("7-Zip", StringComparison.OrdinalIgnoreCase)) {
+ // Strings of the form:
+ // 7-Zip 23.01 (x64) : Copyright (c) 1999-2023 Igor Pavlov : 2023-06-20
+ // 7-Zip (a) 23.01 (x64) : Copyright (c) 1999-2023 Igor Pavlov : 2023-06-20
+ // 7-Zip (a) 18.01 (x64) : Copyright (c) 1999-2018 Igor Pavlov : 2018-01-28
+ // 7-Zip (a) 18.01 (x86) : Copyright (c) 1999-2018 Igor Pavlov : 2018-01-28
+ if (RegexProgramVersionParser.TryMatch (modernRegex, line, out ret)) {
+ return ret;
+ }
+ }
+
+ // Since we know we're dealing with `--help` option output, we can short-circuit things
+ if (line.StartsWith ("Usage:", StringComparison.OrdinalIgnoreCase)) {
+ break;
+ }
+ }
+
+ // Modern version wasn't found, try again with the fallback one
+ foreach (string l in lines) {
+ string line = l.Trim ();
+
+ if (line.Length == 0) {
+ continue;
+ }
+
+ if (RegexProgramVersionParser.TryMatch (fallbackRegex, line, out ret)) {
+ return ret;
+ }
+ }
+
+ return DefaultVersionString;
+ }
+}
diff --git a/build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs b/build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs
index 0125ff33b06..7b0a95b639e 100644
--- a/build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs
+++ b/build-tools/xaprepare/xaprepare/Application/VersionFetchers.cs
@@ -18,7 +18,7 @@ class VersionFetchers
public Dictionary Fetchers => GetFetchers ();
- static Regex MakeRegex (string regex)
+ internal static Regex MakeRegex (string regex)
{
return new Regex (regex, RegexOptions.Compiled | RegexOptions.Singleline);
}
@@ -30,8 +30,11 @@ static Regex MakeRegex (string regex)
return fetchers;
fetchers = new Dictionary (Context.Instance.OS.DefaultStringComparer) {
- {"7z", "--help", MakeRegex ($"Version {StandardVersionRegex}"), 3},
- {"7za", "--help", MakeRegex ($"Version {StandardVersionRegex}"), 3},
+ // Program-specific parsers
+ {"7z", new SevenZipVersionParser ("7z", MakeRegex ($"Version {StandardVersionRegex}"))},
+ {"7za", new SevenZipVersionParser ("7za", MakeRegex ($"Version {StandardVersionRegex}"))},
+
+ // Regex parsers
{"autoconf", "--version", StandardVersionAtEOL, 1},
{"automake", "--version", StandardVersionAtEOL, 1},
{"brew", "--version", MakeRegex ($"^Homebrew {StandardVersionRegex}"), 1},
diff --git a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs
index 506a56b25e4..b1ef7a0e2d9 100644
--- a/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs
+++ b/build-tools/xaprepare/xaprepare/ConfigAndData/BuildAndroidPlatforms.cs
@@ -5,8 +5,8 @@ namespace Xamarin.Android.Prepare
{
class BuildAndroidPlatforms
{
- public const string AndroidNdkVersion = "26b";
- public const string AndroidNdkPkgRevision = "26.1.10909125";
+ public const string AndroidNdkVersion = "26c";
+ public const string AndroidNdkPkgRevision = "26.2.11394342";
public const int NdkMinimumAPI = 21;
public const int NdkMinimumAPILegacy32 = 21;
diff --git a/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs b/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs
index 4e025f17239..e27b85d5b26 100644
--- a/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs
+++ b/build-tools/xaprepare/xaprepare/ToolRunners/SevenZipRunner.cs
@@ -10,6 +10,10 @@ partial class SevenZipRunner : ToolRunner
{
const double DefaultTimeout = 30; // minutes
static readonly Version bsoepMinVersion = new Version (15, 5);
+
+ // Just an educated guess. The official download page had versions 19 and then 23+ available
+ // and the 19 one didn't support the `-snld` switch
+ static readonly Version snldMinVersion = new Version (20, 0);
Version version;
protected override string DefaultToolExecutableName => "7za";
@@ -36,6 +40,7 @@ public async Task Extract (string archivePath, string outputDirectory, Lis
throw new ArgumentException ("must not be null or empty", nameof (outputDirectory));
ProcessRunner runner = CreateProcessRunner ("x");
+
AddStandardArguments (runner);
AddArguments (runner, extraArguments);
runner.AddQuotedArgument ($"-o{outputDirectory}");
@@ -114,6 +119,18 @@ protected override TextWriter CreateLogSink (string? logFilePath)
void AddStandardArguments (ProcessRunner runner)
{
+ Log.DebugLine ($"7-zip standard arguments, for 7z version {version}");
+
+ // Ignore some "dangerous" symbolic symlinks in the ZIP archives. This allows 7zip to unpack Android NDK archives
+ // without error. The option appears to be undocumented, but was mentioned by the 7zip author here:
+ //
+ // https://sourceforge.net/p/sevenzip/discussion/45798/thread/187ce54fb0/
+ //
+ if (version >= snldMinVersion) {
+ Log.DebugLine ("Adding option to ignore dangerous symlinks");
+ runner.AddArgument ("-snld");
+ }
+
// Disable progress indicator (doesn't appear to have any effect with some versions of 7z)
runner.AddArgument ("-bd");
diff --git a/build-tools/xaprepare/xaprepare/xaprepare.csproj b/build-tools/xaprepare/xaprepare/xaprepare.csproj
index 41766677fdd..ffa96b5c34c 100644
--- a/build-tools/xaprepare/xaprepare/xaprepare.csproj
+++ b/build-tools/xaprepare/xaprepare/xaprepare.csproj
@@ -1,17 +1,16 @@
+
$(DotNetStableTargetFramework)
Exe
- 8.0
+ $(LangVersion)
Xamarin.Android.Prepare
xaprepare
true
enable
-
-
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 1a4130af0be..566089db986 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,24 +1,24 @@
-
+
https://github.com/dotnet/installer
- fb7b9a4b9e578fa8c9f5fb67e22daf4b0d22668e
+ d070660282eb5f78497310f77093638744112e03
-
+
https://github.com/dotnet/runtime
- d40c654c274fe4f4afe66328f0599130f3eb2ea6
+ 99b76018b6e4edc4ce185dd5f3c5697c6941d88e
-
+
https://github.com/dotnet/runtime
- d40c654c274fe4f4afe66328f0599130f3eb2ea6
+ 99b76018b6e4edc4ce185dd5f3c5697c6941d88e
-
+
https://github.com/dotnet/emsdk
- 687be2a32a302aaade82380c0eaafa5af85fb4da
+ 2d3f1fe4807a21879cedba9d3fde8cd329fb17f2
-
+
https://github.com/dotnet/cecil
- b8c2293cd1cbd9d0fe6f32d7b5befbd526b5a175
+ 61250b0ed403b3f9b69a33f7d8f66f311338d6a1
diff --git a/eng/Versions.props b/eng/Versions.props
index ea4799ef67c..6bf62dc7325 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -1,15 +1,15 @@
- 9.0.100-preview.2.24106.6
- 9.0.0-preview.2.24080.1
- 9.0.0-preview.2.24080.1
+ 9.0.100-preview.3.24126.2
+ 9.0.0-preview.2.24123.1
+ 9.0.0-preview.2.24123.1
7.0.0-beta.22103.1
7.0.0-beta.22103.1
- 9.0.0-preview.2.24076.1
+ 9.0.0-preview.2.24121.1
$(MicrosoftNETWorkloadEmscriptenCurrentManifest90100TransportVersion)
7.0.100-rc.1.22410.7
- 0.11.4-alpha.24065.1
+ 0.11.4-alpha.24119.1
$(MicrosoftNETCoreAppRefPackageVersion)
diff --git a/external/Java.Interop b/external/Java.Interop
index 7d1e7057cf4..14a9470176b 160000
--- a/external/Java.Interop
+++ b/external/Java.Interop
@@ -1 +1 @@
-Subproject commit 7d1e7057cf4b0adcf65e7064186326dafce11b72
+Subproject commit 14a9470176b314fe521479fc75f6d9dd919abb56
diff --git a/external/xamarin-android-tools b/external/xamarin-android-tools
index a698a33aa4f..37d79c9dcdf 160000
--- a/external/xamarin-android-tools
+++ b/external/xamarin-android-tools
@@ -1 +1 @@
-Subproject commit a698a33aa4ffcaac90b54caf5e77236d57b0cf9e
+Subproject commit 37d79c9dcdf738a181084b0b5890877128d75f1e
diff --git a/external/xamarin-android-tools.override.props b/external/xamarin-android-tools.override.props
new file mode 100644
index 00000000000..f11b5fdc4b9
--- /dev/null
+++ b/external/xamarin-android-tools.override.props
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/Mono.Android/Android.Graphics/Color.cs b/src/Mono.Android/Android.Graphics/Color.cs
index bf57968663c..97a46fd5b11 100644
--- a/src/Mono.Android/Android.Graphics/Color.cs
+++ b/src/Mono.Android/Android.Graphics/Color.cs
@@ -395,11 +395,18 @@ public static void RGBToHSV (int red, int green, int blue, float[] hsv)
public class ColorValueMarshaler : JniValueMarshaler
{
+ const DynamicallyAccessedMemberTypes ConstructorsAndInterfaces = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces;
+ const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code.";
+
public override Type MarshalType {
get { return typeof (int); }
}
- public override Color CreateGenericValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type targetType)
+ public override Color CreateGenericValue (
+ ref JniObjectReference reference,
+ JniObjectReferenceOptions options,
+ [DynamicallyAccessedMembers (ConstructorsAndInterfaces)]
+ Type targetType)
{
throw new NotImplementedException ();
}
@@ -414,6 +421,7 @@ public override void DestroyGenericArgumentState (Color value, ref JniValueMarsh
throw new NotImplementedException ();
}
+ [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type targetType)
{
var c = typeof (Color).GetConstructor (new[]{typeof (int)})!;
@@ -424,6 +432,7 @@ public override Expression CreateParameterToManagedExpression (JniValueMarshaler
return v;
}
+ [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
public override Expression CreateParameterFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize)
{
var r = Expression.Variable (MarshalType, sourceValue.Name + "_p");
@@ -433,6 +442,7 @@ public override Expression CreateParameterFromManagedExpression (JniValueMarshal
return r;
}
+ [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue)
{
return CreateParameterFromManagedExpression (context, sourceValue, 0);
diff --git a/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs b/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs
index f5cab61ec1d..89436ab2b08 100644
--- a/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs
+++ b/src/Mono.Android/Android.Runtime/IJavaObjectValueMarshaler.cs
@@ -10,9 +10,16 @@ namespace Android.Runtime
{
sealed class IJavaObjectValueMarshaler : JniValueMarshaler {
+ const DynamicallyAccessedMemberTypes ConstructorsAndInterfaces = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.Interfaces;
+ const string ExpressionRequiresUnreferencedCode = "System.Linq.Expression usage may trim away required code.";
+
internal static IJavaObjectValueMarshaler Instance = new IJavaObjectValueMarshaler ();
- public override IJavaObject CreateGenericValue (ref JniObjectReference reference, JniObjectReferenceOptions options, Type? targetType)
+ public override IJavaObject CreateGenericValue (
+ ref JniObjectReference reference,
+ JniObjectReferenceOptions options,
+ [DynamicallyAccessedMembers (ConstructorsAndInterfaces)]
+ Type? targetType)
{
throw new NotImplementedException ();
}
@@ -27,6 +34,7 @@ public override void DestroyGenericArgumentState ([AllowNull]IJavaObject value,
throw new NotImplementedException ();
}
+ [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
public override Expression CreateReturnValueFromManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue)
{
return Expression.Call (
@@ -36,6 +44,7 @@ public override Expression CreateReturnValueFromManagedExpression (JniValueMarsh
sourceValue);
}
+ [RequiresUnreferencedCode (ExpressionRequiresUnreferencedCode)]
public override Expression CreateParameterToManagedExpression (JniValueMarshalerContext context, ParameterExpression sourceValue, ParameterAttributes synchronize, Type? targetType)
{
var r = Expression.Variable (targetType, sourceValue.Name + "_val");
diff --git a/src/Mono.Android/System.Drawing/PointConverter.cs b/src/Mono.Android/System.Drawing/PointConverter.cs
index 0255e55c87e..98a791cf951 100644
--- a/src/Mono.Android/System.Drawing/PointConverter.cs
+++ b/src/Mono.Android/System.Drawing/PointConverter.cs
@@ -30,6 +30,7 @@
using System.Collections;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.ComponentModel.Design.Serialization;
using System.Runtime.CompilerServices;
@@ -133,6 +134,7 @@ public override bool GetCreateInstanceSupported (ITypeDescriptorContext context)
return true;
}
+ [RequiresUnreferencedCode ("The Type of value cannot be statically discovered.")]
public override PropertyDescriptorCollection? GetProperties (
ITypeDescriptorContext context,
object value, Attribute[] attributes)
diff --git a/src/Mono.Android/System.Drawing/RectangleConverter.cs b/src/Mono.Android/System.Drawing/RectangleConverter.cs
index 364784f495f..053f07882af 100644
--- a/src/Mono.Android/System.Drawing/RectangleConverter.cs
+++ b/src/Mono.Android/System.Drawing/RectangleConverter.cs
@@ -31,6 +31,7 @@
using System.ComponentModel;
using System.Collections;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text;
using System.ComponentModel.Design.Serialization;
@@ -147,6 +148,7 @@ public override bool GetCreateInstanceSupported (ITypeDescriptorContext context)
return true;
}
+ [RequiresUnreferencedCode ("The Type of value cannot be statically discovered.")]
public override PropertyDescriptorCollection? GetProperties (
ITypeDescriptorContext context,
object value, Attribute[] attributes)
diff --git a/src/Mono.Android/System.Drawing/SizeConverter.cs b/src/Mono.Android/System.Drawing/SizeConverter.cs
index 53fe055733c..f60b57442b1 100644
--- a/src/Mono.Android/System.Drawing/SizeConverter.cs
+++ b/src/Mono.Android/System.Drawing/SizeConverter.cs
@@ -31,6 +31,7 @@
using System.Collections;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
@@ -135,6 +136,7 @@ public override bool GetCreateInstanceSupported (ITypeDescriptorContext context)
return true;
}
+ [RequiresUnreferencedCode ("The Type of value cannot be statically discovered.")]
public override PropertyDescriptorCollection? GetProperties (
ITypeDescriptorContext context,
object value, Attribute[] attributes)
diff --git a/src/Mono.Android/System.Drawing/SizeFConverter.cs b/src/Mono.Android/System.Drawing/SizeFConverter.cs
index 8933839fba2..fb2e81fce0f 100644
--- a/src/Mono.Android/System.Drawing/SizeFConverter.cs
+++ b/src/Mono.Android/System.Drawing/SizeFConverter.cs
@@ -32,6 +32,7 @@
using System;
using System.Collections;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.ComponentModel.Design.Serialization;
using System.Reflection;
@@ -118,6 +119,7 @@ public override bool GetCreateInstanceSupported (ITypeDescriptorContext context)
return true;
}
+ [RequiresUnreferencedCode ("The Type of value cannot be statically discovered.")]
public override PropertyDescriptorCollection? GetProperties (ITypeDescriptorContext context, object value, Attribute[] attributes)
{
if (value is SizeF)
diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs
index 83e858e1a62..f12317c1ead 100644
--- a/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs
+++ b/src/Mono.Android/Xamarin.Android.Net/AndroidClientHandler.cs
@@ -313,14 +313,9 @@ protected virtual Task SetupRequest (HttpRequestMessage request, HttpURLConnecti
object? GetUnderlyingHandler ()
{
var fieldName = "_nativeHandler";
- FieldInfo? field = null;
-
- for (var type = GetType (); type != null; type = type.BaseType) {
- field = type.GetField (fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
- if (field != null)
- break;
- }
-
+ const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
+ FieldInfo? field = typeof (HttpClientHandler).GetField (fieldName, flags) ??
+ typeof (HttpMessageHandler).GetField (fieldName, flags);
if (field == null) {
throw new InvalidOperationException ($"Field '{fieldName}' is missing from type '{GetType ()}'.");
}
diff --git a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs
index 44883640487..26645e6fdcb 100644
--- a/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs
+++ b/src/Mono.Android/Xamarin.Android.Net/AndroidMessageHandler.cs
@@ -536,30 +536,29 @@ protected virtual async Task WriteRequestContentToOutput (HttpRequestMessage req
if (request.Content is null)
return;
- using (var stream = await request.Content.ReadAsStreamAsync ().ConfigureAwait (false)) {
- await stream.CopyToAsync(httpConnection.OutputStream!, 4096, cancellationToken).ConfigureAwait(false);
-
- //
- // Rewind the stream to beginning in case the HttpContent implementation
- // will be accessed again (e.g. after redirect) and it keeps its stream
- // open behind the scenes instead of recreating it on the next call to
- // ReadAsStreamAsync. If we don't rewind it, the ReadAsStreamAsync
- // call above will throw an exception as we'd be attempting to read an
- // already "closed" stream (that is one whose Position is set to its
- // end).
- //
- // This is not a perfect solution since the HttpContent may do weird
- // things in its implementation, but it's better than copying the
- // content into a buffer since we have no way of knowing how the data is
- // read or generated and also we don't want to keep potentially large
- // amounts of data in memory (which would happen if we read the content
- // into a byte[] buffer and kept it cached for re-use on redirect).
- //
- // See https://bugzilla.xamarin.com/show_bug.cgi?id=55477
- //
- if (stream.CanSeek)
- stream.Seek (0, SeekOrigin.Begin);
- }
+ var stream = await request.Content.ReadAsStreamAsync ().ConfigureAwait (false);
+ await stream.CopyToAsync(httpConnection.OutputStream!, 4096, cancellationToken).ConfigureAwait(false);
+
+ //
+ // Rewind the stream to beginning in case the HttpContent implementation
+ // will be accessed again (e.g. after redirect) and it keeps its stream
+ // open behind the scenes instead of recreating it on the next call to
+ // ReadAsStreamAsync. If we don't rewind it, the ReadAsStreamAsync
+ // call above will throw an exception as we'd be attempting to read an
+ // already "closed" stream (that is one whose Position is set to its
+ // end).
+ //
+ // This is not a perfect solution since the HttpContent may do weird
+ // things in its implementation, but it's better than copying the
+ // content into a buffer since we have no way of knowing how the data is
+ // read or generated and also we don't want to keep potentially large
+ // amounts of data in memory (which would happen if we read the content
+ // into a byte[] buffer and kept it cached for re-use on redirect).
+ //
+ // See https://bugzilla.xamarin.com/show_bug.cgi?id=55477
+ //
+ if (stream.CanSeek)
+ stream.Seek (0, SeekOrigin.Begin);
}
internal Task WriteRequestContentToOutputInternal (HttpRequestMessage request, HttpURLConnection httpConnection, CancellationToken cancellationToken)
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
index 0a15a2b8a26..c99ce0db5c5 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs
@@ -33,6 +33,8 @@ public class BuildApk : AndroidTask
[Required]
public string ApkOutputPath { get; set; }
+ public string AppSharedLibrariesDir { get; set; }
+
[Required]
public ITaskItem[] ResolvedUserAssemblies { get; set; }
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
index 6a0444e872b..453885d1758 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
@@ -20,6 +20,7 @@
using Xamarin.Android.Tools;
using Microsoft.Android.Build.Tasks;
+using Java.Interop.Tools.JavaCallableWrappers.Adapters;
namespace Xamarin.Android.Tasks
{
@@ -463,6 +464,16 @@ bool CreateJavaSources (IEnumerable newJavaTypes, TypeDefinitionCache
bool hasExportReference = ResolvedAssemblies.Any (assembly => Path.GetFileName (assembly.ItemSpec) == "Mono.Android.Export.dll");
bool generateOnCreateOverrides = int.Parse (AndroidSdkPlatform) <= 10;
+ var reader_options = new CallableWrapperReaderOptions {
+ DefaultApplicationJavaClass = ApplicationJavaClass,
+ DefaultGenerateOnCreateOverrides = generateOnCreateOverrides,
+ DefaultMonoRuntimeInitialization = monoInit,
+ MethodClassifier = classifier,
+ };
+ var writer_options = new CallableWrapperWriterOptions {
+ CodeGenerationTarget = JavaPeerStyle.XAJavaInterop1
+ };
+
bool ok = true;
foreach (JavaType jt in newJavaTypes) {
TypeDefinition t = jt.Type; // JCW generator doesn't care about ABI-specific types or token ids
@@ -473,23 +484,21 @@ bool CreateJavaSources (IEnumerable newJavaTypes, TypeDefinitionCache
using (var writer = MemoryStreamPool.Shared.CreateStreamWriter ()) {
try {
- var jti = new JavaCallableWrapperGenerator (t, Log.LogWarning, cache, classifier) {
- GenerateOnCreateOverrides = generateOnCreateOverrides,
- ApplicationJavaClass = ApplicationJavaClass,
- MonoRuntimeInitialization = monoInit,
- };
+ var jcw_type = CecilImporter.CreateType (t, cache, reader_options);
+
+ jcw_type.Generate (writer, writer_options);
- jti.Generate (writer);
if (useMarshalMethods) {
if (classifier.FoundDynamicallyRegisteredMethods (t)) {
Log.LogWarning ($"Type '{t.GetAssemblyQualifiedName (cache)}' will register some of its Java override methods dynamically. This may adversely affect runtime performance. See preceding warnings for names of dynamically registered methods.");
}
}
+
writer.Flush ();
- var path = jti.GetDestinationPath (outputPath);
+ var path = jcw_type.GetDestinationPath (outputPath);
Files.CopyIfStreamChanged (writer.BaseStream, path);
- if (jti.HasExport && !hasExportReference)
+ if (jcw_type.HasExport && !hasExportReference)
Diagnostic.Error (4210, Properties.Resources.XA4210);
} catch (XamarinAndroidException xae) {
ok = false;
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs
index 0b89a082870..e1884c1c5f3 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs
@@ -111,19 +111,17 @@ public void BuildBasicApplicationReleaseProfiledAotWithoutDefaultProfile ()
}
[Test]
- [TestCase ("テスト", false, false, true)]
- [TestCase ("テスト", true, true, false)]
- [TestCase ("テスト", true, false, true)]
- [TestCase ("随机生成器", false, false, true)]
- [TestCase ("随机生成器", true, true, false)]
- [TestCase ("随机生成器", true, false, true)]
- [TestCase ("中国", false, false, true)]
- [TestCase ("中国", true, true, false)]
- [TestCase ("中国", true, false, true)]
- public void BuildAotApplicationWithSpecialCharactersInProject (string testName, bool isRelease, bool aot, bool expectedResult)
+ [TestCase ("テスト", false, false)]
+ [TestCase ("テスト", true, true)]
+ [TestCase ("テスト", true, false)]
+ [TestCase ("随机生成器", false, false)]
+ [TestCase ("随机生成器", true, true)]
+ [TestCase ("随机生成器", true, false)]
+ [TestCase ("中国", false, false)]
+ [TestCase ("中国", true, true)]
+ [TestCase ("中国", true, false)]
+ public void BuildAotApplicationWithSpecialCharactersInProject (string testName, bool isRelease, bool aot)
{
- if (!IsWindows)
- expectedResult = true;
var rootPath = Path.Combine (Root, "temp", TestName);
var proj = new XamarinAndroidApplicationProject () {
ProjectName = testName,
@@ -132,13 +130,7 @@ public void BuildAotApplicationWithSpecialCharactersInProject (string testName,
};
proj.SetAndroidSupportedAbis ("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
using (var builder = CreateApkBuilder (Path.Combine (rootPath, proj.ProjectName))){
- builder.ThrowOnBuildFailure = false;
- Assert.AreEqual (expectedResult, builder.Build (proj), "Build should have succeeded.");
- if (!expectedResult) {
- var aotFailed = builder.LastBuildOutput.ContainsText ("Precompiling failed");
- var aapt2Failed = builder.LastBuildOutput.ContainsText ("APT2265");
- Assert.IsTrue (aotFailed || aapt2Failed, "Error APT2265 or an AOT error should have been raised.");
- }
+ Assert.IsTrue (builder.Build (proj), "Build should have succeeded.");
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
index 25550eb3473..02b6c6ba776 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
@@ -42,11 +42,19 @@ sealed class ApplicationConfig
public uint bundled_assembly_name_width;
public uint number_of_assembly_store_files;
public uint number_of_dso_cache_entries;
+
+ [NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
public uint android_runtime_jnienv_class_token;
+
+ [NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
public uint jnienv_initialize_method_token;
+
+ [NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
public uint jnienv_registerjninatives_method_token;
public uint jni_remapping_replacement_type_count;
public uint jni_remapping_replacement_method_index_entry_count;
+
+ [NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)]
public uint mono_components_mask;
public string android_package_name = String.Empty;
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
index a442da7e221..cee11386b4a 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
@@ -29,7 +29,7 @@ public override string GetComment (object data, string fieldName)
{
var dso_entry = EnsureType (data);
if (String.Compare ("hash", fieldName, StringComparison.Ordinal) == 0) {
- return $" hash 0x{dso_entry.hash:x}, from name: {dso_entry.HashedName}";
+ return $" from name: {dso_entry.HashedName}";
}
if (String.Compare ("name", fieldName, StringComparison.Ordinal) == 0) {
@@ -49,7 +49,7 @@ sealed class DSOCacheEntry
[NativeAssembler (Ignore = true)]
public string HashedName;
- [NativeAssembler (UsesDataProvider = true)]
+ [NativeAssembler (UsesDataProvider = true, NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)]
public ulong hash;
public bool ignore;
@@ -93,11 +93,11 @@ sealed class AssemblyStoreSingleAssemblyRuntimeData
// src/monodroid/jni/xamarin-app.hh AssemblyStoreRuntimeData structure
sealed class AssemblyStoreRuntimeData
{
- [NativePointer]
+ [NativePointer (IsNull = true)]
public byte data_start;
public uint assembly_count;
- [NativePointer]
+ [NativePointer (IsNull = true)]
public AssemblyStoreAssemblyDescriptor assemblies;
}
@@ -299,7 +299,7 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
throw new InvalidOperationException ($"Internal error: DSO cache entry has unexpected type {instance.Obj.GetType ()}");
}
- entry.hash = GetXxHash (entry.HashedName, is64Bit);
+ entry.hash = MonoAndroidHelper.GetXxHash (entry.HashedName, is64Bit);
}
cache.Sort ((StructureInstance a, StructureInstance b) => a.Instance.hash.CompareTo (b.Instance.hash));
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs
index 1972dba0d85..39a899bd31a 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/AssemblyCompression.cs
@@ -78,7 +78,7 @@ public static CompressionResult Compress (AssemblyData data, string outputDirect
}
destBytes = bytePool.Rent (LZ4Codec.MaximumOutputSize (sourceBytes.Length));
- int encodedLength = LZ4Codec.Encode (sourceBytes, 0, checked((int)fi.Length), destBytes, 0, destBytes.Length, LZ4Level.L09_HC);
+ int encodedLength = LZ4Codec.Encode (sourceBytes, 0, checked((int)fi.Length), destBytes, 0, destBytes.Length, LZ4Level.L12_MAX);
if (encodedLength < 0)
return CompressionResult.EncodingFailed;
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs
index af894ceb96a..6209dc2d382 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrComposer.cs
@@ -1,7 +1,5 @@
using System;
using System.IO;
-using System.IO.Hashing;
-using System.Text;
using Microsoft.Build.Utilities;
@@ -41,18 +39,13 @@ public void Generate (LlvmIrModule module, AndroidTargetArch arch, StreamWriter
LlvmIrGenerator generator = LlvmIrGenerator.Create (arch, fileName);
generator.Generate (output, module);
output.Flush ();
- }
- public static ulong GetXxHash (string str, bool is64Bit)
- {
- byte[] stringBytes = Encoding.UTF8.GetBytes (str);
- if (is64Bit) {
- return XxHash3.HashToUInt64 (stringBytes);
- }
-
- return (ulong)XxHash32.HashToUInt32 (stringBytes);
+ CleanupAfterGeneration (arch);
}
+ protected virtual void CleanupAfterGeneration (AndroidTargetArch arch)
+ {}
+
protected LlvmIrGlobalVariable EnsureGlobalVariable (LlvmIrVariable variable)
{
var gv = variable as LlvmIrGlobalVariable;
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs
index bb81b03db0e..af1184bd77e 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.cs
@@ -29,6 +29,7 @@ sealed class GeneratorWriteContext
public readonly LlvmIrMetadataManager MetadataManager;
public string CurrentIndent { get; private set; } = String.Empty;
public bool InVariableGroup { get; set; }
+ public LlvmIrVariableNumberFormat NumberFormat { get; set; } = LlvmIrVariableNumberFormat.Default;
public GeneratorWriteContext (TextWriter writer, LlvmIrModule module, LlvmIrModuleTarget target, LlvmIrMetadataManager metadataManager)
{
@@ -80,31 +81,56 @@ sealed class BasicType
public readonly string Name;
public readonly ulong Size;
public readonly bool IsNumeric;
+ public readonly bool IsUnsigned;
+ public readonly bool PreferHex;
+ public readonly string HexFormat;
- public BasicType (string name, ulong size, bool isNumeric = true)
+ public BasicType (string name, ulong size, bool isNumeric = true, bool isUnsigned = false, bool? preferHex = null)
{
Name = name;
Size = size;
IsNumeric = isNumeric;
+ IsUnsigned = isUnsigned;
+
+ // If hex preference isn't specified, we determine whether the type wants to be represented in
+ // the hexadecimal notation based on signedness. Unsigned types will be represented in hexadecimal,
+ // but signed types will remain decimal, as it's easier for humans to see the actual value of the
+ // variable, given this note from LLVM IR manual:
+ //
+ // Note that hexadecimal integers are sign extended from the number of active bits, i.e. the bit width minus the number of leading zeros. So ‘s0x0001’ of type ‘i16’ will be -1, not 1.
+ //
+ // See: https://llvm.org/docs/LangRef.html#simple-constants
+ //
+ if (preferHex.HasValue) {
+ PreferHex = preferHex.Value;
+ } else {
+ PreferHex = isUnsigned;
+ }
+ if (!PreferHex) {
+ HexFormat = String.Empty;
+ return;
+ }
+
+ HexFormat = $"x{size * 2}";
}
}
public const string IRPointerType = "ptr";
static readonly Dictionary basicTypeMap = new Dictionary {
- { typeof (bool), new ("i8", 1, isNumeric: false) },
- { typeof (byte), new ("i8", 1) },
- { typeof (char), new ("i16", 2) },
+ { typeof (bool), new ("i1", 1, isNumeric: false, isUnsigned: true, preferHex: false) },
+ { typeof (byte), new ("i8", 1, isUnsigned: true) },
+ { typeof (char), new ("i16", 2, isUnsigned: true, preferHex: false) },
{ typeof (sbyte), new ("i8", 1) },
{ typeof (short), new ("i16", 2) },
- { typeof (ushort), new ("i16", 2) },
+ { typeof (ushort), new ("i16", 2, isUnsigned: true) },
{ typeof (int), new ("i32", 4) },
- { typeof (uint), new ("i32", 4) },
+ { typeof (uint), new ("i32", 4, isUnsigned: true) },
{ typeof (long), new ("i64", 8) },
- { typeof (ulong), new ("i64", 8) },
+ { typeof (ulong), new ("i64", 8, isUnsigned: true) },
{ typeof (float), new ("float", 4) },
{ typeof (double), new ("double", 8) },
- { typeof (void), new ("void", 0, isNumeric: false) },
+ { typeof (void), new ("void", 0, isNumeric: false, preferHex: false) },
};
public string FilePath { get; }
@@ -191,6 +217,8 @@ void WriteGlobalVariables (GeneratorWriteContext context)
}
foreach (LlvmIrGlobalVariable gv in context.Module.GlobalVariables) {
+ context.NumberFormat = gv.NumberFormat;
+
if (gv is LlvmIrGroupDelimiterVariable groupDelimiter) {
if (!context.InVariableGroup && !String.IsNullOrEmpty (groupDelimiter.Comment)) {
context.Output.WriteLine ();
@@ -205,6 +233,10 @@ void WriteGlobalVariables (GeneratorWriteContext context)
continue;
}
+ if (gv.TargetArch.HasValue && gv.TargetArch.Value != target.TargetArch) {
+ continue;
+ }
+
if (gv.BeforeWriteCallback != null) {
gv.BeforeWriteCallback (gv, target, gv.BeforeWriteCallbackCallerState);
}
@@ -240,8 +272,10 @@ void WriteGlobalVariable (GeneratorWriteContext context, LlvmIrGlobalVariable va
context.Output.Write (", align ");
ulong alignment;
- if (typeInfo.IsAggregate) {
- ulong count = GetAggregateValueElementCount (variable);
+ if (variable.Alignment.HasValue) {
+ alignment = variable.Alignment.Value;
+ } else if (typeInfo.IsAggregate) {
+ ulong count = GetAggregateValueElementCount (context, variable);
alignment = (ulong)target.GetAggregateAlignment ((int)typeInfo.MaxFieldAlignment, count * typeInfo.Size);
} else if (typeInfo.IsStructure) {
alignment = (ulong)target.GetAggregateAlignment ((int)typeInfo.MaxFieldAlignment, typeInfo.Size);
@@ -280,7 +314,7 @@ void WriteTypeAndValue (GeneratorWriteContext context, LlvmIrVariable variable,
return;
}
- throw new InvalidOperationException ($"Internal error: variable of type {variable.Type} must not have a null value");
+ throw new InvalidOperationException ($"Internal error: variable '{variable.Name}'' of type {variable.Type} must not have a null value");
}
if (valueType != variable.Type && !LlvmIrModule.NameValueArrayType.IsAssignableFrom (variable.Type)) {
@@ -290,9 +324,9 @@ void WriteTypeAndValue (GeneratorWriteContext context, LlvmIrVariable variable,
WriteValue (context, valueType, variable);
}
- ulong GetAggregateValueElementCount (LlvmIrVariable variable) => GetAggregateValueElementCount (variable.Type, variable.Value, variable as LlvmIrGlobalVariable);
+ ulong GetAggregateValueElementCount (GeneratorWriteContext context, LlvmIrVariable variable) => GetAggregateValueElementCount (context, variable.Type, variable.Value, variable as LlvmIrGlobalVariable);
- ulong GetAggregateValueElementCount (Type type, object? value, LlvmIrGlobalVariable? globalVariable = null)
+ ulong GetAggregateValueElementCount (GeneratorWriteContext context, Type type, object? value, LlvmIrGlobalVariable? globalVariable = null)
{
if (!type.IsArray ()) {
throw new InvalidOperationException ($"Internal error: unknown type {type} when trying to determine aggregate type element count");
@@ -300,6 +334,9 @@ ulong GetAggregateValueElementCount (Type type, object? value, LlvmIrGlobalVaria
if (value == null) {
if (globalVariable != null) {
+ if (globalVariable.ArrayDataProvider != null) {
+ return globalVariable.ArrayDataProvider.GetTotalDataSize (context.Target);
+ }
return globalVariable.ArrayItemCount;
}
return 0;
@@ -373,7 +410,7 @@ void WriteType (GeneratorWriteContext context, Type type, object? value, out Llv
{
if (IsStructureInstance (type)) {
if (value == null) {
- throw new ArgumentException ("must not be null for structure instances", nameof (value));
+ throw new ArgumentException ($"must not be null for structure instances ({type})", nameof (value));
}
WriteStructureType (context, (StructureInstance)value, out typeInfo);
@@ -386,9 +423,9 @@ void WriteType (GeneratorWriteContext context, Type type, object? value, out Llv
if (type.IsArray ()) {
Type elementType = type.GetArrayElementType ();
- ulong elementCount = GetAggregateValueElementCount (type, value, globalVariable);
+ ulong elementCount = GetAggregateValueElementCount (context, type, value, globalVariable);
- WriteArrayType (context, elementType, elementCount, out typeInfo);
+ WriteArrayType (context, elementType, elementCount, globalVariable, out typeInfo);
return;
}
@@ -404,6 +441,11 @@ void WriteType (GeneratorWriteContext context, Type type, object? value, out Llv
}
void WriteArrayType (GeneratorWriteContext context, Type elementType, ulong elementCount, out LlvmTypeInfo typeInfo)
+ {
+ WriteArrayType (context, elementType, elementCount, variable: null, out typeInfo);
+ }
+
+ void WriteArrayType (GeneratorWriteContext context, Type elementType, ulong elementCount, LlvmIrGlobalVariable? variable, out LlvmTypeInfo typeInfo)
{
string irType;
ulong size;
@@ -420,6 +462,35 @@ void WriteArrayType (GeneratorWriteContext context, Type elementType, ulong elem
} else {
irType = GetIRType (elementType, out size, out isPointer);
maxFieldAlignment = size;
+
+ if (elementType.IsArray) {
+ if (variable == null) {
+ throw new InvalidOperationException ($"Internal error: array of arrays ({elementType}) requires variable to be defined");
+ }
+
+ // For the sake of simpler code, we currently assume that all the element arrays are of the same size, because that's the only scenario
+ // that we use at this time.
+ var value = variable.Value as ICollection;
+ if (value == null) {
+ throw new InvalidOperationException ($"Internal error: variable '{variable.Name}' of type '{variable.Type}' is required to have a value of type which implements the ICollection interface");
+ }
+
+ if (value.Count == 0) {
+ throw new InvalidOperationException ($"Internal error: variable '{variable.Name}' of type '{variable.Type}' is required to have a value which is a non-empty ICollection");
+ }
+
+ Array? firstItem = null;
+ foreach (object v in value) {
+ firstItem = (Array)v;
+ break;
+ }
+
+ if (firstItem == null) {
+ throw new InvalidOperationException ($"Internal error: variable '{variable.Name}' of type '{variable.Type}' is required to have a value which is a non-empty ICollection with non-null elements");
+ }
+
+ irType = $"[{MonoAndroidHelper.CultureInvariantToString (firstItem.Length)} x {irType}]";
+ }
}
typeInfo = new LlvmTypeInfo (
isPointer: isPointer,
@@ -449,12 +520,17 @@ ulong GetStructureMaxFieldAlignment (StructureInfo si)
void WriteValue (GeneratorWriteContext context, Type valueType, LlvmIrVariable variable)
{
+ if (variable is LlvmIrGlobalVariable globalVariable && globalVariable.ArrayDataProvider != null) {
+ WriteStreamedArrayValue (context, globalVariable, globalVariable.ArrayDataProvider);
+ return;
+ }
+
if (variable.Type.IsArray ()) {
bool zeroInitialize = false;
if (variable is LlvmIrGlobalVariable gv) {
zeroInitialize = gv.ZeroInitializeArray || variable.Value == null;
} else {
- zeroInitialize = GetAggregateValueElementCount (variable) == 0;
+ zeroInitialize = GetAggregateValueElementCount (context, variable) == 0;
}
if (zeroInitialize) {
@@ -478,6 +554,29 @@ void AssertArraySize (StructureInstance si, StructureMemberInfo smi, ulong lengt
throw new InvalidOperationException ($"Invalid array size in field '{smi.Info.Name}' of structure '{si.Info.Name}', expected {expectedLength}, found {length}");
}
+ void WriteInlineArray (GeneratorWriteContext context, byte[] bytes, bool encodeAsASCII)
+ {
+ if (encodeAsASCII) {
+ context.Output.Write ('c');
+ context.Output.Write (QuoteString (bytes, bytes.Length, out _, nullTerminated: false));
+ return;
+ }
+
+ string irType = MapToIRType (typeof(byte));
+ bool first = true;
+ context.Output.Write ("[ ");
+ foreach (byte b in bytes) {
+ if (!first) {
+ context.Output.Write (", ");
+ } else {
+ first = false;
+ }
+
+ context.Output.Write ($"{irType} u0x{b:x02}");
+ }
+ context.Output.Write (" ]");
+ }
+
void WriteValue (GeneratorWriteContext context, StructureInstance structInstance, StructureMemberInfo smi, object? value)
{
if (smi.IsNativePointer) {
@@ -495,8 +594,7 @@ void WriteValue (GeneratorWriteContext context, StructureInstance structInstance
// Byte arrays are represented in the same way as strings, without the explicit NUL termination byte
AssertArraySize (structInstance, smi, length, smi.ArrayElements);
- context.Output.Write ('c');
- context.Output.Write (QuoteString (bytes, bytes.Length, out _, nullTerminated: false));
+ WriteInlineArray (context, bytes, encodeAsASCII: false);
return;
}
@@ -549,6 +647,27 @@ bool WriteNativePointerValue (GeneratorWriteContext context, StructureInstance s
return false;
}
+ string ToHex (BasicType basicTypeDesc, Type type, object? value)
+ {
+ const char prefixSigned = 's';
+ const char prefixUnsigned = 'u';
+
+ string hex;
+ if (type == typeof(byte)) {
+ hex = ((byte)value).ToString (basicTypeDesc.HexFormat, CultureInfo.InvariantCulture);
+ } else if (type == typeof(ushort)) {
+ hex = ((ushort)value).ToString (basicTypeDesc.HexFormat, CultureInfo.InvariantCulture);
+ } else if (type == typeof(uint)) {
+ hex = ((uint)value).ToString (basicTypeDesc.HexFormat, CultureInfo.InvariantCulture);
+ } else if (type == typeof(ulong)) {
+ hex = ((ulong)value).ToString (basicTypeDesc.HexFormat, CultureInfo.InvariantCulture);
+ } else {
+ throw new NotImplementedException ($"Conversion to hexadecimal from type {type} is not implemented");
+ };
+
+ return $"{(basicTypeDesc.IsUnsigned ? prefixUnsigned : prefixSigned)}0x{hex}";
+ }
+
void WriteValue (GeneratorWriteContext context, Type type, object? value)
{
if (value is LlvmIrVariable variableRef) {
@@ -556,14 +675,26 @@ void WriteValue (GeneratorWriteContext context, Type type, object? value)
return;
}
- if (IsNumeric (type)) {
- context.Output.Write (MonoAndroidHelper.CultureInvariantToString (value));
- return;
- }
+ bool isBasic = basicTypeMap.TryGetValue (type, out BasicType basicTypeDesc);
+ if (isBasic) {
+ if (basicTypeDesc.IsNumeric) {
+ bool hex = context.NumberFormat switch {
+ LlvmIrVariableNumberFormat.Default => basicTypeDesc.PreferHex,
+ LlvmIrVariableNumberFormat.Decimal => false,
+ LlvmIrVariableNumberFormat.Hexadecimal => true,
+ _ => throw new InvalidOperationException ($"Internal error: number format {context.NumberFormat} is unsupported")
+ };
- if (type == typeof(bool)) {
- context.Output.Write ((bool)value ? '1' : '0');
- return;
+ context.Output.Write (
+ hex ? ToHex (basicTypeDesc, type, value) : MonoAndroidHelper.CultureInvariantToString (value)
+ );
+ return;
+ }
+
+ if (type == typeof(bool)) {
+ context.Output.Write ((bool)value ? "true" : "false");
+ return;
+ }
}
if (IsStructureInstance (type)) {
@@ -588,8 +719,13 @@ void WriteValue (GeneratorWriteContext context, Type type, object? value)
return;
}
- if (type.IsInlineArray ()) {
+ if (type.IsArray) {
+ if (type == typeof(byte[])) {
+ WriteInlineArray (context, (byte[])value, encodeAsASCII: true);
+ return;
+ }
+ throw new NotSupportedException ($"Internal error: array of type {type} is unsupported");
}
throw new NotSupportedException ($"Internal error: value type '{type}' is unsupported");
@@ -616,8 +752,20 @@ void WriteStructureValue (GeneratorWriteContext context, StructureInstance? inst
context.Output.Write (' ');
object? value = GetTypedMemberValue (context, info, smi, instance, smi.MemberType);
+ LlvmIrVariableNumberFormat numberFormat = smi.Info.GetNumberFormat ();
+ LlvmIrVariableNumberFormat? savedNumberFormat = null;
+
+ if (numberFormat != LlvmIrVariableNumberFormat.Default && numberFormat != context.NumberFormat) {
+ savedNumberFormat = context.NumberFormat;
+ context.NumberFormat = numberFormat;
+ }
+
WriteValue (context, instance, smi, value);
+ if (savedNumberFormat.HasValue) {
+ context.NumberFormat = savedNumberFormat.Value;
+ }
+
if (i < lastMember) {
context.Output.Write (", ");
}
@@ -628,9 +776,6 @@ void WriteStructureValue (GeneratorWriteContext context, StructureInstance? inst
sb.Append (MapManagedTypeToNative (smi));
sb.Append (' ');
sb.Append (smi.Info.Name);
- if (value != null && smi.MemberType.IsPrimitive && smi.MemberType != typeof(bool)) {
- sb.Append ($" (0x{value:x})");
- }
comment = sb.ToString ();
}
WriteCommentLine (context, comment);
@@ -641,75 +786,80 @@ void WriteStructureValue (GeneratorWriteContext context, StructureInstance? inst
context.Output.Write ('}');
}
- void WriteArrayValue (GeneratorWriteContext context, LlvmIrVariable variable)
+ void WriteArrayValueStart (GeneratorWriteContext context)
{
- ICollection entries;
- if (variable.Type.ImplementsInterface (typeof(IDictionary))) {
- var list = new List ();
- foreach (var kvp in (IDictionary)variable.Value) {
- list.Add (kvp.Key);
- list.Add (kvp.Value);
- }
- entries = list;
- } else {
- entries = (ICollection)variable.Value;
- }
-
- if (entries.Count == 0) {
- context.Output.Write ("zeroinitializer");
- return;
- }
-
context.Output.WriteLine ('[');
context.IncreaseIndent ();
+ }
- Type elementType = variable.Type.GetArrayElementType ();
- bool writeIndices = (variable.WriteOptions & LlvmIrVariableWriteOptions.ArrayWriteIndexComments) == LlvmIrVariableWriteOptions.ArrayWriteIndexComments;
- ulong counter = 0;
- string? prevItemComment = null;
- uint stride;
+ void WriteArrayValueEnd (GeneratorWriteContext context)
+ {
+ context.DecreaseIndent ();
+ context.Output.Write (']');
+ }
+ uint GetArrayStride (LlvmIrVariable variable)
+ {
if ((variable.WriteOptions & LlvmIrVariableWriteOptions.ArrayFormatInRows) == LlvmIrVariableWriteOptions.ArrayFormatInRows) {
- stride = variable.ArrayStride > 0 ? variable.ArrayStride : 1;
- } else {
- stride = 1;
+ return variable.ArrayStride > 0 ? variable.ArrayStride : 1;
}
+ return 1;
+ }
+
+ void WriteArrayEntries (GeneratorWriteContext context, LlvmIrVariable variable, ICollection? entries, Type elementType, uint stride, bool writeIndices, bool terminateWithComma = false)
+ {
bool first = true;
+ bool ignoreComments = stride > 1;
+ string? prevItemComment = null;
+ ulong counter = 0;
- // TODO: implement output in rows
- foreach (object entry in entries) {
- if (!first) {
- context.Output.Write (',');
- WritePrevItemCommentOrNewline ();
- } else {
- first = false;
- }
+ if (entries != null) {
+ foreach (object entry in entries) {
+ if (!first) {
+ context.Output.Write (',');
+ if (stride == 1 || counter % stride == 0) {
+ WritePrevItemCommentOrNewline ();
+ context.Output.Write (context.CurrentIndent);
+ } else {
+ context.Output.Write (' ');
+ }
+ } else {
+ context.Output.Write (context.CurrentIndent);
+ first = false;
+ }
- prevItemComment = null;
- if (variable.GetArrayItemCommentCallback != null) {
- prevItemComment = variable.GetArrayItemCommentCallback (variable, target, counter, entry, variable.GetArrayItemCommentCallbackCallerState);
- }
+ if (!ignoreComments) {
+ prevItemComment = null;
+ if (variable.GetArrayItemCommentCallback != null) {
+ prevItemComment = variable.GetArrayItemCommentCallback (variable, target, counter, entry, variable.GetArrayItemCommentCallbackCallerState);
+ }
- if (writeIndices && String.IsNullOrEmpty (prevItemComment)) {
- prevItemComment = $" {counter}";
- }
+ if (writeIndices && String.IsNullOrEmpty (prevItemComment)) {
+ prevItemComment = $" {counter}";
+ }
+ }
- counter++;
- context.Output.Write (context.CurrentIndent);
- WriteType (context, elementType, entry, out _);
+ counter++;
+ WriteType (context, elementType, entry, out _);
- context.Output.Write (' ');
- WriteValue (context, elementType, entry);
+ context.Output.Write (' ');
+ WriteValue (context, elementType, entry);
+ }
}
- WritePrevItemCommentOrNewline ();
- context.DecreaseIndent ();
- context.Output.Write (']');
+ if (terminateWithComma) {
+ if (!ignoreComments) {
+ context.Output.WriteLine (); // must put comma outside the comment
+ context.Output.Write (context.CurrentIndent);
+ }
+ context.Output.Write (',');
+ }
+ WritePrevItemCommentOrNewline ();
void WritePrevItemCommentOrNewline ()
{
- if (!String.IsNullOrEmpty (prevItemComment)) {
+ if (!ignoreComments && !String.IsNullOrEmpty (prevItemComment)) {
context.Output.Write (' ');
WriteCommentLine (context, prevItemComment);
} else {
@@ -718,6 +868,97 @@ void WritePrevItemCommentOrNewline ()
}
}
+ bool ArrayWantsToWriteIndices (LlvmIrVariable variable) => (variable.WriteOptions & LlvmIrVariableWriteOptions.ArrayWriteIndexComments) == LlvmIrVariableWriteOptions.ArrayWriteIndexComments;
+
+ void WriteStreamedArrayValue (GeneratorWriteContext context, LlvmIrGlobalVariable variable, LlvmIrStreamedArrayDataProvider dataProvider)
+ {
+ ulong dataSizeSoFar = 0;
+ ulong totalDataSize = dataProvider.GetTotalDataSize (context.Target);
+ bool first = true;
+
+ WriteArrayValueStart (context);
+ while (true) {
+ (LlvmIrStreamedArrayDataProviderState state, ICollection? data) = dataProvider.GetData (context.Target);
+ if (state == LlvmIrStreamedArrayDataProviderState.NextSectionNoData) {
+ continue;
+ }
+
+ bool mustHaveData = state != LlvmIrStreamedArrayDataProviderState.LastSectionNoData;
+ if (mustHaveData) {
+ if (data.Count == 0) {
+ throw new InvalidOperationException ("Data must be provided for streamed arrays");
+ }
+
+ dataSizeSoFar += (ulong)data.Count;
+ if (dataSizeSoFar > totalDataSize) {
+ throw new InvalidOperationException ($"Data provider {dataProvider} is trying to write more data than declared");
+ }
+
+ if (first) {
+ first = false;
+ } else {
+ context.Output.WriteLine ();
+ }
+ string comment = dataProvider.GetSectionStartComment (context.Target);
+
+ if (comment.Length > 0) {
+ context.Output.Write (context.CurrentIndent);
+ WriteCommentLine (context, comment);
+ }
+ }
+
+ bool lastSection = state == LlvmIrStreamedArrayDataProviderState.LastSection || state == LlvmIrStreamedArrayDataProviderState.LastSectionNoData;
+ WriteArrayEntries (
+ context,
+ variable,
+ data,
+ dataProvider.ArrayElementType,
+ GetArrayStride (variable),
+ writeIndices: false,
+ terminateWithComma: !lastSection
+ );
+
+ if (lastSection) {
+ break;
+ }
+
+ }
+ WriteArrayValueEnd (context);
+ }
+
+ void WriteArrayValue (GeneratorWriteContext context, LlvmIrVariable variable)
+ {
+ ICollection entries;
+ if (variable.Type.ImplementsInterface (typeof(IDictionary))) {
+ var list = new List ();
+ foreach (var kvp in (IDictionary)variable.Value) {
+ list.Add (kvp.Key);
+ list.Add (kvp.Value);
+ }
+ entries = list;
+ } else {
+ entries = (ICollection)variable.Value;
+ }
+
+ if (entries.Count == 0) {
+ context.Output.Write ("zeroinitializer");
+ return;
+ }
+
+ WriteArrayValueStart (context);
+
+ WriteArrayEntries (
+ context,
+ variable,
+ entries,
+ variable.Type.GetArrayElementType (),
+ GetArrayStride (variable),
+ writeIndices: ArrayWantsToWriteIndices (variable)
+ );
+
+ WriteArrayValueEnd (context);
+ }
+
void WriteLinkage (GeneratorWriteContext context, LlvmIrLinkage linkage)
{
if (linkage == LlvmIrLinkage.Default) {
@@ -1232,8 +1473,6 @@ static string MapManagedTypeToNative (StructureMemberInfo smi)
return $"{nativeType}*";
}
- static bool IsNumeric (Type type) => basicTypeMap.TryGetValue (type, out BasicType typeDesc) && typeDesc.IsNumeric;
-
object? GetTypedMemberValue (GeneratorWriteContext context, StructureInfo info, StructureMemberInfo smi, StructureInstance instance, Type expectedType, object? defaultValue = null)
{
object? value = smi.GetValue (instance.Obj);
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs
index be1570dafe8..9a50f4ee5cf 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrModule.cs
@@ -464,7 +464,7 @@ void AddStringArrayGlobalVariable (LlvmIrGlobalVariable variable, string? string
Register (kvp.Value);
}
} else if (typeof(ICollection).IsAssignableFrom (variable.Type)) {
- foreach (string s in (ICollection)variable.Value) {
+ foreach (string s in (ICollection)variable.Value) {
Register (s);
}
} else {
@@ -473,8 +473,12 @@ void AddStringArrayGlobalVariable (LlvmIrGlobalVariable variable, string? string
AddStandardGlobalVariable (variable);
- void Register (string value)
+ void Register (string? value)
{
+ if (value == null) {
+ return;
+ }
+
RegisterString (value, stringGroupName, stringGroupComment, symbolSuffix);
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrVariable.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrVariable.cs
index 7f741490ce0..d66bda3d7c9 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrVariable.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrVariable.cs
@@ -1,6 +1,9 @@
using System;
+using System.Collections;
using System.Globalization;
+using Xamarin.Android.Tools;
+
namespace Xamarin.Android.Tasks.LLVMIR;
[Flags]
@@ -11,6 +14,13 @@ enum LlvmIrVariableWriteOptions
ArrayFormatInRows = 0x0002,
}
+enum LlvmIrVariableNumberFormat
+{
+ Default,
+ Hexadecimal,
+ Decimal,
+}
+
abstract class LlvmIrVariable : IEquatable
{
public abstract bool Global { get; }
@@ -29,6 +39,8 @@ abstract class LlvmIrVariable : IEquatable
public object? Value { get; set; }
public string? Comment { get; set; }
+ public LlvmIrVariableNumberFormat NumberFormat { get; set; } = LlvmIrVariableNumberFormat.Decimal;
+
///
/// Both global and local variables will want their names to matter in equality checks, but function
/// parameters must not take it into account, thus this property. If set to false,
@@ -146,6 +158,50 @@ public void AssignNumber (ulong n)
}
}
+enum LlvmIrStreamedArrayDataProviderState
+{
+ NextSection,
+ LastSection,
+ NextSectionNoData,
+ LastSectionNoData,
+}
+
+abstract class LlvmIrStreamedArrayDataProvider
+{
+ ///
+ /// Type of every member of the array returned by . Generator will check
+ /// every member type against this property, allowing also derived types.
+ ///
+ public Type ArrayElementType { get; }
+
+ protected LlvmIrStreamedArrayDataProvider (Type arrayElementType)
+ {
+ ArrayElementType = arrayElementType;
+ }
+
+ ///
+ /// Whenever returns the generator will call this method to obtain the new section
+ /// comment, if any, to be output before the actual data. Returning `String.Empty` prevents the comment
+ /// from being added.
+ ///
+ public virtual string GetSectionStartComment (LlvmIrModuleTarget target) => String.Empty;
+
+ ///
+ /// Provide the next chunk of data for the specified target (ABI). Implementations need to return at least one
+ /// non-empty collection of data. The returned collection **must** be exactly the size of contained data (e.g. it cannot be
+ /// a byte array rented from a byte pool, because these can be bigger than requested. When returning the last (or the only) section,
+ /// must have a value of .
+ /// Each section may be preceded by a comment, .
+ ///
+ public abstract (LlvmIrStreamedArrayDataProviderState status, ICollection data) GetData (LlvmIrModuleTarget target);
+
+ ///
+ /// Provide the total data size for the specified target (ABI). This needs to be used instead of
+ /// because a variable instance is created once and shared by all targets, while per-target data sets might have different sizes.
+ ///
+ public abstract ulong GetTotalDataSize (LlvmIrModuleTarget target);
+}
+
class LlvmIrGlobalVariable : LlvmIrVariable
{
///
@@ -162,9 +218,40 @@ class LlvmIrGlobalVariable : LlvmIrVariable
///
public virtual LlvmIrVariableOptions? Options { get; set; }
+ ///
+ /// There are situations when a variable differs enough between architectures, that the difference cannot be
+ /// handled with . In such situations one can create a separate variable
+ /// for each architecture and set this property.
+ ///
+ public AndroidTargetArch? TargetArch { get; set; }
+
+ ///
+ /// If set to `true`, initialize the array with a shortcut zero-initializer statement. Useful when pre-allocating
+ /// space for runtime use that won't be filled in with any data at the build time.
+ ///
public bool ZeroInitializeArray { get; set; }
+
+ ///
+ /// Specify number of items in an array. Used in cases when we want to pre-allocate an array without giving it any
+ /// value, thus making it impossible for the generator to discover the number of items automatically. This is useful
+ /// when using . This property is used **only** if the variable
+ /// is `null`.
+ ///
public ulong ArrayItemCount { get; set; }
+ ///
+ /// If set, it will override any automatically calculated alignment for this variable
+ ///
+ public ulong? Alignment { get; set; }
+
+ ///
+ /// If set, the provider will be called to obtain all the data to be placed in an array variable. The total amount
+ /// of data that will be returned by the provider **must** be specified in the property,
+ /// in order for the generator to properly declare the variable. The generator will verify that the amount of data
+ /// is exactly that much and throw an exception otherwise.
+ ///
+ public LlvmIrStreamedArrayDataProvider? ArrayDataProvider { get; set; }
+
///
/// Constructs a local variable. is translated to one of the LLVM IR first class types (see
/// https://llvm.org/docs/LangRef.html#t-firstclass) only if it's an integral or floating point type. In all other cases it
@@ -196,11 +283,16 @@ public LlvmIrGlobalVariable (object value, string name, LlvmIrVariableOptions? o
/// generating output for a specific target (e.g. 32-bit vs 64-bit integer variables). If the variable requires such
/// type changes, this should be done at generation time from within the method.
///
- public void OverrideValueAndType (Type newType, object? newValue)
+ public void OverrideTypeAndValue (Type newType, object? newValue)
{
Type = newType;
Value = newValue;
}
+
+ public void OverrideName (string newName)
+ {
+ Name = newName;
+ }
}
class LlvmIrStringVariable : LlvmIrGlobalVariable
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/MemberInfoUtilities.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/MemberInfoUtilities.cs
index 431d92b6229..713b0c5a1a6 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/MemberInfoUtilities.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/MemberInfoUtilities.cs
@@ -71,5 +71,15 @@ public static bool InlineArrayNeedsPadding (this MemberInfo mi)
return attr.NeedsPadding;
}
+
+ public static LlvmIrVariableNumberFormat GetNumberFormat (this MemberInfo mi)
+ {
+ var attr = mi.GetCustomAttribute ();
+ if (attr == null) {
+ return LlvmIrVariableNumberFormat.Default;
+ }
+
+ return attr.NumberFormat;
+ }
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/NativeAssemblerAttribute.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/NativeAssemblerAttribute.cs
index 39fc5e094bb..c239255e1ac 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/NativeAssemblerAttribute.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/NativeAssemblerAttribute.cs
@@ -33,6 +33,8 @@ class NativeAssemblerAttribute : Attribute
/// size to which the member must be padded is specified by
///
public bool NeedsPadding { get; set; }
+
+ public LLVMIR.LlvmIrVariableNumberFormat NumberFormat { get; set; } = LLVMIR.LlvmIrVariableNumberFormat.Default;
}
[AttributeUsage (AttributeTargets.Class, Inherited = true)]
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs
index c30df45bb8e..ee6fc0e7f28 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs
@@ -108,7 +108,7 @@ public override string GetComment (object data, string fieldName)
var klass = EnsureType (data);
if (String.Compare ("token", fieldName, StringComparison.Ordinal) == 0) {
- return $" token 0x{klass.token:x}; class name: {klass.ClassName}";
+ return $" class name: {klass.ClassName}";
}
return String.Empty;
@@ -118,7 +118,7 @@ public override string GetComment (object data, string fieldName)
[NativeAssemblerStructContextDataProvider (typeof(MarshalMethodsManagedClassDataProvider))]
sealed class MarshalMethodsManagedClass
{
- [NativeAssembler (UsesDataProvider = true)]
+ [NativeAssembler (UsesDataProvider = true, NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal)]
public uint token;
[NativePointer (IsNull = true)]
@@ -982,10 +982,10 @@ void AddAssemblyImageCache (LlvmIrModule module, out AssemblyCacheState acs)
foreach (string name in uniqueAssemblyNames) {
// We must make sure we keep the possible culture prefix, which will be treated as "directory" path here
string clippedName = Path.Combine (Path.GetDirectoryName (name) ?? String.Empty, Path.GetFileNameWithoutExtension (name));
- ulong hashFull32 = GetXxHash (name, is64Bit: false);
- ulong hashClipped32 = GetXxHash (clippedName, is64Bit: false);
- ulong hashFull64 = GetXxHash (name, is64Bit: true);
- ulong hashClipped64 = GetXxHash (clippedName, is64Bit: true);
+ ulong hashFull32 = MonoAndroidHelper.GetXxHash (name, is64Bit: false);
+ ulong hashClipped32 = MonoAndroidHelper.GetXxHash (clippedName, is64Bit: false);
+ ulong hashFull64 = MonoAndroidHelper.GetXxHash (name, is64Bit: true);
+ ulong hashClipped64 = MonoAndroidHelper.GetXxHash (clippedName, is64Bit: true);
//
// If the number of name forms changes, xamarin-app.hh MUST be updated to set value of the
@@ -1021,6 +1021,7 @@ void AddAssemblyImageCache (LlvmIrModule module, out AssemblyCacheState acs)
BeforeWriteCallbackCallerState = acs,
GetArrayItemCommentCallback = GetAssemblyImageCacheItemComment,
GetArrayItemCommentCallbackCallerState = acs,
+ NumberFormat = LlvmIrVariableNumberFormat.Hexadecimal,
};
module.Add (assembly_image_cache_hashes);
@@ -1047,7 +1048,7 @@ void UpdateAssemblyImageCacheHashes (LlvmIrVariable variable, LlvmIrModuleTarget
}
LlvmIrGlobalVariable gv = EnsureGlobalVariable (variable);
- gv.OverrideValueAndType (type, value);
+ gv.OverrideTypeAndValue (type, value);
}
string? GetAssemblyImageCacheItemComment (LlvmIrVariable v, LlvmIrModuleTarget target, ulong index, object? value, object? callerState)
@@ -1081,7 +1082,7 @@ void UpdateAssemblyImageCacheIndices (LlvmIrVariable variable, LlvmIrModuleTarge
}
LlvmIrGlobalVariable gv = EnsureGlobalVariable (variable);
- gv.OverrideValueAndType (variable.Type, value);
+ gv.OverrideTypeAndValue (variable.Type, value);
}
AssemblyCacheState EnsureAssemblyCacheState (object? callerState)
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Basic.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Basic.cs
new file mode 100644
index 00000000000..3e8fe9a2d8c
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.Basic.cs
@@ -0,0 +1,200 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO.Hashing;
+using System.Text;
+
+using Xamarin.Android.Tools;
+
+namespace Xamarin.Android.Tasks;
+
+partial class MonoAndroidHelper
+{
+ public static class AndroidAbi
+ {
+ public const string Arm32 = "armeabi-v7a";
+ public const string Arm64 = "arm64-v8a";
+ public const string X86 = "x86";
+ public const string X64 = "x86_64";
+ }
+
+ public static class RuntimeIdentifier
+ {
+ public const string Arm32 = "android-arm";
+ public const string Arm64 = "android-arm64";
+ public const string X86 = "android-x86";
+ public const string X64 = "android-x64";
+ }
+
+ public static readonly HashSet SupportedTargetArchitectures = new HashSet {
+ AndroidTargetArch.Arm,
+ AndroidTargetArch.Arm64,
+ AndroidTargetArch.X86,
+ AndroidTargetArch.X86_64,
+ };
+
+ static readonly char[] ZipPathTrimmedChars = {'/', '\\'};
+
+ static readonly Dictionary ClangAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) {
+ {"arm64-v8a", "aarch64"},
+ {"armeabi-v7a", "arm"},
+ {"x86", "i686"},
+ {"x86_64", "x86_64"}
+ };
+
+ static readonly Dictionary AbiToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) {
+ { AndroidAbi.Arm32, AndroidTargetArch.Arm },
+ { AndroidAbi.Arm64, AndroidTargetArch.Arm64 },
+ { AndroidAbi.X86, AndroidTargetArch.X86 },
+ { AndroidAbi.X64, AndroidTargetArch.X86_64 },
+ };
+
+ static readonly Dictionary AbiToRidMap = new Dictionary (StringComparer.OrdinalIgnoreCase) {
+ { AndroidAbi.Arm32, RuntimeIdentifier.Arm32 },
+ { AndroidAbi.Arm64, RuntimeIdentifier.Arm64 },
+ { AndroidAbi.X86, RuntimeIdentifier.X86 },
+ { AndroidAbi.X64, RuntimeIdentifier.X64 },
+ };
+
+ static readonly Dictionary RidToAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) {
+ { RuntimeIdentifier.Arm32, AndroidAbi.Arm32 },
+ { RuntimeIdentifier.Arm64, AndroidAbi.Arm64 },
+ { RuntimeIdentifier.X86, AndroidAbi.X86 },
+ { RuntimeIdentifier.X64, AndroidAbi.X64 },
+ };
+
+ static readonly Dictionary RidToArchMap = new Dictionary (StringComparer.OrdinalIgnoreCase) {
+ { RuntimeIdentifier.Arm32, AndroidTargetArch.Arm },
+ { RuntimeIdentifier.Arm64, AndroidTargetArch.Arm64 },
+ { RuntimeIdentifier.X86, AndroidTargetArch.X86 },
+ { RuntimeIdentifier.X64, AndroidTargetArch.X86_64 },
+ };
+
+ static readonly Dictionary ArchToRidMap = new Dictionary {
+ { AndroidTargetArch.Arm, RuntimeIdentifier.Arm32 },
+ { AndroidTargetArch.Arm64, RuntimeIdentifier.Arm64 },
+ { AndroidTargetArch.X86, RuntimeIdentifier.X86 },
+ { AndroidTargetArch.X86_64, RuntimeIdentifier.X64 },
+ };
+
+ static readonly Dictionary ArchToAbiMap = new Dictionary {
+ { AndroidTargetArch.Arm, AndroidAbi.Arm32 },
+ { AndroidTargetArch.Arm64, AndroidAbi.Arm64 },
+ { AndroidTargetArch.X86, AndroidAbi.X86 },
+ { AndroidTargetArch.X86_64, AndroidAbi.X64 },
+ };
+
+ public static AndroidTargetArch AbiToTargetArch (string abi)
+ {
+ if (!AbiToArchMap.TryGetValue (abi, out AndroidTargetArch arch)) {
+ return AndroidTargetArch.None;
+ };
+
+ return arch;
+ }
+
+ public static string AbiToRid (string abi)
+ {
+ if (!AbiToRidMap.TryGetValue (abi, out string rid)) {
+ throw new NotSupportedException ($"Internal error: unsupported ABI '{abi}'");
+ };
+
+ return rid;
+ }
+
+ public static string RidToAbi (string rid)
+ {
+ if (!RidToAbiMap.TryGetValue (rid, out string abi)) {
+ throw new NotSupportedException ($"Internal error: unsupported Runtime Identifier '{rid}'");
+ };
+
+ return abi;
+ }
+
+ public static AndroidTargetArch RidToArch (string rid)
+ {
+ if (!RidToArchMap.TryGetValue (rid, out AndroidTargetArch arch)) {
+ throw new NotSupportedException ($"Internal error: unsupported Runtime Identifier '{rid}'");
+ };
+
+ return arch;
+ }
+
+ public static string ArchToRid (AndroidTargetArch arch)
+ {
+ if (!ArchToRidMap.TryGetValue (arch, out string rid)) {
+ throw new InvalidOperationException ($"Internal error: unsupported architecture '{arch}'");
+ };
+
+ return rid;
+ }
+
+ public static string ArchToAbi (AndroidTargetArch arch)
+ {
+ if (!ArchToAbiMap.TryGetValue (arch, out string abi)) {
+ throw new InvalidOperationException ($"Internal error: unsupported architecture '{arch}'");
+ };
+
+ return abi;
+ }
+
+ public static string? CultureInvariantToString (object? obj)
+ {
+ if (obj == null) {
+ return null;
+ }
+
+ return Convert.ToString (obj, CultureInfo.InvariantCulture);
+ }
+
+ public static string MapAndroidAbiToClang (string androidAbi)
+ {
+ if (ClangAbiMap.TryGetValue (androidAbi, out string clangAbi)) {
+ return clangAbi;
+ }
+ return null;
+ }
+
+ public static string MakeZipArchivePath (string part1, params string[]? pathParts)
+ {
+ return MakeZipArchivePath (part1, (ICollection?)pathParts);
+ }
+
+ public static string MakeZipArchivePath (string part1, ICollection? pathParts)
+ {
+ var parts = new List ();
+ if (!String.IsNullOrEmpty (part1)) {
+ parts.Add (part1.TrimEnd (ZipPathTrimmedChars));
+ };
+
+ if (pathParts != null && pathParts.Count > 0) {
+ foreach (string p in pathParts) {
+ if (String.IsNullOrEmpty (p)) {
+ continue;
+ }
+ parts.Add (p.TrimEnd (ZipPathTrimmedChars));
+ }
+ }
+
+ if (parts.Count == 0) {
+ return String.Empty;
+ }
+
+ return String.Join ("/", parts);
+ }
+
+ public static bool IsValidAbi (string abi) => AbiToRidMap.ContainsKey (abi);
+
+ public static byte[] Utf8StringToBytes (string str) => Encoding.UTF8.GetBytes (str);
+
+ public static ulong GetXxHash (string str, bool is64Bit) => GetXxHash (Utf8StringToBytes (str), is64Bit);
+
+ public static ulong GetXxHash (byte[] stringBytes, bool is64Bit)
+ {
+ if (is64Bit) {
+ return XxHash64.HashToUInt64 (stringBytes);
+ }
+
+ return (ulong)XxHash32.HashToUInt32 (stringBytes);
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
index 4efceda5141..9c2794a367a 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MonoAndroidHelper.cs
@@ -1,18 +1,16 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
-using System.Globalization;
using System.Linq;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
-using System.Security.Cryptography;
using System.Text;
using Xamarin.Android.Tools;
using Xamarin.Tools.Zip;
-using Microsoft.Android.Build.Tasks;
#if MSBUILD
+using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
#endif
@@ -277,22 +275,6 @@ public static void LogWarning (object log, string msg, params object [] args)
}
#if MSBUILD
- static readonly Dictionary ClangAbiMap = new Dictionary (StringComparer.OrdinalIgnoreCase) {
- {"arm64-v8a", "aarch64"},
- {"armeabi-v7a", "arm"},
- {"x86", "i686"},
- {"x86_64", "x86_64"}
- };
-
- public static string MapAndroidAbiToClang (string androidAbi)
- {
- if (ClangAbiMap.TryGetValue (androidAbi, out string clangAbi)) {
- return clangAbi;
- }
- return null;
- }
-#endif
-
public static bool IsMonoAndroidAssembly (ITaskItem assembly)
{
var tfi = assembly.GetMetadata ("TargetFrameworkIdentifier");
@@ -313,6 +295,7 @@ public static bool HasMonoAndroidReference (ITaskItem assembly)
var reader = pe.GetMetadataReader ();
return HasMonoAndroidReference (reader);
}
+#endif
public static bool HasMonoAndroidReference (MetadataReader reader)
{
@@ -387,7 +370,6 @@ internal static IEnumerable GetFrameworkAssembliesToTreatAsUserAssemb
return ret;
}
-#endif
public static bool SaveMapFile (IBuildEngine4 engine, string mapFile, Dictionary map)
{
@@ -448,6 +430,7 @@ public static bool SaveCustomViewMapFile (IBuildEngine4 engine, string mapFile,
return Files.CopyIfStreamChanged (writer.BaseStream, mapFile);
}
}
+#endif // MSBUILD
public static string [] GetProguardEnvironmentVaribles (string proguardHome)
{
@@ -482,6 +465,8 @@ public static IEnumerable Executables (string executable)
yield return executable;
}
+
+#if MSBUILD
public static string TryGetAndroidJarPath (TaskLoggingHelper log, string platform, bool designTimeBuild = false, bool buildingInsideVisualStudio = false, string targetFramework = "", string androidSdkDirectory = "")
{
var platformPath = MonoAndroidHelper.AndroidSdk.TryGetPlatformDirectoryFromApiLevel (platform, MonoAndroidHelper.SupportedVersions);
@@ -503,7 +488,7 @@ public static void SaveResourceCaseMap (IBuildEngine4 engine, Dictionary LoadResourceCaseMap (IBuildEngine4 engine, Func
diff --git a/src/r8/build.gradle b/src/r8/build.gradle
index 58ce928562d..e95e9ce1a52 100644
--- a/src/r8/build.gradle
+++ b/src/r8/build.gradle
@@ -15,7 +15,7 @@ repositories {
}
dependencies {
- implementation 'com.android.tools:r8:8.2.42'
+ implementation 'com.android.tools:r8:8.2.47'
}
jar {
diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallTests.cs
index f25d948b44c..f1021c2d15d 100644
--- a/tests/MSBuildDeviceIntegration/Tests/InstallTests.cs
+++ b/tests/MSBuildDeviceIntegration/Tests/InstallTests.cs
@@ -495,6 +495,7 @@ public void LocalizedAssemblies_ShouldBeFastDeployed ()
[Test]
public void IncrementalFastDeployment ()
{
+ Assert.Ignore ("https://github.com/NuGet/Home/issues/13269");
AssertCommercialBuild ();
var class1src = new BuildItem.Source ("Class1.cs") {
diff --git a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs
index 96f66778ef3..b71ee2893da 100644
--- a/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs
+++ b/tests/Mono.Android-Tests/Xamarin.Android.Net/AndroidMessageHandlerTests.cs
@@ -98,6 +98,31 @@ async Task DoDecompression (string urlPath, string encoding, string jsonFi
return true;
}
+ [Test]
+ public async Task DoesNotDisposeContentStream()
+ {
+ using var listener = new HttpListener ();
+ listener.Prefixes.Add ("http://+:47663/");
+ listener.Start ();
+ listener.BeginGetContext (ar => {
+ var ctx = listener.EndGetContext (ar);
+ ctx.Response.StatusCode = 204;
+ ctx.Response.ContentLength64 = 0;
+ ctx.Response.Close ();
+ }, null);
+
+ var jsonContent = new StringContent ("hello");
+ var request = new HttpRequestMessage (HttpMethod.Post, "http://localhost:47663/") { Content = jsonContent };
+
+ var response = await new HttpClient (new AndroidMessageHandler ()).SendAsync (request);
+ Assert.True (response.IsSuccessStatusCode);
+
+ var contentValue = await jsonContent.ReadAsStringAsync ();
+ Assert.AreEqual ("hello", contentValue);
+
+ listener.Close ();
+ }
+
[Test]
public async Task ServerCertificateCustomValidationCallback_ApproveRequest ()
{