Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] <ResolveAssemblies/> and ref assemblies (
Browse files Browse the repository at this point in the history
…#3053) (#3062)

Fixes: #3045

In VS 2019 16.1 Preview, a project using `Microsoft.Extensions.Http`
fails to build with:

	C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\MSBuild\Xamarin\Android\Xamarin.Android.Common.targets(2146,5): error MSB4018: The "LinkAssemblies" task failed unexpectedly.
	System.IO.FileNotFoundException: Could not load assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. Perhaps it doesn't exist in the Mono for Android profile?
	    File name: 'System.Runtime.dll'
	    at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve(AssemblyNameReference reference, ReaderParameters parameters)
	    at Java.Interop.Tools.Cecil.DirectoryAssemblyResolver.Resolve(AssemblyNameReference reference)
	    at Mono.Cecil.MetadataResolver.Resolve(TypeReference type)
	    at Mono.Cecil.ModuleDefinition.Resolve(TypeReference type)
	    at Mono.Cecil.TypeReference.Resolve()
	    at Java.Interop.Tools.Cecil.TypeDefinitionRocks.<GetTypeAndBaseTypes>d__1.MoveNext()
	    at System.Linq.Enumerable.Any[TSource](IEnumerable`1 source, Func`2 predicate)
	    at Java.Interop.Tools.Cecil.TypeDefinitionRocks.IsSubclassOf(TypeDefinition type, String typeName)
	    at MonoDroid.Tuner.FixAbstractMethodsStep.ProcessAssembly(AssemblyDefinition assembly)
	    at Mono.Linker.Steps.BaseStep.Process(LinkContext context)
	    at Mono.Linker.Pipeline.Process(LinkContext context)
	    at MonoDroid.Tuner.Linker.Process(LinkerOptions options, ILogger logger, LinkContext& context)
	    at Xamarin.Android.Tasks.LinkAssemblies.Execute(DirectoryAssemblyResolver res)
	    at Xamarin.Android.Tasks.LinkAssemblies.Execute()
	    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
	    at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext() [C:\Temp\ResolveAssembliesSystemRuntime\AndroidApp1\AndroidApp1.csproj]

The problem being that the `<ResolveAssemblies/>` MSBuild task didn't
return `System.Runtime.dll` in `@(ResolvedAssemblies)`.

What further complicated matters, was that the same project was
working with latest xamarin-android/master (16.2)? I was only able to
reproduce the problem in a test after some work to create a project
that only has a `<PackageReference/>` to `Microsoft.Extensions.Http`
with no other references *at all*. I had to do some MSBuild trickery
to remove `Java.Interop` and `System.Runtime` references.

Looking at the dependency tree in the failing test,
`System.Runtime.CompilerServices.Unsafe.dll` should be reporting a
reference to `System.Runtime.dll`. Through debugging, I saw it only
had a reference to `netstandard.dll`?

Then I realized there were two assemblies:

	~\.nuget\packages\system.runtime.compilerservices.unsafe\4.5.1\ref\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
	~\.nuget\packages\system.runtime.compilerservices.unsafe\4.5.1\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll

The "ref" assembly, references `netstandard.dll` and the "lib"
assembly (that we should be using) references `System.Runtime.dll`.

Through further debugging, I found two bugs here:

  * We shouldn't call `resolver.AddSearchDirectory()` until *after*
    we've checked if the top-level assembly is a reference assembly.
    We don't want a reference assembly in the search paths!
  * `MetadataResolver` should key its cache on the resolved path to
    the assembly.  This allows `<ResolveAssemblies/>` to open two
    *different* instances of `System.Runtime.CompilerServices.Unsafe`.
  • Loading branch information
jonathanpeppers authored and jonpryor committed May 2, 2019
1 parent f2d6d28 commit 87a80b3
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 7 deletions.
4 changes: 1 addition & 3 deletions src/Xamarin.Android.Build.Tasks/Tasks/ResolveAssemblies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,6 @@ void Execute (MetadataResolver resolver)

try {
foreach (var assembly in Assemblies) {
var assembly_path = Path.GetDirectoryName (assembly.ItemSpec);
resolver.AddSearchDirectory (assembly_path);

// Add each user assembly and all referenced assemblies (recursive)
string resolved_assembly = resolver.Resolve (assembly.ItemSpec);
if (MonoAndroidHelper.IsReferenceAssembly (resolved_assembly)) {
Expand All @@ -102,6 +99,7 @@ void Execute (MetadataResolver resolver)
}
}
topAssemblyReferences.Add (resolved_assembly);
resolver.AddSearchDirectory (Path.GetDirectoryName (resolved_assembly));
var taskItem = new TaskItem (assembly) {
ItemSpec = Path.GetFullPath (resolved_assembly),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3403,6 +3403,35 @@ public void MissingOrgApacheHttpClient ([Values ("dx", "d8")] string dexTool)
}
}

//NOTE: Referencing only Microsoft.Extensions.Http, surfaced a bug in <ResolveAssemblies/>
[Test]
public void MicrosoftExtensionsHttp ()
{
// The goal is to create a project with only this <PackageReference/>
var proj = new XamarinAndroidApplicationProject {
PackageReferences = {
KnownPackages.Microsoft_Extensions_Http,
}
};
proj.References.Clear ();
proj.Sources.Clear ();
// We have to add a custom Target to remove Java.Interop and System.Runtime
proj.Imports.Add (new Import ("foo.targets") {
TextContent = () => @"<?xml version=""1.0"" encoding=""utf-16""?>
<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
<Target Name=""_Foo"" BeforeTargets=""_ResolveAssemblies"">
<ItemGroup>
<_Remove Include=""@(_ReferencePath)"" Condition=""'%(FileName)' == 'Java.Interop' Or '%(FileName)' == 'System.Runtime'"" />
<_ReferencePath Remove=""@(_Remove)"" />
</ItemGroup>
</Target>
</Project>"
});
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
}
}

[Test]
[TestCase ("armeabi;armeabi-v7a", TestName = "XA0115")]
[TestCase ("armeabi,armeabi-v7a", TestName = "XA0115Commas")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,16 @@ public static class KnownPackages
}
},
};
public static Package Microsoft_Extensions_Http = new Package {
Id = "Microsoft.Extensions.Http",
Version = "2.2.0",
TargetFramework = "netstandard2.0",
References = {
new BuildItem.Reference ("Microsoft.Extensions.Http") {
MetadataValues = "HintPath=..\\packages\\Microsoft.Extensions.Http.2.2.0\\lib\\netstandard2.0\\Microsoft.Extensions.Http.dll"
}
},
};
}
}

7 changes: 3 additions & 4 deletions src/Xamarin.Android.Build.Tasks/Utilities/MetadataResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ public class MetadataResolver : IDisposable

public MetadataReader GetAssemblyReader (string assemblyName)
{
var key = Path.GetFileNameWithoutExtension (assemblyName);
if (!cache.TryGetValue (key, out PEReader reader)) {
var assemblyPath = Resolve (assemblyName);
cache.Add (key, reader = new PEReader (File.OpenRead (assemblyPath)));
var assemblyPath = Resolve (assemblyName);
if (!cache.TryGetValue (assemblyPath, out PEReader reader)) {
cache.Add (assemblyPath, reader = new PEReader (File.OpenRead (assemblyPath)));
}
return reader.GetMetadataReader ();
}
Expand Down

0 comments on commit 87a80b3

Please sign in to comment.