From 8645de76a590ade5cd30eff39bc6567ce59cd13b Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Tue, 17 Jan 2023 12:31:27 +0000 Subject: [PATCH] MAUI project with non-ASCII project name cannot release to my Android phone --- Documentation/guides/messages/apt2264.md | 18 +++++---- .../Properties/Resources.resx | 2 +- .../Tasks/Aapt2.cs | 39 ++++++++++++------- .../Xamarin.Android.Build.Tests/AotTests.cs | 27 +++++++++++++ .../Utilities/DeviceTest.cs | 10 ++++- .../Tests/InstallAndRunTests.cs | 7 ++-- 6 files changed, 75 insertions(+), 28 deletions(-) diff --git a/Documentation/guides/messages/apt2264.md b/Documentation/guides/messages/apt2264.md index d058a29a74f..8d264efa562 100644 --- a/Documentation/guides/messages/apt2264.md +++ b/Documentation/guides/messages/apt2264.md @@ -9,27 +9,29 @@ ms.date: 12/16/2022 The tool `aapt2` is unable to resolve one of the files it was passed. This is generally caused by the path being longer than the Maximum Path -length allowed on windows. +length allowed on windows. Alternately it might be due to non-ASCII +characters in the project name or the path to the project. ## Solution The best way to avoid this is to ensure that your project is not located -deep in the folder structure. For example if you create all of your -projects in folders such as +deep in the folder structure and does not contain non-ASCII characters. +For example if you create all of your projects in folders such as -`C:\Users\shelly\Visual Studio\Android\MyProjects\Com.SomeReallyLongCompanyName.MyBrillantApplication\MyBrilliantApplicaiton.Android\` +`C:\Users\shëlly\Visual Studio\Android\MyProjects\Com.SomeReallyLongCompanyName.MyBrillantApplication\MyBrilliantApplicaiton.Android\` you may well encounter problems with not only `aapt2` but also Ahead of Time -compilation. Keeping your project names and folder structures short and -concise will help work around these issues. For example instead of the above +compilation. Keeping your project names and folder structures short, concise and +ASCII will help work around these issues. For example instead of the above you could use `C:\Work\Android\MyBrilliantApp` Which is much shorter and much less likely to encounter path issues. -However this is no always possible. Sometimes a project or a environment requires -deep folder structures. In this case enabling long path support in Windows *might* +However this is not always possible. Sometimes a project or a environment requires +deep folder structures. For non-ASCII paths there is no work around. +In the long path case, enabling long path support in Windows *might* be enough to get your project working. Details on how to do this can be found [here](https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation?tabs=registry#enable-long-paths-in-windows-10-version-1607-and-later). diff --git a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx index dfb964b4851..7409bfc460f 100644 --- a/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx +++ b/src/Xamarin.Android.Build.Tasks/Properties/Resources.resx @@ -542,7 +542,7 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla {0} - The assembly name - This is probably caused by the project exceeding the Windows maximum path length limitation. See https://learn.microsoft.com/xamarin/android/errors-and-warnings/apt2264 for details. + This is probably caused by the project having non-ASCII characters in it path or exceeding the Windows maximum path length limitation. See https://learn.microsoft.com/xamarin/android/errors-and-warnings/apt2264 for details. The following are literal names and should not be translated: diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs index e411b66e61e..abba06c73e6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs @@ -138,22 +138,8 @@ protected bool LogAapt2EventsFromOutput (string singleLine, MessageImportance me LogCodedError ("APT0001", Properties.Resources.APT0001, message.Substring ("unknown option '".Length).TrimEnd ('.', '\'')); return false; } - if (message.Contains ("in APK") && message.Contains ("is compressed.")) { - LogMessage (singleLine, messageImportance); + if (LogNotesOrWarnings (message, singleLine, messageImportance)) return true; - } - if (message.Contains ("fakeLogOpen")) { - LogMessage (singleLine, messageImportance); - return true; - } - if (message.Contains ("note:")) { - LogMessage (singleLine, messageImportance); - return true; - } - if (message.Contains ("warn:")) { - LogCodedWarning (GetErrorCode (singleLine), singleLine); - return true; - } if (level.Contains ("note")) { LogMessage (message, messageImportance); return true; @@ -199,6 +185,8 @@ protected bool LogAapt2EventsFromOutput (string singleLine, MessageImportance me if (!apptResult) { var message = string.Format ("{0} \"{1}\".", singleLine.Trim (), singleLine.Substring (singleLine.LastIndexOfAny (new char [] { '\\', '/' }) + 1)); + if (LogNotesOrWarnings (message, singleLine, messageImportance)) + return true; var errorCode = GetErrorCode (message); LogCodedError (errorCode, AddAdditionalErrorText (errorCode, message), ToolName); } else { @@ -207,6 +195,27 @@ protected bool LogAapt2EventsFromOutput (string singleLine, MessageImportance me return true; } + bool LogNotesOrWarnings (string message, string singleLine, MessageImportance messageImportance) + { + if (message.Contains ("in APK") && message.Contains ("is compressed.")) { + LogMessage (singleLine, messageImportance); + return true; + } + if (message.Contains ("fakeLogOpen")) { + LogMessage (singleLine, messageImportance); + return true; + } + if (message.Contains ("note:")) { + LogMessage (singleLine, messageImportance); + return true; + } + if (message.Contains ("warn:")) { + LogCodedWarning (GetErrorCode (singleLine), singleLine); + return true; + } + return false; + } + static string AddAdditionalErrorText (string errorCode, string message) { var sb = new StringBuilder (); 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 20d2a2282f5..535d32673e1 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 @@ -112,6 +112,33 @@ public void BuildBasicApplicationReleaseProfiledAotWithoutDefaultProfile () StringAssertEx.DoesNotContainRegex (@$"Using profile data file.*{filename}\.aotprofile", b.LastBuildOutput, "Should not use default AOT profile", RegexOptions.IgnoreCase); } + [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) + { + if (!IsWindows) + expectedResult = true; + var rootPath = Path.Combine (Root, "temp", TestName); + var proj = new XamarinAndroidApplicationProject () { + ProjectName = testName, + IsRelease = isRelease, + AotAssemblies = aot, + }; + 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."); + } + } + static object [] AotChecks () => new object [] { new object[] { /* supportedAbis */ "arm64-v8a", diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs index 0184ef1d425..825e77f6735 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs @@ -26,6 +26,11 @@ public class DeviceTest: BaseTest protected static bool IsDeviceAttached (bool refreshCachedValue = false) { if (string.IsNullOrEmpty (_shellEchoOutput) || refreshCachedValue) { + // run this twice as sometimes the first time returns the + // device as "offline". + RunAdbCommand ("devices"); + var devices = RunAdbCommand ("devices"); + TestContext.Out.WriteLine ($"LOG adb devices: {devices}"); _shellEchoOutput = RunAdbCommand ("shell echo OK", timeout: 15); } return _shellEchoOutput.Contains ("OK"); @@ -58,11 +63,14 @@ public void DeviceSetup () DeviceAbi = RunAdbCommand ("shell getprop ro.product.cpu.abilist64").Trim (); if (string.IsNullOrEmpty (DeviceAbi)) - DeviceAbi = RunAdbCommand ("shell getprop ro.product.cpu.abi") ?? RunAdbCommand ("shell getprop ro.product.cpu.abi2"); + DeviceAbi = (RunAdbCommand ("shell getprop ro.product.cpu.abi") ?? RunAdbCommand ("shell getprop ro.product.cpu.abi2")) ?? "x86_64"; if (DeviceAbi.Contains (",")) { DeviceAbi = DeviceAbi.Split (',')[0]; } + } else { + TestContext.Out.WriteLine ($"LOG GetSdkVersion: {DeviceSdkVersion}"); + DeviceAbi = "x86_64"; } } catch (Exception ex) { Console.Error.WriteLine ("Failed to determine whether there is Android target emulator or not: " + ex); diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 1672b36b7cb..a6e903cc6dd 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -167,10 +167,11 @@ void Reset () [Test] [Category ("UsesDevice")] - public void SmokeTestBuildAndRunWithSpecialCharacters () + [TestCase ("テスト")] + [TestCase ("随机生成器")] + [TestCase ("中国")] + public void SmokeTestBuildAndRunWithSpecialCharacters (string testName) { - var testName = "テスト"; - var rootPath = Path.Combine (Root, "temp", TestName); var proj = new XamarinFormsAndroidApplicationProject () { ProjectName = testName,