-
Notifications
You must be signed in to change notification settings - Fork 30
Fix sort ordering for ndk-bundle, add macOS support #91
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
Conversation
ea98ace
to
1a81057
Compare
Wait this isn't green:
|
1a81057
to
eefaef6
Compare
What should be the semantics of Should Given that the local I believe that what is more "reasonable" is the latter behavior: that if In order for that to happen, I think we'd need to remove Relatedly, |
eefaef6
to
ab45fd0
Compare
Related: #92 |
Context: https://build.azdo.io/xamarin/public/21545 An environment change on Azure DevOps seems to cause the test failure on Windows: Ndk_PathInSdk AndroidNdkPath not found inside sdk! Expected string length 71 but was 53. Strings differ at index 3. Expected: "C:\Users\VssAdministrator\AppData\Local\Temp\tmpAE78.tmp\sdk\..." But was: "C:\Program Files (x86)\Android\android-sdk\ndk-bundle" It appears that `xamarin-android-tools` is preferring an `ndk-bundle found on the system instead of the value passed in for `AndroidSdkPath`. To make matters worse, use of `Distinct()` returns an unordered sequence, and so the sort ordering is unknown: https://docs.microsoft.com/dotnet/api/system.linq.enumerable.distinct To fix this, let's not use `ToList()` or add `AndroidSdkPath` to the list at all. We can make an explicit check for it before the `foreach` loop. Inside the loop we should skip `AndroidSdkPath`. I also added the same code for the macOS/linux side in `AndroidSdkUnix`. I updated the `Ndk_PathInSdk` test so it will run on all platforms.
ab45fd0
to
f8c43c9
Compare
Fixes: dotnet#92 What we want *sounds* straightforward: given: var info = new AndroidSdkInfo (androidSdkPath: validSdkDir); then *if* `{validSdkDir}/ndk-bundle` exists, then `info.AndroidNdkPath` should be `{validSdkDir}/ndk-bundle`. Unfortunately, this is Doomed™, for two reasons: 1. `AndroidSdkBase.Initialize()` uses `AndroidSdkBase.PreferedAndroidNdkPath` if the `androidNdkPath` parameter is `null`, which will be the case here, and 2. If by chance `AndroidSdkBase.PreferedAndroidNdkPath` isn't set, we still hit `AllAndroidNdks.FirstOrDefault()`, which involves a `.Distinct()` call. The problem with [`Enumerable.Distinct()`][0] is that order is not preserved: > returns an unordered sequence that contains no duplicate values Note *unordered* sequence. Thus, the order should be *considered* to be effectively random. At which point I throw up my arms and say "this is too complicated," and start ripping out unnecessary functionality. (More should be removed, for that matter.) In particular, instead of this "semantically unworkable" code: androidNdkPath = androidNdkPath ?? PreferedAndroidNdkPath AndroidNdkPath = ValidateAndroidNdkLocation (androidNdkPath) ? androidNdkPath : AllAndroidNdks.FirstOrDefault (); which results in a priority order of: 1. `androidNdkPath` 2. `PreferedAndroidNdkPath` 3. The first `AllAndroidNdks` value, in indeterminate order, because of the not immediately obvious `.Distinct()` in `AllAndroidNdks`. we instead clean this up to be more explicit: AndroidNdkPath = GetValidNdkPath (androidNdkPath); Whereupon `GetValidNdkPath()` can have our intended order: 1. `androidNdkPath` 2. `{androidSdkPath}/ndk-bundle` 3. `PreferedAndroidNdkPath` 4. The first valid "other" location. "While we're at it," centralize error checking. We had calls to `ValidateAndroidSdkLocation()` and `ValidateAndroidNdkLocation()` strewn *everywhere*. These calls can be moved into `GetValidPath()` and `GetValidNdkPath()`, simplifying the logic of `GetAllAvailableAndroidSdks()`/etc. but also ensuring that we *can't forget* a validation call either. The result is that more code is removed than added. [0]: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinct?view=netcore-3.1
dirs.AddRange (GetAllAvailableAndroidSdks ()); | ||
allAndroidSdks = dirs.Where (d => ValidateAndroidSdkLocation (d)) | ||
.Select (d => d!) | ||
.Distinct () |
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.
@jonpryor will Distinct()
lose the ordering again? or does the ordering not matter here?
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.
Yes, it will lose the ordering again, but this is only accessed from AndroidSdkInfo.AllAndroidSdks
, which "nobody" should be using, and which always had .Distinct()
semantics, so anybody using that method shouldn't see anything bizarre.
I hope.
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.
Confirmed we're only using GetAllAvailableAndroidSdks (and also every time we use it we're using Distinct too :)
Fixes: https://github.com/xamarin/xamarin-android-tools/issues/92
Context: https://github.com/xamarin/xamarin-android-tools/pull/90#issuecomment-651375196
The PR builds for #90 encountered an "unrelated test failure"
in `AndroidSdkInfoTests.Ndk_PathInSdk()` on Windows, because Windows
is non-deterministic: the test asserts that given an Android SDK
directory `androidSdk` which contains the file
`{androidSdk}\ndk-bundle\ndk-stack.cmd`, then this:
var info = new AndroidSdkInfo (logger: null, androidSdkPath: androidSdk);
will have `info.AndroidNdkPath`==`{androidSdk}\ndk-bundle`.
Instead, this test would occasionally fail on CI:
Ndk_PathInSdk
AndroidNdkPath not found inside sdk!
Expected string length 71 but was 53. Strings differ at index 3.
Expected: "C:\Users\VssAdministrator\AppData\Local\Temp\tmpAE78.tmp\sdk\..."
But was: "C:\Program Files (x86)\Android\android-sdk\ndk-bundle"
Here, the "preferred"/system-wide NDK is being chosen over the
`{androidSdk}\ndk-bundle` directory that the unit test created.
The wrong directory was chosen for two reasons:
1. `AndroidSdkBase.Initialize()` would use `PreferedAndroidNdkPath`
when `androidNdkPath` was null, *first*, before we checked
`{androidSdk}\ndk-bundle`.
2. If `PreferedAndroidNdkPath` happened to be null, then
`AndroidSdkBase.Initialize()` would try to use
`AllAndroidNdks.FirstOrDefault()` as a default value, also before
checking `{androidSdk}\ndk-bundle`. The problem here is that the
`AllAndrdoidNdks` property uses [`Enumerable.Distinct()`][0], which
returns an *unordered* list of directories.
That the test ever worked at all is a minor miracle.
Additionally, the support for `{androidSdk}\ndk-bundle` was Windows-
specific; it didn't run on macOS.
Update `AndroidSdkInfo` so that `{androidSdk}/ndk-bundle` is supported
on macOS, and that `{androidSdk}/ndk-bundle` is *preferred* when the
`androidNdkPath` parameter is `null`, *before* checking any other
plausible default locations.
This allows the `AndroidSdkInfoTests.Ndk_PathInSdk()` test to run
everywhere, and work reliably.
[0]: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.distinct?view=netcore-3.1 |
Context: https://build.azdo.io/xamarin/public/21545
An environment change on Azure DevOps seems to cause the test failure
on Windows:
It appears that
xamarin-android-tools
is preferring anndk-bundle found on the system instead of the value passed in for
AndroidSdkPath`.To make matters worse, use of
Distinct()
returns an unorderedsequence, and so the sort ordering is unknown:
https://docs.microsoft.com/dotnet/api/system.linq.enumerable.distinct
To fix this, let's not use
ToList()
or addAndroidSdkPath
to thelist at all. We can make an explicit check for it before the
foreach
loop. Inside the loop we should skip
AndroidSdkPath
.I also added the same code for the macOS/linux side in
AndroidSdkUnix
.I updated the
Ndk_PathInSdk
test so it will run on all platforms.