-
Notifications
You must be signed in to change notification settings - Fork 564
Special Char Support #6273
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Special Char Support #6273
Conversation
|
I was going to comment that the test works, so seemingly there isn't a problem, right? …but then I thought about it: I don't think any of Which means we have no idea if non-ASCII paths work on Windows at all, at least not via CI, right? |
|
I'll see about adding the SmokeTest run of MSBuildDeviceIntegration to the windows smoke test run |
29b21fb to
04ea048
Compare
53a1483 to
74ed2b3
Compare
| rawToDelete, | ||
| }, | ||
| }; | ||
| libProj.SetProperty ("Deterministic", "true"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonathanpeppers I'm not sure why I've had to add this to this particular test. It was failing without it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Frankly, we should use $(Deterministic)=true everywhere…
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs
Show resolved
Hide resolved
|
Turns out @jonathanpeppers found AndroidStudio doesn't even let you create a project with these special characters. So I guess it doesn't work at all in Android Studio. |
0850448 to
80a4cdc
Compare
| if (isDirectory) { | ||
| cmd.Add ("--dir"); | ||
| cmd.Add (isArchive ? "--zip" : "--dir"); | ||
| cmd.Add (GetFullPath (fileOrDirectory).TrimEnd ('\\')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that this PR is in the context of "Chinese character set support", and that this is a command-line argument to aapt2, should this really use GetFullPath(fileOrDirectory) at all anymore?
I'm kinda surprised that this works at all, in that the PRs are green…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It needs to use get full path because aapt2 does not work in the project directory, but in a temp directory. This is to get around problems where the aapt2 daemons would lock the project directory while VS was running. So ALL the paths we pass to aapt2 need to be full paths.
The reason this works is because passing a zip file means aapt2 does not try to traverse the directory, which is where the buggy code is in aapt2.
| cmd.AppendSwitch (AdditionalArguments); | ||
|
|
||
| cmd.AppendSwitchIfNotNull (" ", Path.GetFullPath (ApkToSign)); | ||
| cmd.AppendSwitchIfNotNull (" ", ApkToSign); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
…especially considering that we're removing Path.GetFullPath() here…
src/Xamarin.Android.Build.Tasks/Tasks/ResolveLibraryProjectImports.cs
Outdated
Show resolved
Hide resolved
| </PropertyGroup> | ||
| <AndroidSignPackage Condition=" '$(AndroidUseApkSigner)' != 'true' " | ||
| UnsignedApk="%(ApkAbiFilesIntermediate.FullPath)" | ||
| UnsignedApk="%(ApkAbiFilesIntermediate.Identity)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're using %(Identity), why not instead use @(ApkAbiFilesIntermediate)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto subsequent changes as well. Why prefer %(Foo.Identity) over @(Foo)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So AndroidSignPackage and ApkSigner need to use %(Foo.Identity) so that MSbuild will batch the calls. These tools can only sign one package at a time. If we provided @(Foo) and we had multiple apps (like we do when we split on abi) we would send the following to the signing tool
fooo.apk;foo2.apk
which is not what we want. What we want is for MSbuild to call AndroidSignPackage for each apk, and we do that via batching.
src/Xamarin.Android.Build.Tasks/Xamarin.Android.EmbeddedResource.targets
Outdated
Show resolved
Hide resolved
80a4cdc to
5c62d13
Compare
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
bbb563f to
e730adf
Compare
Binutils 2.36 introduced [code][0] to deal extra long path names on
Windows which makes compilation (in our case using `as`) and linking
to break if the source/object files reside in a directory which has
special characters (in our case they were `テスト`):
Xamarin.Android.Common.targets(1887,3): error XA3006: Could not compile native assembly file: typemaps.armeabi-v7a.s [SmokeTestBuildWithSpecialCharactersFalse\テスト\テスト.csproj]
[aarch64-linux-android-as.EXE stderr] Assembler messages: (TaskId:187)
[aarch64-linux-android-as.EXE stderr] Fatal error: can't create typemaps.arm64-v8a.o: Invalid argument (TaskId:187)
Downgrade binutils version to 2.35.2 which doesn't contain the offending
code block and will work for us, with relative paths. It will **still**
break with full paths if the path contains special characters, but since
our use case involves only relative paths, this is fine.
There is also a bug in aapt2 [1] which stops it processing a directory which has
certain special characters on windows. This will only appear on projects which
have library resources, such as Xamarin.Forms. The problem only appears when
processing a directory into a flata archive. Oddly it works file when processing a
file directly. In order to work around this we now generate a resource only `res.zip`
file when we extract the library resources. This `res.zip` is then used during the
generation of the `flata` archive. Weirdly, this works where directory transversal does
not.
Additionally it seems to be slightly quicker
Before
`581 ms Aapt2Compile 1 calls`
After
`366 ms Aapt2Compile 1 calls`
So the time taken to generate the `res.zip` is offset by the quicker execution of
`aapt2 compile`.
Finally, `java` tooling such as `zipalign` and `apksigner` also failed with the
special characters. This was because we were using full paths to the files
we were signing/aligning. Switching over to use relative paths fixes this particular
problem.
Add a set of Unit tests to make sure we can handle the special characters.
[0]: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=bfd/bfdio.c;h=463b3879c52ba6beac47190f8eb0810b0c330e65;hb=HEAD#l124
[1] https://issuetracker.google.com/issues/188679588
e730adf to
5d9ef9a
Compare
| CreateResourceArchive (resDir, resDirArchive); | ||
| var skipProcessing = aarFile.GetMetadata (AndroidSkipResourceProcessing); | ||
| if (string.IsNullOrEmpty (skipProcessing)) { | ||
| skipProcessing = "True"; | ||
| } | ||
| resolvedResourceDirectories.Add (new TaskItem (Path.GetFullPath (resDir), new Dictionary<string, string> { | ||
| { OriginalFile, aarFullPath }, | ||
| { AndroidSkipResourceProcessing, skipProcessing }, | ||
| { ResourceDirectoryArchive, Path.GetFullPath (resDirArchive)}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For .aar files, can we simply set their existing path to %(ResourceDirectoryArchive) and not create a new .zip file?
I think this will help performance quite a bit for AndroidX, which is mostly .aar files now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried that. aapt2 errors because there are files like classes.jar etc in there. It only seems to like files which ONLY contain res files.
| [Test] | ||
| [Category ("SmokeTests")] | ||
| public void SmokeTestBuildWithSpecialCharacters ([Values (false, true)] bool forms) | ||
| { | ||
| var testName = "テスト"; | ||
|
|
||
| var rootPath = Path.Combine (Root, "temp", TestName); | ||
| var proj = forms ? | ||
| new XamarinFormsAndroidApplicationProject () : | ||
| new XamarinAndroidApplicationProject (); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we put a test like this in MSBuildDeviceIntegration, to make sure it deploys and the app launches?
I'm wondering if something Fast Deployment might break, and we didn't catch it yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem, as outlined earlier, is that the MSBuildDeviceIntegration tests only run on macOS, which doesn't have a "non-ASCII characters" problem in the first place.
What we want/need is for MSBuildDevice integration to run on Windows. The problem is we tried that in an earlier iteration of #6297, and hit #6297 (comment)
Instead, it (later) insta-fails, because of virtualization:
[emulator stdout] emulator: ERROR: x86_64 emulation currently requires hardware acceleration! (TaskId:5) C:\a\_work\1\s\build-tools\scripts\TestApks.targets(65,5): error : Emulator failed to start: emulator: ERROR: x86_64 emulation currently requires hardware acceleration! [C:\a\_work\1\s\tests\Mono.Android-Tests\Mono.Android-Tests.csproj] [emulator stdout] CPU acceleration status: Hyper-V detected and Windows Hypervisor Platform is available. Please ensure both the "Hyper-V" and "Windows Hypervisor Platform" features enabled in "Turn Windows features on or off". (TaskId:5) [emulator stdout] More info on configuring VM acceleration on Windows: (TaskId:5) [emulator stdout] https://developer.android.com/studio/run/emulator-acceleration#vm-windows (TaskId:5) [emulator stdout] General information on acceleration: https://developer.android.com/studio/run/emulator-acceleration. (TaskId:5)
@pjcollins had additional followup: #6297 (comment)
- Investigate Helix machine pools for Android emulator testing.
- Revisit physical Windows machine usage. One possible target would be the shared pool we used to run QA tests on. Some of these machines have physical Android devices attached, but could still potentially be used: …
- See if we can somehow enable virtualization on our 1ES Windows pool.
In the meantime, we're going to need to rely on manual testing for Windows (ugh).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This suggests an "alternate half step": instead of requiring "full" MSBuildDeviceIntegration tests, would building the project with /t:SignAndroidPackage /p:Configuration=Release be a meaningfully useful intermediate step? This wouldn't test fast deployment -- or any deployment, for that matter -- but would hit the "binutils doesn't like non-ASCII" codepath, plus aapt, plus lots of other parts of the build system.
Should we add a "build (no device attached!) test" around the SignAndroidPackage target and non-ASCII directories?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test already does that. A basic release build produces the apk etc, so it will go through all the bin utils stuff, and aapt2. It runs on windows and Mac.
We do have a version of the same test which runs on Device but that is only gonna run on Mac because the emulator cannot run on the build machines we use for windows.
[Xamarin.Android.Build.Tasks] Special Char Support (#6273)
What would we like? The ability for Xamarin.Android projects to
reside in directories containing non-ASCII characters.
The problem(s)? For starters, many Android SDK utilities don't
support non-ASCII characters when running on Windows, e.g.:
* https://issuetracker.google.com/issues/188679588
Android Studio (kinda/sorta) "enforces" this, showing a warning
message when **Save location** contains non-ASCII characters:

> Save location: /tmp/テスト
> …
> ⚠️ Your project location contains non-ASCII characters.
These are not particularly encouraging foundations.
Improve support for non-ASCII characters by updating various parts
of our build system to:
1. Prefer *relatives* paths when possible, as we can ensure
ASCII-only paths this way, and
2. "Otherwise" change *how* we invoke Android tools to avoid their
use of full paths and directory traversal.
Additionally, we've long used GNU Binutils to build `libxamarin-app.so`
(commit decfbccf, fc3f0282, others), and we discovered that
Binutils 2.36 introduced [code][0] to deal extra long path names on
Windows which makes compilation (in our case using `as`) and linking
to break if the source/object files reside in a directory which has
non-ASCII characters (in our case they were `テスト`):
Xamarin.Android.Common.targets(1887,3): error XA3006: Could not compile native assembly file: typemaps.armeabi-v7a.s [SmokeTestBuildWithSpecialCharactersFalse\テスト\テスト.csproj]
[aarch64-linux-android-as.EXE stderr] Assembler messages: (TaskId:187)
[aarch64-linux-android-as.EXE stderr] Fatal error: can't create typemaps.arm64-v8a.o: Invalid argument (TaskId:187)
Downgrade to Binutils 2.35.2, which doesn't contain the offending
code block and will work for us, so long as we use relative paths.
It will **still** break with full paths if the path contains special
characters, but since our use case involves only relative paths,
this is fine.
There is also a bug in [`aapt2`][1] which stops it processing a
directory which has certain special characters on Windows. This will
only appear on projects which have library resources, such as
Xamarin.Forms. The problem only appears when processing a directory
into a `.flata` archive. Oddly it works fine when processing a file
directly. In order to work around this we now generate a resource
only `res.zip` file when we extract the library resources.
`res.zip` is then used during the generation of the `.flata` archive.
Weirdly, this works where directory transversal does not.
Additionally it seems to be slightly quicker during incremental builds:
* Before
581 ms Aapt2Compile 1 calls
* After
366 ms Aapt2Compile 1 calls
So the time taken to generate the `res.zip` is offset by the quicker
execution of `aapt2 compile`.
Finally, Java tooling such as `zipalign` and `apksigner` also failed
with the special characters. This was because we were using full
paths to the files we were signing/aligning. Switching over to use
relative paths fixes this particular problem.
Add a set of Unit tests to make sure we can handle the special characters.
[0]: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=bfd/bfdio.c;h=463b3879c52ba6beac47190f8eb0810b0c330e65;hb=HEAD#l124
[1]: https://issuetracker.google.com/issues/188679588 |
Binutils 2.36 introduced code to deal extra long path names on
Windows which makes compilation (in our case using
as) and linkingto break if the source/object files reside in a directory which has
special characters (in our case they were
テスト):Downgrade binutils version to 2.35.2 which doesn't contain the offending
code block and will work for us, with relative paths. It will still
break with full paths if the path contains special characters, but since
our use case involves only relative paths, this is fine.
There is also a bug in aapt2 [1] which stops it processing a directory which has
certain special characters on windows. This will only appear on projects which
have library resources, such as Xamarin.Forms. The problem only appears when
processing a directory into a flata archive. Oddly it works file when processing a
file directly. In order to work around this we now generate a resource only
res.zipfile when we extract the library resources. This
res.zipis then used during thegeneration of the
flataarchive. Weirdly, this works where directory transversal doesnot.
Additionally it seems to be slightly quicker
Before
581 ms Aapt2Compile 1 callsAfter
366 ms Aapt2Compile 1 callsSo the time taken to generate the
res.zipis offset by the quicker execution ofaapt2 compile.Finally,
javatooling such aszipalignandapksigneralso failed with thespecial characters. This was because we were using full paths to the files
we were signing/aligning. Switching over to use relative paths fixes this particular
problem.
Add a set of Unit tests to make sure we can handle the special characters.
[1] https://issuetracker.google.com/issues/188679588