2016-04-21 15:57:02 +03:00
using System ;
using System.IO ;
using System.Collections.Generic ;
using System.Xml.XPath ;
using Mono.Linker ;
using Mono.Linker.Steps ;
using Mono.Tuner ;
using MonoMac.Tuner ;
using MonoTouch.Tuner ;
using Xamarin.Bundler ;
using Xamarin.Linker ;
using Xamarin.Linker.Steps ;
2017-01-18 11:22:19 +03:00
using Xamarin.Tuner ;
2016-04-21 15:57:02 +03:00
using Xamarin.Utils ;
using Mono.Cecil ;
using Mono.Cecil.Mdb ;
namespace MonoMac.Tuner {
public class LinkerOptions {
public AssemblyDefinition MainAssembly { get ; set ; }
public string OutputDirectory { get ; set ; }
public bool LinkSymbols { get ; set ; }
public LinkMode LinkMode { get ; set ; }
public AssemblyResolver Resolver { get ; set ; }
public IEnumerable < string > SkippedAssemblies { get ; set ; }
public I18nAssemblies I18nAssemblies { get ; set ; }
public bool EnsureUIThread { get ; set ; }
public IList < string > ExtraDefinitions { get ; set ; }
public TargetFramework TargetFramework { get ; set ; }
public string Architecture { get ; set ; }
2016-05-11 14:27:51 +03:00
internal PInvokeWrapperGenerator MarshalNativeExceptionsState { get ; set ; }
2016-04-21 15:57:02 +03:00
internal RuntimeOptions RuntimeOptions { get ; set ; }
2016-10-26 13:57:11 +03:00
public bool SkipExportedSymbolsInSdkAssemblies { get ; set ; }
2016-04-21 15:57:02 +03:00
public static I18nAssemblies ParseI18nAssemblies ( string i18n )
{
var assemblies = I18nAssemblies . None ;
foreach ( var part in i18n . Split ( ',' ) ) {
var assembly = part . Trim ( ) ;
if ( string . IsNullOrEmpty ( assembly ) )
continue ;
try {
assemblies | = ( I18nAssemblies ) Enum . Parse ( typeof ( I18nAssemblies ) , assembly , true ) ;
} catch {
throw new FormatException ( "Unknown value for i18n: " + assembly ) ;
}
}
return assemblies ;
}
}
2017-01-18 11:22:19 +03:00
public class MonoMacLinkContext : DerivedLinkContext {
2016-04-21 15:57:02 +03:00
Dictionary < string , List < MethodDefinition > > pinvokes = new Dictionary < string , List < MethodDefinition > > ( ) ;
public MonoMacLinkContext ( Pipeline pipeline , AssemblyResolver resolver ) : base ( pipeline , resolver )
{
}
public IDictionary < string , List < MethodDefinition > > PInvokeModules {
get { return pinvokes ; }
}
}
class Linker {
public static void Process ( LinkerOptions options , out LinkContext context , out List < string > assemblies )
{
switch ( options . TargetFramework . Identifier ) {
case "Xamarin.Mac" :
Profile . Current = new MacMobileProfile ( options . Architecture = = "x86_64" ? 64 : 32 ) ;
break ;
default :
Profile . Current = new MonoMacProfile ( ) ;
break ;
}
Namespaces . Initialize ( ) ;
var pipeline = CreatePipeline ( options ) ;
pipeline . PrependStep ( new ResolveFromAssemblyStep ( options . MainAssembly ) ) ;
context = CreateLinkContext ( options , pipeline ) ;
context . Resolver . AddSearchDirectory ( options . OutputDirectory ) ;
try {
pipeline . Process ( context ) ;
} catch ( AssemblyResolutionException fnfe ) {
throw new MonoMacException ( 2002 , true , fnfe , fnfe . Message ) ;
} catch ( AggregateException ) {
throw ;
} catch ( MonoMacException ) {
throw ;
} catch ( ResolutionException re ) {
TypeReference tr = ( re . Member as TypeReference ) ;
IMetadataScope scope = tr = = null ? re . Member . DeclaringType . Scope : tr . Scope ;
throw new MonoMacException ( 2002 , true , re , "Failed to resolve \"{0}\" reference from \"{1}\"" , re . Member , scope ) ;
2017-02-03 23:04:06 +03:00
} catch ( XmlResolutionException ex ) {
throw new MonoMacException ( 2017 , true , ex , "Could not process XML description: {0}" , ex ? . InnerException ? . Message ? ? ex . Message ) ;
2016-04-21 15:57:02 +03:00
} catch ( Exception e ) {
throw new MonoMacException ( 2001 , true , e , "Could not link assemblies. Reason: {0}" , e . Message ) ;
}
assemblies = ListAssemblies ( context ) ;
}
static LinkContext CreateLinkContext ( LinkerOptions options , Pipeline pipeline )
{
var context = new MonoMacLinkContext ( pipeline , options . Resolver ) ;
context . CoreAction = AssemblyAction . Link ;
context . LinkSymbols = options . LinkSymbols ;
if ( options . LinkSymbols ) {
context . SymbolReaderProvider = new MdbReaderProvider ( ) ;
context . SymbolWriterProvider = new MdbWriterProvider ( ) ;
}
context . OutputDirectory = options . OutputDirectory ;
return context ;
}
static Pipeline CreatePipeline ( LinkerOptions options )
{
var pipeline = new Pipeline ( ) ;
2016-05-11 17:16:30 +03:00
pipeline . AppendStep ( options . LinkMode = = LinkMode . None ? new LoadOptionalReferencesStep ( ) : new LoadReferencesStep ( ) ) ;
2016-04-21 15:57:02 +03:00
if ( options . I18nAssemblies ! = I18nAssemblies . None )
pipeline . AppendStep ( new LoadI18nAssemblies ( options . I18nAssemblies ) ) ;
// that must be done early since the XML files can "add" new assemblies [#15878]
// and some of the assemblies might be (directly or referenced) SDK assemblies
foreach ( string definition in options . ExtraDefinitions )
pipeline . AppendStep ( GetResolveStep ( definition ) ) ;
if ( options . LinkMode ! = LinkMode . None )
pipeline . AppendStep ( new BlacklistStep ( ) ) ;
pipeline . AppendStep ( new CustomizeMacActions ( options . LinkMode , options . SkippedAssemblies ) ) ;
// We need to store the Field attribute in annotations, since it may end up removed.
pipeline . AppendStep ( new ProcessExportedFields ( ) ) ;
if ( options . LinkMode ! = LinkMode . None ) {
pipeline . AppendStep ( new TypeMapStep ( ) ) ;
pipeline . AppendStep ( new SubStepDispatcher {
new ApplyPreserveAttribute ( ) ,
new CoreRemoveSecurity ( ) ,
new OptimizeGeneratedCodeSubStep ( options . EnsureUIThread ) ,
2016-11-01 20:11:25 +03:00
new RemoveUserResourcesSubStep ( ) ,
2016-04-21 15:57:02 +03:00
new CoreRemoveAttributes ( ) ,
new CoreHttpMessageHandler ( options ) ,
new MarkNSObjects ( ) ,
} ) ;
pipeline . AppendStep ( new MonoMacPreserveCode ( options ) ) ;
pipeline . AppendStep ( new PreserveCrypto ( ) ) ;
pipeline . AppendStep ( new MonoMacMarkStep ( ) ) ;
pipeline . AppendStep ( new MacRemoveResources ( options ) ) ;
pipeline . AppendStep ( new MobileSweepStep ( ) ) ;
pipeline . AppendStep ( new CleanStep ( ) ) ;
pipeline . AppendStep ( new MonoMacNamespaces ( ) ) ;
pipeline . AppendStep ( new RemoveSelectors ( ) ) ;
pipeline . AppendStep ( new RegenerateGuidStep ( ) ) ;
2016-11-01 20:11:25 +03:00
} else {
SubStepDispatcher sub = new SubStepDispatcher ( ) {
new RemoveUserResourcesSubStep ( )
} ;
pipeline . AppendStep ( sub ) ;
2016-04-21 15:57:02 +03:00
}
2016-10-26 13:57:11 +03:00
pipeline . AppendStep ( new ListExportedSymbols ( options . MarshalNativeExceptionsState , options . SkipExportedSymbolsInSdkAssemblies ) ) ;
2016-04-21 15:57:02 +03:00
pipeline . AppendStep ( new OutputStep ( ) ) ;
return pipeline ;
}
static List < string > ListAssemblies ( LinkContext context )
{
var list = new List < string > ( ) ;
foreach ( var assembly in context . GetAssemblies ( ) ) {
if ( context . Annotations . GetAction ( assembly ) = = AssemblyAction . Delete )
continue ;
list . Add ( GetFullyQualifiedName ( assembly ) ) ;
}
return list ;
}
static string GetFullyQualifiedName ( AssemblyDefinition assembly )
{
2016-08-24 16:43:35 +03:00
return assembly . MainModule . FileName ;
2016-04-21 15:57:02 +03:00
}
static ResolveFromXmlStep GetResolveStep ( string filename )
{
filename = Path . GetFullPath ( filename ) ;
if ( ! File . Exists ( filename ) )
throw new MonoMacException ( 2004 , true , "Extra linker definitions file '{0}' could not be located." , filename ) ;
try {
using ( StreamReader sr = new StreamReader ( filename ) ) {
return new ResolveFromXmlStep ( new XPathDocument ( new StringReader ( sr . ReadToEnd ( ) ) ) ) ;
}
}
catch ( Exception e ) {
throw new MonoMacException ( 2005 , true , e , "Definitions from '{0}' could not be parsed." , filename ) ;
}
}
}
public class CustomizeMacActions : CustomizeActions
{
LinkMode link_mode ;
public CustomizeMacActions ( LinkMode mode , IEnumerable < string > skipped_assemblies )
: base ( mode = = LinkMode . SDKOnly , skipped_assemblies )
{
link_mode = mode ;
}
protected override bool IsLinked ( AssemblyDefinition assembly )
{
if ( link_mode = = LinkMode . None )
return false ;
return base . IsLinked ( assembly ) ;
}
protected override void ProcessAssembly ( AssemblyDefinition assembly )
{
if ( link_mode = = LinkMode . None ) {
Annotations . SetAction ( assembly , AssemblyAction . Copy ) ;
return ;
}
base . ProcessAssembly ( assembly ) ;
}
}
2016-05-11 17:16:30 +03:00
class LoadOptionalReferencesStep : LoadReferencesStep
{
HashSet < AssemblyNameDefinition > _references = new HashSet < AssemblyNameDefinition > ( ) ;
protected override void ProcessAssembly ( AssemblyDefinition assembly )
{
ProcessReferences ( assembly ) ;
}
void ProcessReferences ( AssemblyDefinition assembly )
{
if ( _references . Contains ( assembly . Name ) )
return ;
_references . Add ( assembly . Name ) ;
foreach ( AssemblyNameReference reference in assembly . MainModule . AssemblyReferences ) {
try {
ProcessReferences ( Context . Resolve ( reference ) ) ;
} catch ( AssemblyResolutionException fnfe ) {
ErrorHelper . Warning ( 2013 , fnfe , "Failed to resolve the reference to \"{0}\", referenced in \"{1}\". The app will not include the referenced assembly, and may fail at runtime." , fnfe . AssemblyReference . FullName , assembly . Name . FullName ) ;
}
}
}
}
2016-04-21 15:57:02 +03:00
}