Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] optimize $(AssemblySearchPaths) (#2223)
Browse files Browse the repository at this point in the history
Context: https://docs.microsoft.com/en-us/visualstudio/msbuild/resolveassemblyreference-task?view=vs-2017
Context: https://twitter.com/KirillOsenkov/status/1043725067081764865

@KirillOsenkov added a new analyzer to the Windows binlog viewer,
noting `$(AssemblySearchPaths)` that are unused during a build.

Looking at what we are using currently, it reports:

	Unused AssemblySearchPaths locations
	    {AssemblyFolders}
	    {AssemblyFoldersFromConfig:C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\AssemblyFolders.config,v9.0}
	    {CandidateAssemblyFiles}
	    {Registry:Software\Microsoft\MonoAndroid,v9.0,AssemblyFoldersEx}
	    bin\Debug\

Luckily, we already had MSBuild logic in place to remove `{GAC}` from
this list.

But we certainly are probing search paths that are nonsensical for
Xamarin.Android:

  - `{AssemblyFolders}`: Specifies the task will use the Visual
    Studio.NET 2003 finding-assemblies-from-registry scheme.
  - `{AssemblyFoldersFromConfig:*}`: this probes the system-wide
    config file in the MSBuild / Visual Studio install
  - `{CandidateAssemblyFiles}`: The `CandidateAssemblyFiles` property on
    `ResolveAssemblyReferences` is blank. Docs say: Assemblies in this
    list will be considered when the SearchPaths parameter contains
    `{CandidateAssemblyFiles}` as one of the paths to consider.
  - `{Registry:*}`: probe the Windows registry...
  - `bin\Debug` or `$(OutputPath)`: not needed, and a potential cause of
    confusion. This would be if there was an arbitrary extra assembly
    sitting in `$(OutputPath)`.

And if we look further, we see odd things:

	Primary reference "System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e".
	    Resolved file path is "C:\Users\myuser\Desktop\Git\xamarin-android\bin\Control\lib\xamarin.android\xbuild-frameworks\MonoAndroid\v1.0\System.dll".
	    Reference found at search path location "{TargetFrameworkDirectory}".
	    For SearchPath "{TargetFrameworkDirectory}".
	        Considered "C:\Users\myuser\Desktop\Git\xamarin-android\bin\Control\lib\xamarin.android\xbuild-frameworks\MonoAndroid\v9.0\System.winmd", but it didn't exist.
	        Considered "C:\Users\myuser\Desktop\Git\xamarin-android\bin\Control\lib\xamarin.android\xbuild-frameworks\MonoAndroid\v9.0\System.dll", but it didn't exist.
	        Considered "C:\Users\myuser\Desktop\Git\xamarin-android\bin\Control\lib\xamarin.android\xbuild-frameworks\MonoAndroid\v9.0\System.exe", but it didn't exist.
	        Considered "C:\Users\myuser\Desktop\Git\xamarin-android\bin\Control\lib\xamarin.android\xbuild-frameworks\MonoAndroid\v1.0\System.winmd", but it didn't exist.

Why are we looking for `.winmd` files? This is a UWP thing...

And then apparently, our build also will look for `.pri` files?
Another UWP thing...

	AllowedRelatedFileExtensions
	    .pdb
	    .xml
	    .pri
	    .dll.config
	    .exe.config
	    .dll.mdb
	    .exe.mdb

So the first fix is to trim `$(AssemblySearchPaths)` to precisely
what Xamarin.Android needs:

	<AssemblySearchPaths>
	    {HintPathFromItem};
	    {TargetFrameworkDirectory};
	    {RawFileName};
	</AssemblySearchPaths>

We shouldn't be concerned about other system-wide places.

Next, we should only be looking at these file extensions:

	<AllowedReferenceAssemblyFileExtensions>
	    .dll;
	</AllowedReferenceAssemblyFileExtensions>
	<AllowedReferenceRelatedFileExtensions>
	    .pdb;
	    .xml;
	    .dll.config;
	    .dll.mdb;
	</AllowedReferenceRelatedFileExtensions>

Removing `.winmd` from `$(AllowedReferenceAssemblyFileExtensions)`
and `.pri` from `$(AllowedReferenceRelatedFileExtensions)`.  We can
also ignore `.exe*` files, since Xamarin.Android projects only use
`.dll` files.

This makes the log probing for `System.dll` much cleaner:

	Primary reference "System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e".
	    Resolved file path is "C:\Users\myuser\Desktop\Git\xamarin-android\bin\Release\lib\xamarin.android\xbuild-frameworks\MonoAndroid\v1.0\System.dll".
	    Reference found at search path location "{TargetFrameworkDirectory}".
	    For SearchPath "{TargetFrameworkDirectory}".
	        Considered "C:\Users\myuser\Desktop\Git\xamarin-android\bin\Release\lib\xamarin.android\xbuild-frameworks\MonoAndroid\v9.0\System.dll", but it didn't exist.

Here is the log of changes made to these properties here at MSBuild
evaluation time:

	Property reassignment: $(AssemblySearchPaths)="
	        {HintPathFromItem};
	        {TargetFrameworkDirectory};
	        {RawFileName};
	    " (previous value: "
	    {CandidateAssemblyFiles};
	    ;
	    {HintPathFromItem};
	    {TargetFrameworkDirectory};
	    {AssemblyFoldersFromConfig:C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\AssemblyFolders.config,v9.0};
	    {Registry:Software\Microsoft\MonoAndroid,v9.0,AssemblyFoldersEx};
	    {AssemblyFolders};
	    {GAC};
	    {RawFileName};
	    bin\Debug\
	    ") at xamarin-android\bin\Release\lib\xamarin.android\xbuild\Xamarin\Android\Xamarin.Android.Common.targets (338,2)

	Property reassignment: $(AllowedReferenceAssemblyFileExtensions)="
	        .dll;
	    " (previous value: "
	    .winmd;
	    .dll;
	    .exe
	    ") at xamarin-android\bin\Release\lib\xamarin.android\xbuild\Xamarin\Android\Xamarin.Android.Common.targets (343,2)

	Property reassignment: $(AllowedReferenceRelatedFileExtensions)="
	        .pdb;
	        .xml;
	        .dll.config;
	        .dll.mdb;
	    " (previous value: "
	    .pdb;
	    .xml;
	    .pri;
	    .dll.config;
	    .exe.config
	    ") at xamarin-android\bin\Release\lib\xamarin.android\xbuild\Xamarin\Android\Xamarin.Android.Common.targets (347,2)

~~ Results ~~

I timed the Xamarin.Forms Control Gallery project, since I felt this
change would improve larger projects the most.

Project here:
https://github.com/jonathanpeppers/Xamarin.Forms/tree/msbuild-timing

Before:

	1106 ms  ResolveAssemblies                          2 calls
	1401 ms  ResolveAssemblyReference                  18 calls

After:

	 909 ms  ResolveAssemblies                          2 calls
	1088 ms  ResolveAssemblyReference                  18 calls

This seems to directly save ~500ms to these targets in incremental
builds for this project.  This project references ~7 other
Xamarin.Android projects.

~~ Upstream ~~

On MacOS, I also see probing for `.winmd` files!

	Considered "xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v9.0/System.winmd", but it didn't exist.

Perhaps we need some upstream changes to MSBuild as well...
  • Loading branch information
jonathanpeppers authored and jonpryor committed Sep 26, 2018
1 parent 73b93c2 commit e390702
Showing 1 changed file with 14 additions and 2 deletions.
16 changes: 14 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,20 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.

<!-- Do not resolve from the GAC under any circumstances in Mobile -->
<PropertyGroup>
<AssemblySearchPaths>$([System.String]::Copy('$(AssemblySearchPaths)').Replace('{GAC}',''))</AssemblySearchPaths>
<AssemblySearchPaths Condition="'$(MSBuildRuntimeVersion)' != ''">$(AssemblySearchPaths.Split(';'))</AssemblySearchPaths>
<AssemblySearchPaths>
{HintPathFromItem};
{TargetFrameworkDirectory};
{RawFileName};
</AssemblySearchPaths>
<AllowedReferenceAssemblyFileExtensions>
.dll;
</AllowedReferenceAssemblyFileExtensions>
<AllowedReferenceRelatedFileExtensions>
.pdb;
.xml;
.dll.config;
.dll.mdb;
</AllowedReferenceRelatedFileExtensions>
</PropertyGroup>

<Target Name="_SeparateAppExtensionReferences">
Expand Down

0 comments on commit e390702

Please sign in to comment.