Skip to content
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

[Xamarin.Android.Build.Tasks] <GenerateJavaStubs/> improvements #2956

Merged

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Apr 10, 2019

I went through the _GenerateJavaStubs MSBuild target and the
<GenerateJavaStubs/> MSBuild task. There was an "easy" win to
improve incremental build performance. I also made a few other general
improvements to <GenerateJavaStubs/>.

Inputs & Outputs

_GenerateJavaStubs has Inputs of:

Inputs="$(MSBuildAllProjects);@(_ResolvedAssemblies);$(_AndroidManifestAbs);$(_AndroidBuildPropertiesCache);@(_AndroidResourceDest)"

Lets say a NetStandard assembly changes, such as a XAML change in a
Xamarin.Forms app. In this case _GenerateJavaStubs does not actually
need to run again.

We can use @(_ResolvedUserMonoAndroidAssemblies) as an input instead
of @(_ResolvedAssemblies).

This is the biggest win--to skip this target completely.

selectedWhitelistAssemblies

ManifestDocument had the concept of a "whitelist" assembly, which
currently consisted of a single assembly:

internal static readonly string [] FrameworkAttributeLookupTargets = {"Mono.Android.GoogleMaps.dll"};

Mono.Android.GoogleMaps.dll no longer exists, so I was able to
remove a bit of code that still used that dead codepath.

Typemaps

Just a couple minor improvements here:

  • Fixed the usage of the [Obsolete] constructor for
    TypeNameMapGenerator. Using a (TaskLevel level, string value)
    callback instead.
  • I reused the MemoryStream and got rid of the UpdateWhenChanged
    method.

Tests

I updated the IncrementalBuildTest.ProduceReferenceAssembly test
quite a bit:

  • The library project using $(ProduceReferenceAssembly) is now a
    NetStandard library.
  • I check all the targets that should be skipped from now on.

I made a few changes in Xamarin.ProjectTools to clean things up:

  • The DotNetStandard class should just define
    Language=XamarinAndroidProjectLanguage.CSharp by default.
  • Added a ShouldRestorePackageReferences property for deciding if we
    pass /restore or not.
  • The DotNetStandard class always needs to use /restore, even if
    there are no PackageReferences.
  • I moved a couple methods from DotNetXamarinProject to
    XamarinProject so the DotNetStandard has them.

Results

Testing the Xamarin.Forms project in this repo. A fresh build I wasn't
able to see a noticeable improvement.

However, an incremental build with XAML-change:

Before:
  861 ms  _GenerateJavaStubs                         1 calls
After:
  n/a

The target is skipped now:

_GenerateJavaStubs:
Skipping target "_GenerateJavaStubs" because all output files are up-to-date with respect to the input files.

Overall this seems to be ~800ms better for incremental builds with
XAML changes.

@jonpryor
Copy link
Member

It doesn't immediately make sense, but this change seems to have broken ~every on-device .apk test: https://jenkins.mono-project.com/job/xamarin-android-pr-pipeline-release/518/testReport/ e.g. https://jenkins.mono-project.com/job/xamarin-android-pr-pipeline-release/518/testReport/Xamarin.Android/Locale_Tests/Possible_Crash___Release/

Unhandled Exception:
System.ArgumentException: Handle must be valid.
Parameter name: instance
  at Java.Interop.JniEnvironment+InstanceMethods.CallObjectMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method)
  at Android.Runtime.JNIEnv.CallObjectMethod (System.IntPtr jobject, System.IntPtr jmethod)
  at Java.Interop.TypeManager.GetClassName (System.IntPtr class_ptr)
  at Java.Interop.TypeManager.CreateInstance (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type targetType)
  at Java.Lang.Object.GetObject (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer, System.Type type)
  at Java.Lang.Object._GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer)
  at Java.Lang.Object.GetObject[T] (System.IntPtr handle, Android.Runtime.JniHandleOwnership transfer)
  at Java.Lang.Thread.get_DefaultUncaughtExceptionHandler ()
  at Android.Runtime.JNIEnv.Initialize (Android.Runtime.JnienvInitializeArgs* args)

Looks like the typemap.* files are missing a type?

@jonathanpeppers jonathanpeppers added the do-not-merge PR should not be merged. label Apr 11, 2019
@jonathanpeppers jonathanpeppers removed the do-not-merge PR should not be merged. label Apr 12, 2019
I went through the `_GenerateJavaStubs` MSBuild target and the
`<GenerateJavaStubs/>` MSBuild task. There was an "easy" win to
improve incremental build performance. I also made a few other general
improvements to `<GenerateJavaStubs/>`.

~~ Inputs & Outputs ~~

`_GenerateJavaStubs` has `Inputs` of:

    Inputs="$(MSBuildAllProjects);@(_ResolvedAssemblies);$(_AndroidManifestAbs);$(_AndroidBuildPropertiesCache);@(_AndroidResourceDest)"

Lets say a NetStandard assembly changes, such as a XAML change in a
Xamarin.Forms app. In this case `_GenerateJavaStubs` does not actually
need to run again.

We can use `@(_ResolvedUserMonoAndroidAssemblies)` as an input instead
of `@(_ResolvedAssemblies)`.

This is the biggest win--to skip this target completely.

~~ selectedWhitelistAssemblies ~~

`ManifestDocument` had the concept of a "whitelist" assembly, which
currently consisted of a single assembly:

    internal static readonly string [] FrameworkAttributeLookupTargets = {"Mono.Android.GoogleMaps.dll"};

`Mono.Android.GoogleMaps.dll` no longer exists, so I was able to
remove a bit of code that still used that dead codepath.

~~ Typemaps ~~

Just a couple minor improvements here:

* Fixed the usage of the `[Obsolete]` constructor for
  `TypeNameMapGenerator`. Using a `(TaskLevel level, string value)`
  callback instead.
* I reused the `MemoryStream` and got rid of the `UpdateWhenChanged`
  method.

~~ Tests ~~

I updated the `IncrementalBuildTest.ProduceReferenceAssembly` test
quite a bit:

* The library project using `$(ProduceReferenceAssembly)` is now a
  NetStandard library.
* I check all the targets that should be skipped from now on.

I made a few changes in `Xamarin.ProjectTools` to clean things up:

* The `DotNetStandard` class should just define
  `Language=XamarinAndroidProjectLanguage.CSharp` by default.
* Added a `ShouldRestorePackageReferences` property for deciding if we
  pass `/restore` or not.
* The `DotNetStandard` class always needs to use `/restore`, even if
  there are no `PackageReferences`.
* I moved a couple methods from `DotNetXamarinProject` to
  `XamarinProject` so the `DotNetStandard` has them.

~~ Results ~~

Testing the Xamarin.Forms project in this repo. A fresh build I wasn't
able to see a noticeable improvement.

*However*, an incremental build with XAML-change:

    Before:
      861 ms  _GenerateJavaStubs                         1 calls
    After:
      n/a

The target is skipped now:

    _GenerateJavaStubs:
    Skipping target "_GenerateJavaStubs" because all output files are up-to-date with respect to the input files.

Overall this seems to be ~800ms better for incremental builds with
XAML changes.
@jonathanpeppers
Copy link
Member Author

I think the macOS failures are OK:

Test Result (2 failures / +1)
JnienvTest.Mono.Android_Tests, Java.InteropTests.JnienvTest.DoNotLeakWeakReferences / 
ReleaseXamarin.Android.Bcl_Tests.nunit.Possible Crash / Release

It seems like the "adb crashed" thing happened.

@dellis1972 dellis1972 merged commit 9b99cce into dotnet:master Apr 16, 2019
@github-actions github-actions bot locked and limited conversation to collaborators Jan 31, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants