Skip to content

Commit 87a80b3

Browse files
jonathanpeppersjonpryor
authored andcommitted
[Xamarin.Android.Build.Tasks] <ResolveAssemblies/> and ref assemblies (#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`.
1 parent f2d6d28 commit 87a80b3

File tree

4 files changed

+43
-7
lines changed

4 files changed

+43
-7
lines changed

src/Xamarin.Android.Build.Tasks/Tasks/ResolveAssemblies.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,6 @@ void Execute (MetadataResolver resolver)
8787

8888
try {
8989
foreach (var assembly in Assemblies) {
90-
var assembly_path = Path.GetDirectoryName (assembly.ItemSpec);
91-
resolver.AddSearchDirectory (assembly_path);
92-
9390
// Add each user assembly and all referenced assemblies (recursive)
9491
string resolved_assembly = resolver.Resolve (assembly.ItemSpec);
9592
if (MonoAndroidHelper.IsReferenceAssembly (resolved_assembly)) {
@@ -102,6 +99,7 @@ void Execute (MetadataResolver resolver)
10299
}
103100
}
104101
topAssemblyReferences.Add (resolved_assembly);
102+
resolver.AddSearchDirectory (Path.GetDirectoryName (resolved_assembly));
105103
var taskItem = new TaskItem (assembly) {
106104
ItemSpec = Path.GetFullPath (resolved_assembly),
107105
};

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3403,6 +3403,35 @@ public void MissingOrgApacheHttpClient ([Values ("dx", "d8")] string dexTool)
34033403
}
34043404
}
34053405

3406+
//NOTE: Referencing only Microsoft.Extensions.Http, surfaced a bug in <ResolveAssemblies/>
3407+
[Test]
3408+
public void MicrosoftExtensionsHttp ()
3409+
{
3410+
// The goal is to create a project with only this <PackageReference/>
3411+
var proj = new XamarinAndroidApplicationProject {
3412+
PackageReferences = {
3413+
KnownPackages.Microsoft_Extensions_Http,
3414+
}
3415+
};
3416+
proj.References.Clear ();
3417+
proj.Sources.Clear ();
3418+
// We have to add a custom Target to remove Java.Interop and System.Runtime
3419+
proj.Imports.Add (new Import ("foo.targets") {
3420+
TextContent = () => @"<?xml version=""1.0"" encoding=""utf-16""?>
3421+
<Project ToolsVersion=""4.0"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
3422+
<Target Name=""_Foo"" BeforeTargets=""_ResolveAssemblies"">
3423+
<ItemGroup>
3424+
<_Remove Include=""@(_ReferencePath)"" Condition=""'%(FileName)' == 'Java.Interop' Or '%(FileName)' == 'System.Runtime'"" />
3425+
<_ReferencePath Remove=""@(_Remove)"" />
3426+
</ItemGroup>
3427+
</Target>
3428+
</Project>"
3429+
});
3430+
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
3431+
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
3432+
}
3433+
}
3434+
34063435
[Test]
34073436
[TestCase ("armeabi;armeabi-v7a", TestName = "XA0115")]
34083437
[TestCase ("armeabi,armeabi-v7a", TestName = "XA0115Commas")]

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Android/KnownPackages.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,16 @@ public static class KnownPackages
700700
}
701701
},
702702
};
703+
public static Package Microsoft_Extensions_Http = new Package {
704+
Id = "Microsoft.Extensions.Http",
705+
Version = "2.2.0",
706+
TargetFramework = "netstandard2.0",
707+
References = {
708+
new BuildItem.Reference ("Microsoft.Extensions.Http") {
709+
MetadataValues = "HintPath=..\\packages\\Microsoft.Extensions.Http.2.2.0\\lib\\netstandard2.0\\Microsoft.Extensions.Http.dll"
710+
}
711+
},
712+
};
703713
}
704714
}
705715

src/Xamarin.Android.Build.Tasks/Utilities/MetadataResolver.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ public class MetadataResolver : IDisposable
1616

1717
public MetadataReader GetAssemblyReader (string assemblyName)
1818
{
19-
var key = Path.GetFileNameWithoutExtension (assemblyName);
20-
if (!cache.TryGetValue (key, out PEReader reader)) {
21-
var assemblyPath = Resolve (assemblyName);
22-
cache.Add (key, reader = new PEReader (File.OpenRead (assemblyPath)));
19+
var assemblyPath = Resolve (assemblyName);
20+
if (!cache.TryGetValue (assemblyPath, out PEReader reader)) {
21+
cache.Add (assemblyPath, reader = new PEReader (File.OpenRead (assemblyPath)));
2322
}
2423
return reader.GetMetadataReader ();
2524
}

0 commit comments

Comments
 (0)