Skip to content

Commit 97caad8

Browse files
committed
MAUI project with non-ASCII project name cannot release to my Android phone
Context https://i.azdo.io/1714603 Context https://issuetracker.google.com/issues/188679588 There is a known issue with `aapt2`, `AndroidAsset` and non-ASCII paths/project names. `aapt2` has a bug where it cannot correctly traverse a directory which contains non-ASCII characters. We curently have no way to work around this issue. So the PR adds some more detail to the specific error message which `aapt2` raises in these cases. It suggests the user check their paths to make sure it does not contain non-ASCII characters. We also add some unit tests to cover this senario.
1 parent dda99cd commit 97caad8

File tree

6 files changed

+75
-28
lines changed

6 files changed

+75
-28
lines changed

Documentation/guides/messages/apt2264.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,29 @@ ms.date: 12/16/2022
99

1010
The tool `aapt2` is unable to resolve one of the files it was passed.
1111
This is generally caused by the path being longer than the Maximum Path
12-
length allowed on windows.
12+
length allowed on windows. Alternately it might be due to non-ASCII
13+
characters in the project name or the path to the project.
1314

1415
## Solution
1516

1617
The best way to avoid this is to ensure that your project is not located
17-
deep in the folder structure. For example if you create all of your
18-
projects in folders such as
18+
deep in the folder structure and does not contain non-ASCII characters.
19+
For example if you create all of your projects in folders such as
1920

20-
`C:\Users\shelly\Visual Studio\Android\MyProjects\Com.SomeReallyLongCompanyName.MyBrillantApplication\MyBrilliantApplicaiton.Android\`
21+
`C:\Users\shëlly\Visual Studio\Android\MyProjects\Com.SomeReallyLongCompanyName.MyBrillantApplication\MyBrilliantApplicaiton.Android\`
2122

2223
you may well encounter problems with not only `aapt2` but also Ahead of Time
23-
compilation. Keeping your project names and folder structures short and
24-
concise will help work around these issues. For example instead of the above
24+
compilation. Keeping your project names and folder structures short, concise and
25+
ASCII will help work around these issues. For example instead of the above
2526
you could use
2627

2728
`C:\Work\Android\MyBrilliantApp`
2829

2930
Which is much shorter and much less likely to encounter path issues.
3031

31-
However this is no always possible. Sometimes a project or a environment requires
32-
deep folder structures. In this case enabling long path support in Windows *might*
32+
However this is not always possible. Sometimes a project or a environment requires
33+
deep folder structures. For non-ASCII paths there is no work around.
34+
In the long path case, enabling long path support in Windows *might*
3335
be enough to get your project working. Details on how to do this can be found
3436
[here](https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation?tabs=registry#enable-long-paths-in-windows-10-version-1607-and-later).
3537

src/Xamarin.Android.Build.Tasks/Properties/Resources.resx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla
542542
{0} - The assembly name</comment>
543543
</data>
544544
<data name="APT2264" xml:space="preserve">
545-
<value>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.</value>
545+
<value>This could be 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.</value>
546546
<comment>The following are literal names and should not be translated:</comment>
547547
</data>
548548
<data name="XA3001" xml:space="preserve">

src/Xamarin.Android.Build.Tasks/Tasks/Aapt2.cs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -138,22 +138,8 @@ protected bool LogAapt2EventsFromOutput (string singleLine, MessageImportance me
138138
LogCodedError ("APT0001", Properties.Resources.APT0001, message.Substring ("unknown option '".Length).TrimEnd ('.', '\''));
139139
return false;
140140
}
141-
if (message.Contains ("in APK") && message.Contains ("is compressed.")) {
142-
LogMessage (singleLine, messageImportance);
141+
if (LogNotesOrWarnings (message, singleLine, messageImportance))
143142
return true;
144-
}
145-
if (message.Contains ("fakeLogOpen")) {
146-
LogMessage (singleLine, messageImportance);
147-
return true;
148-
}
149-
if (message.Contains ("note:")) {
150-
LogMessage (singleLine, messageImportance);
151-
return true;
152-
}
153-
if (message.Contains ("warn:")) {
154-
LogCodedWarning (GetErrorCode (singleLine), singleLine);
155-
return true;
156-
}
157143
if (level.Contains ("note")) {
158144
LogMessage (message, messageImportance);
159145
return true;
@@ -199,6 +185,8 @@ protected bool LogAapt2EventsFromOutput (string singleLine, MessageImportance me
199185

200186
if (!apptResult) {
201187
var message = string.Format ("{0} \"{1}\".", singleLine.Trim (), singleLine.Substring (singleLine.LastIndexOfAny (new char [] { '\\', '/' }) + 1));
188+
if (LogNotesOrWarnings (message, singleLine, messageImportance))
189+
return true;
202190
var errorCode = GetErrorCode (message);
203191
LogCodedError (errorCode, AddAdditionalErrorText (errorCode, message), ToolName);
204192
} else {
@@ -207,6 +195,27 @@ protected bool LogAapt2EventsFromOutput (string singleLine, MessageImportance me
207195
return true;
208196
}
209197

198+
bool LogNotesOrWarnings (string message, string singleLine, MessageImportance messageImportance)
199+
{
200+
if (message.Contains ("in APK") && message.Contains ("is compressed.")) {
201+
LogMessage (singleLine, messageImportance);
202+
return true;
203+
}
204+
if (message.Contains ("fakeLogOpen")) {
205+
LogMessage (singleLine, messageImportance);
206+
return true;
207+
}
208+
if (message.Contains ("note:")) {
209+
LogMessage (singleLine, messageImportance);
210+
return true;
211+
}
212+
if (message.Contains ("warn:")) {
213+
LogCodedWarning (GetErrorCode (singleLine), singleLine);
214+
return true;
215+
}
216+
return false;
217+
}
218+
210219
static string AddAdditionalErrorText (string errorCode, string message)
211220
{
212221
var sb = new StringBuilder ();

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/AotTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,33 @@ public void BuildBasicApplicationReleaseProfiledAotWithoutDefaultProfile ()
112112
StringAssertEx.DoesNotContainRegex (@$"Using profile data file.*{filename}\.aotprofile", b.LastBuildOutput, "Should not use default AOT profile", RegexOptions.IgnoreCase);
113113
}
114114

115+
[Test]
116+
[TestCase ("テスト", false, false, true)]
117+
[TestCase ("テスト", true, true, false)]
118+
[TestCase ("テスト", true, false, true)]
119+
[TestCase ("随机生成器", false, false, true)]
120+
[TestCase ("随机生成器", true, true, false)]
121+
[TestCase ("随机生成器", true, false, true)]
122+
[TestCase ("中国", false, false, true)]
123+
[TestCase ("中国", true, true, false)]
124+
[TestCase ("中国", true, false, true)]
125+
public void BuildAotApplicationWithSpecialCharactersInProject (string testName, bool isRelease, bool aot, bool expectedResult)
126+
{
127+
if (!IsWindows)
128+
expectedResult = true;
129+
var rootPath = Path.Combine (Root, "temp", TestName);
130+
var proj = new XamarinAndroidApplicationProject () {
131+
ProjectName = testName,
132+
IsRelease = isRelease,
133+
AotAssemblies = aot,
134+
};
135+
proj.SetAndroidSupportedAbis ("armeabi-v7a", "arm64-v8a", "x86", "x86_64");
136+
using (var builder = CreateApkBuilder (Path.Combine (rootPath, proj.ProjectName))){
137+
builder.ThrowOnBuildFailure = false;
138+
Assert.AreEqual (expectedResult, builder.Build (proj), "Build should have succeeded.");
139+
}
140+
}
141+
115142
static object [] AotChecks () => new object [] {
116143
new object[] {
117144
/* supportedAbis */ "arm64-v8a",

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public class DeviceTest: BaseTest
2626
protected static bool IsDeviceAttached (bool refreshCachedValue = false)
2727
{
2828
if (string.IsNullOrEmpty (_shellEchoOutput) || refreshCachedValue) {
29+
// run this twice as sometimes the first time returns the
30+
// device as "offline".
31+
RunAdbCommand ("devices");
32+
var devices = RunAdbCommand ("devices");
33+
TestContext.Out.WriteLine ($"LOG adb devices: {devices}");
2934
_shellEchoOutput = RunAdbCommand ("shell echo OK", timeout: 15);
3035
}
3136
return _shellEchoOutput.Contains ("OK");
@@ -58,11 +63,14 @@ public void DeviceSetup ()
5863
DeviceAbi = RunAdbCommand ("shell getprop ro.product.cpu.abilist64").Trim ();
5964

6065
if (string.IsNullOrEmpty (DeviceAbi))
61-
DeviceAbi = RunAdbCommand ("shell getprop ro.product.cpu.abi") ?? RunAdbCommand ("shell getprop ro.product.cpu.abi2");
66+
DeviceAbi = (RunAdbCommand ("shell getprop ro.product.cpu.abi") ?? RunAdbCommand ("shell getprop ro.product.cpu.abi2")) ?? "x86_64";
6267

6368
if (DeviceAbi.Contains (",")) {
6469
DeviceAbi = DeviceAbi.Split (',')[0];
6570
}
71+
} else {
72+
TestContext.Out.WriteLine ($"LOG GetSdkVersion: {DeviceSdkVersion}");
73+
DeviceAbi = "x86_64";
6674
}
6775
} catch (Exception ex) {
6876
Console.Error.WriteLine ("Failed to determine whether there is Android target emulator or not: " + ex);

tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,11 @@ void Reset ()
167167

168168
[Test]
169169
[Category ("UsesDevice")]
170-
public void SmokeTestBuildAndRunWithSpecialCharacters ()
170+
[TestCase ("テスト")]
171+
[TestCase ("随机生成器")]
172+
[TestCase ("中国")]
173+
public void SmokeTestBuildAndRunWithSpecialCharacters (string testName)
171174
{
172-
var testName = "テスト";
173-
174175
var rootPath = Path.Combine (Root, "temp", TestName);
175176
var proj = new XamarinFormsAndroidApplicationProject () {
176177
ProjectName = testName,

0 commit comments

Comments
 (0)