Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Better support for netstandard libraries.
Browse files Browse the repository at this point in the history
Fixes dotnet#1154, dotnet#1162

Netstandard packages sometimes ship with both reference and
implementation assemblies. The Nuget build task `ResolveNuGetPackageAssets`
only resolves the `ref` version of the assemblies. There does
not seem to be away way to FORCE Nuget to resolve the lib one.
How .net Core manages to do this is still a mistery. That said
the Nuget `ResolveNuGetPackageAssets` does give us a hint as to
how to use the `project.assets.json` file to figure out what
`lib` version of the package we should be including.

This commit reworks `ResolveAssemblies` to attempt to map the
`ref` to a `lib` if we find a Referenece Assembly. Historically
we just issue a warning (which will probably be ignored), but
now we will use the `project.assets.json` file to find the
implementation version of the `ref` assembly.

We need to be using `Nuget.ProjectModel` since it an API for
querying the `project.assets.json`. We make use of the Nuget build properties
`$(ProjectLockFile)` for the location of the `project.assets.json`
, `$(NuGetPackageRoot)` for the root folder of the Nuget packages
and `$(NuGetTargetMoniker)` for resolving which `TargetFrameworks`
we are looking for. All of these properties should be set by Nuget.
If they are not we should fallback to the default behaviour and just issue the warning.

	{
  		"version": 3,
  		"targets": {
    			"MonoAndroid,Version=v8.1": {
				"System.IO.Packaging/4.4.0": {
					"type": "package",
					"compile": {
						"ref/netstandard1.3/System.IO.Packaging.dll": {}
					},
					"runtime": {
						"lib/netstandard1.3/System.IO.Packaging.dll": {}
					}
				},
			}
		}
	}

The code above is a cut down sample of the `project.assets.json`. So our
code will first resolve the `targets`. We use `$(NuGetTargetMoniker)` to
do this. For an android project this should have a value of
`MonoAndroid,Version=v8.1`. Note we do NOT need to worry about the version
here. When Nuget restores the packages it creates the file so it will
use the correct version.
Next we try to find the `System.IO.Packaging`. We need to look at the
`lockFile.Libraries` to get information about the install path in the
Nuget folder, and then `target.Libraries` to pick out the  `runtime`
item.
Once we have resolved the path we need to then combine that with the
`$(NuGetPackageRoot)` to get the full path to the new library. If at any
point during all of this code we don't get what we expect (i.e a null) we
should abort and just issue the warning.

The only real concern with this is if the format of the `project.assets.json`
file changes. It is not ment to be edited by a human so there is the
possibiltity that the Nuget team will decide to either change the schema or
even migrate to a new file format. Hopefully we can just update the `Nuget`
nuggets if that happens.
  • Loading branch information
dellis1972 committed Mar 9, 2018
1 parent 6641a32 commit 4bddb52
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 7 deletions.
2 changes: 1 addition & 1 deletion external/Java.Interop
55 changes: 53 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Tasks/ResolveAssemblies.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
using MonoDroid.Tuner;
using System.IO;
using Xamarin.Android.Tools;
using NuGet.Common;
using NuGet.Frameworks;
using NuGet.ProjectModel;

using Java.Interop.Tools.Cecil;

Expand All @@ -24,6 +27,12 @@ public class ResolveAssemblies : Task
[Required]
public string ReferenceAssembliesDirectory { get; set; }

public string ProjectAssetFile { get; set; }

public string TargetMoniker { get; set; }

public string NuGetPackageRoot { get; set; }

public string I18nAssemblies { get; set; }
public string LinkMode { get; set; }

Expand Down Expand Up @@ -57,6 +66,9 @@ bool Execute (DirectoryAssemblyResolver resolver)
Log.LogDebugMessage (" I18nAssemblies: {0}", I18nAssemblies);
Log.LogDebugMessage (" LinkMode: {0}", LinkMode);
Log.LogDebugTaskItems (" Assemblies:", Assemblies);
Log.LogDebugMessage (" ProjectAssetFile: {0}", ProjectAssetFile);
Log.LogDebugMessage (" NuGetPackageRoot: {0}", NuGetPackageRoot);
Log.LogDebugMessage (" TargetMoniker: {0}", TargetMoniker);

foreach (var dir in ReferenceAssembliesDirectory.Split (new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
resolver.SearchDirectories.Add (dir);
Expand All @@ -65,6 +77,11 @@ bool Execute (DirectoryAssemblyResolver resolver)

var topAssemblyReferences = new List<AssemblyDefinition> ();

LockFile lockFile = null;
if (!string.IsNullOrEmpty (ProjectAssetFile) && File.Exists (ProjectAssetFile)) {
lockFile = LockFileUtilities.GetLockFile (ProjectAssetFile, NullLogger.Instance);
}

try {
foreach (var assembly in Assemblies) {
var assembly_path = Path.GetDirectoryName (assembly.ItemSpec);
Expand All @@ -77,8 +94,13 @@ bool Execute (DirectoryAssemblyResolver resolver)
if (assemblyDef == null)
throw new InvalidOperationException ("Failed to load assembly " + assembly.ItemSpec);
if (MonoAndroidHelper.IsReferenceAssembly (assemblyDef)) {
Log.LogWarning ($"Ignoring {assembly_path} as it is a Reference Assembly");
continue;
// Resolve "runtime" library
if (lockFile != null)
assemblyDef = ResolveRuntimeAssemblyForReferenceAssembly (lockFile, resolver, assemblyDef.Name);
if (lockFile == null || assemblyDef == null) {
Log.LogWarning ($"Ignoring {assembly_path} as it is a Reference Assembly");
continue;
}
}
topAssemblyReferences.Add (assemblyDef);
assemblies.Add (Path.GetFullPath (assemblyDef.MainModule.FullyQualifiedName));
Expand Down Expand Up @@ -120,6 +142,35 @@ bool Execute (DirectoryAssemblyResolver resolver)
readonly List<string> do_not_package_atts = new List<string> ();
int indent = 2;

AssemblyDefinition ResolveRuntimeAssemblyForReferenceAssembly (LockFile lockFile, DirectoryAssemblyResolver resolver, AssemblyNameDefinition assemblyNameDefinition)
{
if (string.IsNullOrEmpty(TargetMoniker) || string.IsNullOrEmpty (NuGetPackageRoot) || !Directory.Exists (NuGetPackageRoot))
return null;

var framework = NuGetFramework.Parse (TargetMoniker);
if (framework == null) {
Log.LogWarning ($"Could not parse '{TargetMoniker}'");
return null;
}
var target = lockFile.GetTarget (framework, string.Empty);
if (target == null) {
Log.LogWarning ($"Could not resolve target for '{TargetMoniker}'");
return null;
}
var libraryPath = lockFile.Libraries.FirstOrDefault (x => x.Name == assemblyNameDefinition.Name);
if (libraryPath == null)
return null;
var library = target.Libraries.FirstOrDefault (x => x.Name == assemblyNameDefinition.Name);
if (library == null)
return null;
var runtime = library.RuntimeAssemblies.FirstOrDefault ();
if (runtime == null)
return null;
var path = Path.Combine (NuGetPackageRoot, libraryPath.Path, runtime.Path);
Log.LogDebugMessage ($"Attempting to load {path}");
return resolver.Load (path, forceLoad: true);
}

void AddAssemblyReferences (DirectoryAssemblyResolver resolver, ICollection<string> assemblies, AssemblyDefinition assembly, bool topLevel)
{
var fqname = assembly.MainModule.FullyQualifiedName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,14 @@ public partial class App : Application
public App()
{
JsonConvert.DeserializeObject<string>(""test"");
package = Package.Open ("""");
try {
JsonConvert.DeserializeObject<string>(""test"");
package = Package.Open ("""");
} catch {
}
InitializeComponent();
MainPage = new ContentPage ();
}
protected override void OnStart()
Expand Down Expand Up @@ -320,6 +325,7 @@ protected override void OnResume()
IsRelease = true,
UseLatestPlatformSdk = true,
References = {
new BuildItem.Reference ("Java.Interop.Export"),
new BuildItem.Reference ("Mono.Android.Export"),
new BuildItem.ProjectReference ($"..\\{netStandardProject.ProjectName}\\{netStandardProject.ProjectName}.csproj",
netStandardProject.ProjectName, netStandardProject.ProjectGuid),
Expand All @@ -337,6 +343,29 @@ protected override void OnResume()
KnownPackages.XamarinForms_2_3_4_231,
}
};
app.MainActivity = @"using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using XamFormsSample;
namespace App1
{
[Activity (Label = ""App1"", MainLauncher = true, Icon = ""@drawable/icon"")]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity {
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
global::Xamarin.Forms.Forms.Init (this, bundle);
LoadApplication (new App ());
}
}
}";
app.SetProperty (KnownProperties.AndroidSupportedAbis, "x86;armeabi-v7a");
var expectedFiles = new string [] {
"Java.Interop.dll",
Expand All @@ -346,6 +375,7 @@ protected override void OnResume()
"System.dll",
"System.Runtime.Serialization.dll",
"System.IO.Packaging.dll",
"System.IO.Compression.dll",
"Mono.Android.Export.dll",
"App1.dll",
"FormsViewGroup.dll",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<OutputType>Library</OutputType>
<RootNamespace>Xamarin.Android.Build.Tests</RootNamespace>
<AssemblyName>Xamarin.Android.Build.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
</PropertyGroup>
<Import Project="Xamarin.Android.Build.Tests.Shared.projitems" Label="Shared" Condition="Exists('Xamarin.Android.Build.Tests.Shared.projitems')" />
<Import Project="..\..\..\..\Configuration.props" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<RootNamespace>Xamarin.Android.Tasks</RootNamespace>
<AssemblyName>Xamarin.Android.Build.Tasks</AssemblyName>
<FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="..\..\Configuration.props" />
Expand Down Expand Up @@ -48,6 +48,53 @@
<Reference Include="FSharp.Compiler.CodeDom">
<HintPath>..\..\packages\FSharp.Compiler.CodeDom.1.0.0.1\lib\net40\FSharp.Compiler.CodeDom.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NuGet.Frameworks">
<HintPath>..\..\packages\NuGet.Frameworks.4.6.0\lib\net46\NuGet.Frameworks.dll</HintPath>
</Reference>
<Reference Include="NuGet.Common">
<HintPath>..\..\packages\NuGet.Common.4.6.0\lib\net46\NuGet.Common.dll</HintPath>
</Reference>
<Reference Include="System.IO.Compression" />
<Reference Include="NuGet.Configuration">
<HintPath>..\..\packages\NuGet.Configuration.4.6.0\lib\net46\NuGet.Configuration.dll</HintPath>
</Reference>
<Reference Include="System.Security" />
<Reference Include="NuGet.Versioning">
<HintPath>..\..\packages\NuGet.Versioning.4.6.0\lib\net46\NuGet.Versioning.dll</HintPath>
</Reference>
<Reference Include="NuGet.LibraryModel">
<HintPath>..\..\packages\NuGet.LibraryModel.4.6.0\lib\net46\NuGet.LibraryModel.dll</HintPath>
</Reference>
<Reference Include="NuGet.Packaging.Core">
<HintPath>..\..\packages\NuGet.Packaging.Core.4.6.0\lib\net46\NuGet.Packaging.Core.dll</HintPath>
</Reference>
<Reference Include="NuGet.Packaging">
<HintPath>..\..\packages\NuGet.Packaging.4.6.0\lib\net46\NuGet.Packaging.dll</HintPath>
</Reference>
<Reference Include="System.Runtime">
<HintPath>..\..\packages\System.Runtime.4.3.0\lib\net462\System.Runtime.dll</HintPath>
</Reference>
<Reference Include="mscorlib" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Runtime.InteropServices">
<HintPath>..\..\packages\System.Runtime.InteropServices.4.3.0\lib\net462\System.Runtime.InteropServices.dll</HintPath>
</Reference>
<Reference Include="NuGet.Protocol">
<HintPath>..\..\packages\NuGet.Protocol.4.6.0\lib\net46\NuGet.Protocol.dll</HintPath>
</Reference>
<Reference Include="System.IdentityModel" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.ServiceModel" />
<Reference Include="NuGet.DependencyResolver.Core">
<HintPath>..\..\packages\NuGet.DependencyResolver.Core.4.6.0\lib\net46\NuGet.DependencyResolver.Core.dll</HintPath>
</Reference>
<Reference Include="NuGet.ProjectModel">
<HintPath>..\..\packages\NuGet.ProjectModel.4.6.0\lib\net46\NuGet.ProjectModel.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)Profile.g.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,9 @@ because xbuild doesn't support framework reference assemblies.
Assemblies="@(FilteredAssemblies)"
I18nAssemblies="$(MandroidI18n)"
LinkMode="$(AndroidLinkMode)"
ProjectAssetFile="$(ProjectLockFile)"
NuGetPackageRoot="$(NuGetPackageRoot)"
TargetMoniker="$(NuGetTargetMoniker)"
ReferenceAssembliesDirectory="$(TargetFrameworkDirectory)">
<Output TaskParameter="ResolvedAssemblies" ItemName="ResolvedAssemblies" />
<Output TaskParameter="ResolvedUserAssemblies" ItemName="ResolvedUserAssemblies" />
Expand Down
13 changes: 13 additions & 0 deletions src/Xamarin.Android.Build.Tasks/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,17 @@
<packages>
<package id="FSharp.Compiler.CodeDom" version="1.0.0.1" targetFramework="net45" />
<package id="Irony" version="0.9.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="11.0.1" targetFramework="net451" />
<package id="NuGet.Common" version="4.6.0" targetFramework="net462" />
<package id="NuGet.Configuration" version="4.6.0" targetFramework="net462" />
<package id="NuGet.DependencyResolver.Core" version="4.6.0" targetFramework="net462" />
<package id="NuGet.Frameworks" version="4.6.0" targetFramework="net462" />
<package id="NuGet.LibraryModel" version="4.6.0" targetFramework="net462" />
<package id="NuGet.Packaging" version="4.6.0" targetFramework="net462" />
<package id="NuGet.Packaging.Core" version="4.6.0" targetFramework="net462" />
<package id="NuGet.ProjectModel" version="4.6.0" targetFramework="net462" />
<package id="NuGet.Protocol" version="4.6.0" targetFramework="net462" />
<package id="NuGet.Versioning" version="4.6.0" targetFramework="net462" />
<package id="System.Runtime" version="4.3.0" targetFramework="net462" />
<package id="System.Runtime.InteropServices" version="4.3.0" targetFramework="net462" />
</packages>

0 comments on commit 4bddb52

Please sign in to comment.