-
Notifications
You must be signed in to change notification settings - Fork 545
[Xamarin.Android.Build.Tasks] Better support for netstandard libraries. #1356
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
Changes from all commits
536120c
df6c979
098d861
4451917
5a93b85
26c3701
b66647a
b8ee9c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,12 +10,15 @@ | |
using MonoDroid.Tuner; | ||
using System.IO; | ||
using Xamarin.Android.Tools; | ||
using NuGet.Common; | ||
using NuGet.Frameworks; | ||
using NuGet.ProjectModel; | ||
|
||
using Java.Interop.Tools.Cecil; | ||
|
||
namespace Xamarin.Android.Tasks | ||
{ | ||
public class ResolveAssemblies : Task | ||
public class ResolveAssemblies : AsyncTask | ||
{ | ||
// The user's assemblies to package | ||
[Required] | ||
|
@@ -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; } | ||
|
||
|
@@ -45,25 +54,45 @@ public class ResolveAssemblies : Task | |
|
||
public override bool Execute () | ||
{ | ||
using (var resolver = new DirectoryAssemblyResolver (this.CreateTaskLogger (), loadDebugSymbols: false)) { | ||
return Execute (resolver); | ||
} | ||
System.Threading.Tasks.Task.Run (() => { | ||
using (var resolver = new DirectoryAssemblyResolver (this.CreateTaskLogger (), loadDebugSymbols: false)) { | ||
return Execute (resolver); | ||
} | ||
}, Token).ContinueWith ((t) => { | ||
if (t.Exception != null) { | ||
var ex = t.Exception.GetBaseException (); | ||
LogError (ex.Message + Environment.NewLine + ex.StackTrace); | ||
} | ||
Complete (); | ||
}); | ||
return base.Execute (); | ||
} | ||
|
||
bool Execute (DirectoryAssemblyResolver resolver) | ||
{ | ||
Log.LogDebugMessage ("ResolveAssemblies Task"); | ||
Log.LogDebugMessage (" ReferenceAssembliesDirectory: {0}", ReferenceAssembliesDirectory); | ||
Log.LogDebugMessage (" I18nAssemblies: {0}", I18nAssemblies); | ||
Log.LogDebugMessage (" LinkMode: {0}", LinkMode); | ||
Log.LogDebugTaskItems (" Assemblies:", Assemblies); | ||
LogDebugMessage ("ResolveAssemblies Task"); | ||
LogDebugMessage (" ReferenceAssembliesDirectory: {0}", ReferenceAssembliesDirectory); | ||
LogDebugMessage (" I18nAssemblies: {0}", I18nAssemblies); | ||
LogDebugMessage (" LinkMode: {0}", LinkMode); | ||
LogDebugTaskItems (" Assemblies:", Assemblies); | ||
LogDebugMessage (" ProjectAssetFile: {0}", ProjectAssetFile); | ||
LogDebugMessage (" NuGetPackageRoot: {0}", NuGetPackageRoot); | ||
LogDebugMessage (" TargetMoniker: {0}", TargetMoniker); | ||
|
||
foreach (var dir in ReferenceAssembliesDirectory.Split (new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) | ||
resolver.SearchDirectories.Add (dir); | ||
|
||
var assemblies = new HashSet<string> (); | ||
|
||
var topAssemblyReferences = new List<AssemblyDefinition> (); | ||
var logger = new NuGetLogger((s) => { | ||
LogDebugMessage ("{0}", s); | ||
}); | ||
|
||
LockFile lockFile = null; | ||
if (!string.IsNullOrEmpty (ProjectAssetFile) && File.Exists (ProjectAssetFile)) { | ||
lockFile = LockFileUtilities.GetLockFile (ProjectAssetFile, logger); | ||
} | ||
|
||
try { | ||
foreach (var assembly in Assemblies) { | ||
|
@@ -77,21 +106,26 @@ 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) { | ||
LogWarning ($"Ignoring {assembly_path} as it is a Reference Assembly"); | ||
continue; | ||
} | ||
} | ||
topAssemblyReferences.Add (assemblyDef); | ||
assemblies.Add (Path.GetFullPath (assemblyDef.MainModule.FullyQualifiedName)); | ||
} | ||
} catch (Exception ex) { | ||
Log.LogError ("Exception while loading assemblies: {0}", ex); | ||
LogError ("Exception while loading assemblies: {0}", ex); | ||
return false; | ||
} | ||
try { | ||
foreach (var assembly in topAssemblyReferences) | ||
AddAssemblyReferences (resolver, assemblies, assembly, true); | ||
} catch (Exception ex) { | ||
Log.LogError ("Exception while loading assemblies: {0}", ex); | ||
LogError ("Exception while loading assemblies: {0}", ex); | ||
return false; | ||
} | ||
|
||
|
@@ -109,17 +143,46 @@ bool Execute (DirectoryAssemblyResolver resolver) | |
ResolvedUserAssemblies = ResolvedAssemblies.Where (p => !MonoAndroidHelper.IsFrameworkAssembly (p.ItemSpec, true)).ToArray (); | ||
ResolvedDoNotPackageAttributes = do_not_package_atts.ToArray (); | ||
|
||
Log.LogDebugTaskItems (" [Output] ResolvedAssemblies:", ResolvedAssemblies); | ||
Log.LogDebugTaskItems (" [Output] ResolvedUserAssemblies:", ResolvedUserAssemblies); | ||
Log.LogDebugTaskItems (" [Output] ResolvedFrameworkAssemblies:", ResolvedFrameworkAssemblies); | ||
Log.LogDebugTaskItems (" [Output] ResolvedDoNotPackageAttributes:", ResolvedDoNotPackageAttributes); | ||
LogDebugTaskItems (" [Output] ResolvedAssemblies:", ResolvedAssemblies); | ||
LogDebugTaskItems (" [Output] ResolvedUserAssemblies:", ResolvedUserAssemblies); | ||
LogDebugTaskItems (" [Output] ResolvedFrameworkAssemblies:", ResolvedFrameworkAssemblies); | ||
LogDebugTaskItems (" [Output] ResolvedDoNotPackageAttributes:", ResolvedDoNotPackageAttributes); | ||
|
||
return !Log.HasLoggedErrors; | ||
} | ||
|
||
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) { | ||
LogWarning ($"Could not parse '{TargetMoniker}'"); | ||
return null; | ||
} | ||
var target = lockFile.GetTarget (framework, string.Empty); | ||
if (target == null) { | ||
LogWarning ($"Could not resolve target for '{TargetMoniker}'"); | ||
return null; | ||
} | ||
var libraryPath = lockFile.Libraries.FirstOrDefault (x => x.Name == assemblyNameDefinition.Name); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Library's name property is the package id - is it guaranteed that AssemblyNameDefninition.Name will have a matching package id? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rohit21agrawal that is a good point. I might have to rework this a bit then :( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about emitting AssemblyMetadataAttribute with the PackageId from msbuild, using something like https://mobile.twitter.com/kzu/status/977257467917819905 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I reworked the code in #1459 :) |
||
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); | ||
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; | ||
|
@@ -132,11 +195,11 @@ void AddAssemblyReferences (DirectoryAssemblyResolver resolver, ICollection<stri | |
foreach (var att in assembly.CustomAttributes.Where (a => a.AttributeType.FullName == "Java.Interop.DoNotPackageAttribute")) { | ||
string file = (string) att.ConstructorArguments.First ().Value; | ||
if (string.IsNullOrWhiteSpace (file)) | ||
Log.LogError ("In referenced assembly {0}, Java.Interop.DoNotPackageAttribute requires non-null file name.", assembly.FullName); | ||
LogError ("In referenced assembly {0}, Java.Interop.DoNotPackageAttribute requires non-null file name.", assembly.FullName); | ||
do_not_package_atts.Add (Path.GetFileName (file)); | ||
} | ||
|
||
Log.LogMessage (MessageImportance.Low, "{0}Adding assembly reference for {1}, recursively...", new string (' ', indent), assembly.Name); | ||
LogMessage ("{0}Adding assembly reference for {1}, recursively...", new string (' ', indent), assembly.Name); | ||
indent += 2; | ||
// Add this assembly | ||
if (!topLevel && assemblies.All (a => new AssemblyNameDefinition (a, null).Name != assembly.Name.Name)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using System; | ||
using NuGet.Common; | ||
using Microsoft.Build.Utilities; | ||
using TPL = System.Threading.Tasks; | ||
|
||
namespace Xamarin.Android.Tasks { | ||
|
||
class NuGetLogger : LoggerBase { | ||
Action<string> log; | ||
|
||
public NuGetLogger (Action<string> log) | ||
{ | ||
this.log = log; | ||
} | ||
|
||
public override void Log (ILogMessage message) { | ||
log (message.Message); | ||
} | ||
|
||
public override void Log (LogLevel level, string data) { | ||
log (data); | ||
} | ||
|
||
public override TPL.Task LogAsync (ILogMessage message) { | ||
Log (message); | ||
return TPL.Task.FromResult(0); | ||
} | ||
|
||
public override TPL.Task LogAsync (LogLevel level, string data) { | ||
Log (level, data); | ||
return TPL.Task.FromResult(0); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" /> | ||
|
@@ -51,6 +51,53 @@ | |
<Reference Include="FSharp.Core"> | ||
<HintPath>..\..\packages\FSharp.Core.3.1.2.5\lib\net40\FSharp.Core.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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These packages are what need to be added to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. Don't think we need to add |
||
</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" /> | ||
|
@@ -507,6 +554,7 @@ | |
<Compile Include="Utilities\SatelliteAssembly.cs" /> | ||
<Compile Include="Tasks\AndroidApkSigner.cs" /> | ||
<Compile Include="Tasks\Desugar.cs" /> | ||
<Compile Include="Utilities\NuGetLogger.cs" /> | ||
<None Include="Resources\desugar_deploy.jar"> | ||
<Link>desugar_deploy.jar</Link> | ||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming this method was cribbed from .NET Core? If that's the case, we should likewise mention that in
src/Xamarin.Android.Build.Tasks.tpnitems
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nope, it was written by me. I referenced the Nuget source code to see how the API Nuget.ProjectModel was used but none of this code was copied from .Net Core or Nuget.