930 строки
34 KiB
C#
930 строки
34 KiB
C#
// Copyright 2013--2014 Xamarin Inc. All rights reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.Linq;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using Mono.Cecil;
|
|
using Mono.Tuner;
|
|
using Mono.Linker;
|
|
using Xamarin.Linker;
|
|
|
|
using Xamarin.Utils;
|
|
using Registrar;
|
|
using ObjCRuntime;
|
|
|
|
#if MONOTOUCH
|
|
using MonoTouch;
|
|
using MonoTouch.Tuner;
|
|
using PlatformResolver = MonoTouch.Tuner.MonoTouchResolver;
|
|
using PlatformLinkContext = MonoTouch.Tuner.MonoTouchLinkContext;
|
|
#elif MMP
|
|
using MonoMac.Tuner;
|
|
using PlatformResolver = Xamarin.Bundler.MonoMacResolver;
|
|
using PlatformLinkContext = MonoMac.Tuner.MonoMacLinkContext;
|
|
#elif NET
|
|
using LinkerOptions = Xamarin.Linker.LinkerConfiguration;
|
|
using PlatformLinkContext = Xamarin.Tuner.DerivedLinkContext;
|
|
using PlatformResolver = Xamarin.Linker.DotNetResolver;
|
|
#else
|
|
#error Invalid defines
|
|
#endif
|
|
|
|
namespace Xamarin.Bundler {
|
|
public partial class Target {
|
|
public Application App;
|
|
public AssemblyCollection Assemblies = new AssemblyCollection (); // The root assembly is not in this list.
|
|
|
|
public PlatformLinkContext LinkContext;
|
|
public LinkerOptions LinkerOptions;
|
|
public PlatformResolver Resolver = new PlatformResolver ();
|
|
|
|
public HashSet<string> Frameworks = new HashSet<string> ();
|
|
public HashSet<string> WeakFrameworks = new HashSet<string> ();
|
|
|
|
internal StaticRegistrar StaticRegistrar { get; set; }
|
|
|
|
// If we didn't link because the existing (cached) assemblyes are up-to-date.
|
|
bool cached_link = false;
|
|
|
|
Symbols dynamic_symbols;
|
|
|
|
// Note that each 'Target' can have multiple abis: armv7+armv7s for instance.
|
|
public List<Abi> Abis;
|
|
|
|
// If we're targetting a 32 bit arch for this target.
|
|
bool? is32bits;
|
|
public bool Is32Build {
|
|
get {
|
|
if (!is32bits.HasValue)
|
|
is32bits = Application.IsArchEnabled (Abis, Abi.Arch32Mask);
|
|
return is32bits.Value;
|
|
}
|
|
}
|
|
|
|
// If we're targetting a 64 bit arch for this target.
|
|
bool? is64bits;
|
|
public bool Is64Build {
|
|
get {
|
|
if (!is64bits.HasValue)
|
|
is64bits = Application.IsArchEnabled (Abis, Abi.Arch64Mask);
|
|
return is64bits.Value;
|
|
}
|
|
}
|
|
|
|
public Target (Application app)
|
|
{
|
|
this.App = app;
|
|
this.StaticRegistrar = new StaticRegistrar (this);
|
|
}
|
|
|
|
// If this is an app extension, this returns the equivalent (32/64bit) target for the container app.
|
|
// This may be null (it's possible to build an extension for 32+64bit, and the main app only for 64-bit, for instance.
|
|
public Target ContainerTarget {
|
|
get {
|
|
return App.ContainerApp.Targets.FirstOrDefault ((v) => v.Is32Build == Is32Build);
|
|
}
|
|
}
|
|
|
|
public Assembly AddAssembly (AssemblyDefinition assembly)
|
|
{
|
|
var asm = new Assembly (this, assembly);
|
|
Assemblies.Add (asm);
|
|
return asm;
|
|
}
|
|
|
|
// This will find the link context, possibly looking in container targets.
|
|
public PlatformLinkContext GetLinkContext ()
|
|
{
|
|
if (LinkContext != null)
|
|
return LinkContext;
|
|
if (App.IsExtension && App.IsCodeShared)
|
|
return ContainerTarget.GetLinkContext ();
|
|
return null;
|
|
}
|
|
|
|
public bool CachedLink {
|
|
get {
|
|
return cached_link;
|
|
}
|
|
}
|
|
|
|
public void ExtractNativeLinkInfo (List<Exception> exceptions)
|
|
{
|
|
foreach (var a in Assemblies) {
|
|
try {
|
|
a.ExtractNativeLinkInfo ();
|
|
} catch (Exception e) {
|
|
exceptions.Add (e);
|
|
}
|
|
}
|
|
|
|
#if MTOUCH
|
|
if (!App.OnlyStaticLibraries && Assemblies.Count ((v) => v.HasLinkWithAttributes) > 1) {
|
|
ErrorHelper.Warning (127, Errors.MT0127);
|
|
App.ClearAssemblyBuildTargets (); // the default is to compile to static libraries, so just revert to the default.
|
|
}
|
|
#endif
|
|
}
|
|
|
|
[DllImport (Constants.libSystemLibrary, SetLastError = true)]
|
|
static extern string realpath (string path, IntPtr zero);
|
|
|
|
public static string GetRealPath (string path, bool warnIfNoSuchPathExists = true)
|
|
{
|
|
// For some reason realpath doesn't always like filenames only, and will randomly fail.
|
|
// Prepend the current directory if there's no directory specified.
|
|
if (string.IsNullOrEmpty (Path.GetDirectoryName (path)))
|
|
path = Path.Combine (Environment.CurrentDirectory, path);
|
|
|
|
var rv = realpath (path, IntPtr.Zero);
|
|
if (rv != null)
|
|
return rv;
|
|
|
|
var errno = Marshal.GetLastWin32Error ();
|
|
if (warnIfNoSuchPathExists || (errno !=2))
|
|
ErrorHelper.Warning (54, Errors.MT0054, path, FileCopier.strerror (errno), errno);
|
|
return path;
|
|
}
|
|
|
|
public void ValidateAssembliesBeforeLink ()
|
|
{
|
|
if (App.AreAnyAssembliesTrimmed) {
|
|
foreach (Assembly assembly in Assemblies) {
|
|
if ((assembly.AssemblyDefinition.MainModule.Attributes & ModuleAttributes.ILOnly) == 0)
|
|
throw ErrorHelper.CreateError (2014, Errors.MT2014, assembly.AssemblyDefinition.MainModule.FileName);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void ComputeLinkerFlags ()
|
|
{
|
|
foreach (var a in Assemblies)
|
|
a.ComputeLinkerFlags ();
|
|
}
|
|
|
|
public void GatherFrameworks ()
|
|
{
|
|
Assembly asm = null;
|
|
|
|
foreach (var assembly in Assemblies) {
|
|
if (assembly.AssemblyDefinition.Name.Name == Driver.GetProductAssembly (App)) {
|
|
asm = assembly;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (asm == null)
|
|
throw ErrorHelper.CreateError (99, Errors.MX0099, $"could not find the product assembly {Driver.GetProductAssembly(App)} in the list of assemblies referenced by the executable");
|
|
|
|
AssemblyDefinition productAssembly = asm.AssemblyDefinition;
|
|
|
|
// *** make sure any change in the above lists (or new list) are also reflected in
|
|
// *** Makefile so simlauncher-sgen does not miss any framework
|
|
|
|
HashSet<string> processed = new HashSet<string> ();
|
|
#if !MONOMAC
|
|
Version v80 = new Version (8, 0);
|
|
#endif
|
|
|
|
foreach (ModuleDefinition md in productAssembly.Modules) {
|
|
foreach (TypeDefinition td in md.Types) {
|
|
// process only once each namespace (as we keep adding logic below)
|
|
string nspace = td.Namespace;
|
|
if (processed.Contains (nspace))
|
|
continue;
|
|
processed.Add (nspace);
|
|
|
|
Framework framework;
|
|
if (Driver.GetFrameworks (App).TryGetValue (nspace, out framework)) {
|
|
// framework specific processing
|
|
switch (framework.Name) {
|
|
#if MONOMAC && !NET
|
|
case "QTKit":
|
|
// we already warn in Frameworks.cs Gather method
|
|
if (!Driver.LinkProhibitedFrameworks)
|
|
continue;
|
|
break;
|
|
#else
|
|
case "CoreAudioKit":
|
|
// CoreAudioKit seems to be functional in the iOS 9 simulator.
|
|
if (App.IsSimulatorBuild && App.SdkVersion.Major < 9)
|
|
continue;
|
|
break;
|
|
case "Metal":
|
|
case "MetalKit":
|
|
case "MetalPerformanceShaders":
|
|
case "CHIP":
|
|
case "PHASE":
|
|
case "ThreadNetwork":
|
|
// some frameworks do not exists on simulators and will result in linker errors if we include them
|
|
if (App.IsSimulatorBuild)
|
|
continue;
|
|
break;
|
|
case "DeviceCheck":
|
|
if (App.IsSimulatorBuild && App.SdkVersion.Major < 13)
|
|
continue;
|
|
break;
|
|
case "PushKit":
|
|
// in Xcode 6 beta 7 this became an (ld) error - it was a warning earlier :(
|
|
// ld: embedded dylibs/frameworks are only supported on iOS 8.0 and later (@rpath/PushKit.framework/PushKit) for architecture armv7
|
|
// this was fixed in Xcode 6.2 (6.1 was still buggy) see #29786
|
|
if ((App.DeploymentTarget < v80) && (Driver.XcodeVersion < new Version (6, 2))) {
|
|
ErrorHelper.Warning (49, Errors.MT0049, framework.Name);
|
|
continue;
|
|
}
|
|
break;
|
|
#if !NET
|
|
case "WatchKit":
|
|
// Xcode 11 doesn't ship WatchKit for iOS
|
|
if (Driver.XcodeVersion.Major == 11 && App.Platform == ApplePlatform.iOS) {
|
|
ErrorHelper.Warning (5219, Errors.MT5219);
|
|
continue;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
if (App.IsSimulatorBuild && !App.IsFrameworkAvailableInSimulator (framework.Name)) {
|
|
if (App.AreAnyAssembliesTrimmed) {
|
|
ErrorHelper.Warning (5223, Errors.MX5223, framework.Name, App.PlatformName);
|
|
} else {
|
|
Driver.Log (3, Errors.MX5223, framework.Name, App.PlatformName);
|
|
}
|
|
continue;
|
|
}
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
if (framework.Unavailable) {
|
|
ErrorHelper.Warning (181, Errors.MX0181 /* Not linking with the framework {0} (used by the type {1}) because it's not available on the current platform ({2}). */, framework.Name, td.FullName, App.PlatformName);
|
|
continue;
|
|
}
|
|
|
|
if (App.SdkVersion >= framework.Version) {
|
|
var add_to = framework.AlwaysWeakLinked || App.DeploymentTarget < framework.Version ? asm.WeakFrameworks : asm.Frameworks;
|
|
add_to.Add (framework.Name);
|
|
continue;
|
|
} else {
|
|
Driver.Log (3, "Not linking with the framework {0} (used by the type {1}) because it was introduced in {2} {3}, and we're using the {2} {4} SDK.", framework.Name, td.FullName, App.PlatformName, framework.Version, App.SdkVersion);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure there are no duplicates between frameworks and weak frameworks.
|
|
// Keep the weak ones.
|
|
asm.Frameworks.ExceptWith (asm.WeakFrameworks);
|
|
}
|
|
|
|
internal static void PrintAssemblyReferences (AssemblyDefinition assembly)
|
|
{
|
|
if (Driver.Verbosity < 2)
|
|
return;
|
|
|
|
var main = assembly.MainModule;
|
|
Driver.Log ($"Loaded assembly '{assembly.FullName}' from {StringUtils.Quote (assembly.MainModule.FileName)}");
|
|
foreach (var ar in main.AssemblyReferences)
|
|
Driver.Log ($" References: '{ar.FullName}'");
|
|
}
|
|
|
|
public Symbols GetAllSymbols ()
|
|
{
|
|
CollectAllSymbols ();
|
|
return dynamic_symbols;
|
|
}
|
|
|
|
public void CollectAllSymbols ()
|
|
{
|
|
if (dynamic_symbols != null)
|
|
return;
|
|
|
|
var dyn_msgSend_functions = new [] {
|
|
new { Name = "xamarin_dyn_objc_msgSend", ValidAbis = Abi.SimulatorArchMask | Abi.ARM64 },
|
|
new { Name = "xamarin_dyn_objc_msgSendSuper", ValidAbis = Abi.SimulatorArchMask | Abi.ARM64 },
|
|
new { Name = "xamarin_dyn_objc_msgSend_stret", ValidAbis = Abi.SimulatorArchMask },
|
|
new { Name = "xamarin_dyn_objc_msgSendSuper_stret", ValidAbis = Abi.SimulatorArchMask },
|
|
};
|
|
|
|
var cache_location = Path.Combine (App.Cache.Location, "entry-points.txt");
|
|
if (cached_link) {
|
|
dynamic_symbols = new Symbols ();
|
|
dynamic_symbols.Load (cache_location, this);
|
|
} else {
|
|
if (LinkContext == null) {
|
|
// This happens when using the simlauncher and the msbuild tasks asked for a list
|
|
// of symbols (--symbollist). In that case just produce an empty list, since the
|
|
// binary shouldn't end up stripped anyway.
|
|
dynamic_symbols = new Symbols ();
|
|
} else {
|
|
dynamic_symbols = LinkContext.RequiredSymbols;
|
|
}
|
|
|
|
// keep the debugging helper in debugging binaries only
|
|
var has_mono_pmip = App.EnableDebug;
|
|
#if MMP
|
|
has_mono_pmip &= !Driver.IsUnifiedFullSystemFramework;
|
|
#endif
|
|
if (has_mono_pmip)
|
|
dynamic_symbols.AddFunction ("mono_pmip");
|
|
|
|
bool has_dyn_msgSend;
|
|
|
|
switch (App.Platform) {
|
|
case ApplePlatform.iOS:
|
|
case ApplePlatform.TVOS:
|
|
case ApplePlatform.WatchOS:
|
|
has_dyn_msgSend = App.IsSimulatorBuild;
|
|
break;
|
|
case ApplePlatform.MacCatalyst:
|
|
case ApplePlatform.MacOSX:
|
|
has_dyn_msgSend = App.MarshalObjectiveCExceptions != MarshalObjectiveCExceptionMode.Disable && !App.RequiresPInvokeWrappers;
|
|
break;
|
|
default:
|
|
throw ErrorHelper.CreateError (71, Errors.MX0071, App.Platform, App.ProductName);
|
|
}
|
|
|
|
if (has_dyn_msgSend) {
|
|
foreach (var dyn_msgSend_function in dyn_msgSend_functions)
|
|
dynamic_symbols.AddFunction (dyn_msgSend_function.Name);
|
|
}
|
|
|
|
#if MONOTOUCH
|
|
if (App.EnableProfiling && App.LibProfilerLinkMode == AssemblyBuildTarget.StaticObject)
|
|
dynamic_symbols.AddFunction ("mono_profiler_init_log");
|
|
#endif
|
|
|
|
dynamic_symbols.Save (cache_location);
|
|
}
|
|
|
|
foreach (var dynamicFunction in dyn_msgSend_functions) {
|
|
var symbol = dynamic_symbols.Find (dynamicFunction.Name);
|
|
if (symbol != null) {
|
|
symbol.ValidAbis = dynamicFunction.ValidAbis;
|
|
}
|
|
}
|
|
|
|
foreach (var name in App.IgnoredSymbols) {
|
|
var symbol = dynamic_symbols.Find (name);
|
|
if (symbol == null) {
|
|
ErrorHelper.Warning (5218, Errors.MT5218, StringUtils.Quote (name));
|
|
} else {
|
|
symbol.Ignore = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool IsRequiredSymbol (Symbol symbol, Assembly single_assembly = null, Abi? target_abis = null)
|
|
{
|
|
if (symbol.Ignore)
|
|
return false;
|
|
|
|
// If this symbol is only defined for certain abis, verify if there is an abi match
|
|
if (target_abis.HasValue && symbol.ValidAbis.HasValue && (target_abis.Value & symbol.ValidAbis.Value) == 0)
|
|
return false;
|
|
|
|
// Check if this symbol is used in the assembly we're filtering to
|
|
if (single_assembly != null && !symbol.Members.Any ((v) => v.Module.Assembly == single_assembly.AssemblyDefinition))
|
|
return false; // nope, this symbol is not used in the assembly we're using as filter.
|
|
|
|
// If we're code-sharing, the managed linker might have found symbols
|
|
// that are not in any of the assemblies in the current app.
|
|
// This occurs because the managed linker processes all the
|
|
// assemblies for all the apps together, but when linking natively
|
|
// we're only linking with the assemblies that actually go into the app.
|
|
if (App.Platform != ApplePlatform.MacOSX && App.IsCodeShared && symbol.Assemblies.Count > 0) {
|
|
// So if this is a symbol related to any assembly, make sure
|
|
// at least one of those assemblies are in the current app.
|
|
if (!symbol.Assemblies.Any ((v) => Assemblies.Contains (v)))
|
|
return false;
|
|
}
|
|
|
|
switch (symbol.Type) {
|
|
case SymbolType.Field:
|
|
return true;
|
|
case SymbolType.Function:
|
|
#if MTOUCH
|
|
// functions are not required if they're used in an assembly which isn't using dlsym, and we're AOT-compiling.
|
|
if (App.IsSimulatorBuild)
|
|
return true;
|
|
if (App.Platform == ApplePlatform.MacCatalyst)
|
|
return true;
|
|
if (single_assembly != null)
|
|
return App.UseDlsym (single_assembly.FileName);
|
|
|
|
if (symbol.Members?.Any () == true) {
|
|
foreach (var member in symbol.Members) {
|
|
if (App.UseDlsym (member.Module.FileName)) {
|
|
// If any assembly uses dlsym to reference this symbol, it's a required symbol that must be preserved,
|
|
// because otherwise stripping the binary will cause the symbol (but not the function itself) to be removed,
|
|
// preventing any assembly using dlsym to find it.
|
|
return true;
|
|
}
|
|
}
|
|
// None of the members use dlsym (and we have at least one member), then we don't need to preserve the symbol.
|
|
return false;
|
|
}
|
|
#endif
|
|
return true;
|
|
case SymbolType.ObjectiveCClass:
|
|
// Objective-C classes are not required when we're using the static registrar and we're not compiling to shared libraries,
|
|
// (because the registrar code is linked into the main app, but not each shared library,
|
|
// so the registrar code won't keep symbols in the shared libraries).
|
|
if (single_assembly != null)
|
|
return true;
|
|
return App.Registrar != RegistrarMode.Static;
|
|
default:
|
|
throw ErrorHelper.CreateError (99, Errors.MX0099, $"invalid symbol type {symbol.Type} for symbol {symbol.Name}");
|
|
}
|
|
}
|
|
|
|
public Symbols GetRequiredSymbols (Assembly assembly = null, Abi? target_abis = null)
|
|
{
|
|
CollectAllSymbols ();
|
|
|
|
Symbols filtered = new Symbols ();
|
|
foreach (var ep in dynamic_symbols) {
|
|
if (IsRequiredSymbol (ep, assembly, target_abis)) {
|
|
filtered.Add (ep);
|
|
}
|
|
}
|
|
return filtered ?? dynamic_symbols;
|
|
}
|
|
|
|
#if MTOUCH
|
|
IEnumerable<CompileTask> GenerateReferencingSource (string reference_m, IEnumerable<Symbol> symbols)
|
|
#else
|
|
internal string GenerateReferencingSource (string reference_m, IEnumerable<Symbol> symbols)
|
|
#endif
|
|
{
|
|
if (!symbols.Any ()) {
|
|
if (File.Exists (reference_m))
|
|
File.Delete (reference_m);
|
|
#if MTOUCH
|
|
yield break;
|
|
#else
|
|
return null;
|
|
#endif
|
|
}
|
|
var sb = new StringBuilder ();
|
|
sb.AppendLine ("#import <Foundation/Foundation.h>");
|
|
foreach (var symbol in symbols) {
|
|
switch (symbol.Type) {
|
|
case SymbolType.Function:
|
|
case SymbolType.Field:
|
|
sb.Append ("extern void * ").Append (symbol.Name).AppendLine (";");
|
|
break;
|
|
case SymbolType.ObjectiveCClass:
|
|
sb.AppendLine ($"@interface {symbol.ObjectiveCName} : NSObject @end");
|
|
break;
|
|
default:
|
|
throw ErrorHelper.CreateError (99, Errors.MX0099, $"invalid symbol type {symbol.Type} for symbol {symbol.Name}");
|
|
}
|
|
}
|
|
sb.AppendLine ("static void __xamarin_symbol_referencer () __attribute__ ((used)) __attribute__ ((optnone));");
|
|
sb.AppendLine ("void __xamarin_symbol_referencer ()");
|
|
sb.AppendLine ("{");
|
|
sb.AppendLine ("\tvoid *value;");
|
|
foreach (var symbol in symbols) {
|
|
switch (symbol.Type) {
|
|
case SymbolType.Function:
|
|
case SymbolType.Field:
|
|
sb.AppendLine ($"\tvalue = {symbol.Name};");
|
|
break;
|
|
case SymbolType.ObjectiveCClass:
|
|
sb.AppendLine ($"\tvalue = [{symbol.ObjectiveCName} class];");
|
|
break;
|
|
default:
|
|
throw ErrorHelper.CreateError (99, Errors.MX0099, $"invalid symbol type {symbol.Type} for symbol {symbol.Name}");
|
|
}
|
|
}
|
|
sb.AppendLine ("}");
|
|
sb.AppendLine ();
|
|
|
|
Driver.WriteIfDifferent (reference_m, sb.ToString (), true);
|
|
|
|
#if MTOUCH
|
|
foreach (var abi in GetArchitectures (AssemblyBuildTarget.StaticObject)) {
|
|
var arch = abi.AsArchString ();
|
|
var reference_o = Path.Combine (Path.GetDirectoryName (reference_m), arch, Path.GetFileNameWithoutExtension (reference_m) + ".o");
|
|
var compile_task = new CompileTask {
|
|
Target = this,
|
|
Abi = abi,
|
|
InputFile = reference_m,
|
|
OutputFile = reference_o,
|
|
SharedLibrary = false,
|
|
Language = "objective-c",
|
|
};
|
|
yield return compile_task;
|
|
}
|
|
#else
|
|
return reference_m;
|
|
#endif
|
|
}
|
|
|
|
// This is to load the symbols for all assemblies, so that we can give better error messages
|
|
// (with file name / line number information).
|
|
public void LoadSymbols ()
|
|
{
|
|
foreach (var a in Assemblies)
|
|
a.LoadSymbols ();
|
|
}
|
|
|
|
public void GenerateMain (ApplePlatform platform, Abi abi, string main_source, IList<string> registration_methods)
|
|
{
|
|
var sb = new StringBuilder ();
|
|
GenerateMain (sb, platform, abi, main_source, registration_methods);
|
|
}
|
|
|
|
public void GenerateMain (StringBuilder sb, ApplePlatform platform, Abi abi, string main_source, IList<string> registration_methods)
|
|
{
|
|
try {
|
|
using (var sw = new StringWriter (sb)) {
|
|
|
|
if (registration_methods != null) {
|
|
foreach (var method in registration_methods) {
|
|
sw.Write ("extern \"C\" void ");
|
|
sw.Write (method);
|
|
sw.WriteLine ("();");
|
|
}
|
|
sw.WriteLine ();
|
|
}
|
|
|
|
sw.WriteLine ("static void xamarin_invoke_registration_methods ()");
|
|
sw.WriteLine ("{");
|
|
if (registration_methods != null) {
|
|
for (int i = 0; i < registration_methods.Count; i++) {
|
|
sw.Write ("\t");
|
|
sw.Write (registration_methods [i]);
|
|
sw.WriteLine ("();");
|
|
}
|
|
}
|
|
sw.WriteLine ("}");
|
|
sw.WriteLine ();
|
|
|
|
switch (platform) {
|
|
case ApplePlatform.iOS:
|
|
case ApplePlatform.TVOS:
|
|
case ApplePlatform.WatchOS:
|
|
case ApplePlatform.MacCatalyst:
|
|
GenerateIOSMain (sw, abi);
|
|
break;
|
|
case ApplePlatform.MacOSX:
|
|
#if NET
|
|
GenerateIOSMain (sw, abi);
|
|
#else
|
|
GenerateMacMain (sw);
|
|
#endif
|
|
break;
|
|
default:
|
|
throw ErrorHelper.CreateError (71, Errors.MX0071, platform, App.ProductName);
|
|
}
|
|
}
|
|
Driver.WriteIfDifferent (main_source, sb.ToString (), true);
|
|
} catch (ProductException) {
|
|
throw;
|
|
} catch (Exception e) {
|
|
throw new ProductException (4001, true, e, Errors.MT4001, main_source);
|
|
}
|
|
}
|
|
|
|
void GenerateMacMain (StringWriter sw)
|
|
{
|
|
sw.WriteLine ("#define MONOMAC 1");
|
|
sw.WriteLine ("#include <xamarin/xamarin.h>");
|
|
#if !NET
|
|
if (App.Registrar == RegistrarMode.PartialStatic)
|
|
sw.WriteLine ("extern \"C\" void xamarin_create_classes_Xamarin_Mac ();");
|
|
#endif
|
|
sw.WriteLine ();
|
|
sw.WriteLine ();
|
|
sw.WriteLine ();
|
|
sw.WriteLine ("extern \"C\" int xammac_setup ()");
|
|
|
|
sw.WriteLine ("{");
|
|
if (App.CustomBundleName != null) {
|
|
sw.WriteLine ("\textern NSString* xamarin_custom_bundle_name;");
|
|
sw.WriteLine ("\txamarin_custom_bundle_name = @\"" + App.CustomBundleName + "\";");
|
|
}
|
|
sw.WriteLine ("\txamarin_executable_name = \"{0}\";", App.AssemblyName);
|
|
if (!App.IsDefaultMarshalManagedExceptionMode)
|
|
sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", App.MarshalManagedExceptions);
|
|
sw.WriteLine ("\txamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionMode{0};", App.MarshalObjectiveCExceptions);
|
|
if (App.DisableLldbAttach.HasValue ? App.DisableLldbAttach.Value : !App.EnableDebug)
|
|
sw.WriteLine ("\txamarin_disable_lldb_attach = true;");
|
|
if (App.DisableOmitFramePointer ?? App.EnableDebug)
|
|
sw.WriteLine ("\txamarin_disable_omit_fp = true;");
|
|
sw.WriteLine ();
|
|
|
|
#if !NET
|
|
if (App.Registrar == RegistrarMode.Static)
|
|
sw.WriteLine ("\txamarin_create_classes ();");
|
|
else if (App.Registrar == RegistrarMode.PartialStatic)
|
|
sw.WriteLine ("\txamarin_create_classes_Xamarin_Mac ();");
|
|
#endif
|
|
|
|
if (App.EnableDebug)
|
|
sw.WriteLine ("\txamarin_debug_mode = TRUE;");
|
|
|
|
sw.WriteLine ($"\tsetenv (\"MONO_GC_PARAMS\", \"{App.MonoGCParams}\", 1);");
|
|
|
|
sw.WriteLine ("\txamarin_supports_dynamic_registration = {0};", App.DynamicRegistrationSupported ? "TRUE" : "FALSE");
|
|
|
|
#if MMP
|
|
// AOT for .NET/macOS needs some design to verify it's staying the same way as current Xamarin.Mac
|
|
// for instance: we might decide to select which assemblies to AOT in a different way.
|
|
if (App.AOTOptions != null && App.AOTOptions.IsHybridAOT)
|
|
sw.WriteLine ("\txamarin_mac_hybrid_aot = TRUE;");
|
|
#endif
|
|
|
|
if (Driver.IsUnifiedMobile)
|
|
sw.WriteLine ("\txamarin_mac_modern = TRUE;");
|
|
|
|
sw.WriteLine ("\txamarin_invoke_registration_methods ();");
|
|
|
|
sw.WriteLine ("\treturn 0;");
|
|
sw.WriteLine ("}");
|
|
sw.WriteLine ();
|
|
}
|
|
|
|
// note: this is executed under Parallel.ForEach
|
|
void GenerateIOSMain (StringWriter sw, Abi abi)
|
|
{
|
|
var app = App;
|
|
var assemblies = Assemblies;
|
|
var assembly_name = App.AssemblyName;
|
|
var assembly_externs = new StringBuilder ();
|
|
var assembly_aot_modules = new StringBuilder ();
|
|
var register_assemblies = new StringBuilder ();
|
|
var assembly_location = new StringBuilder ();
|
|
var assembly_location_count = 0;
|
|
var enable_llvm = (abi & Abi.LLVM) != 0;
|
|
|
|
register_assemblies.AppendLine ("\tGCHandle exception_gchandle = INVALID_GCHANDLE;");
|
|
foreach (var s in assemblies) {
|
|
if (!s.IsAOTCompiled)
|
|
continue;
|
|
|
|
var info = s.AssemblyDefinition.Name.Name;
|
|
info = EncodeAotSymbol (info);
|
|
assembly_externs.Append ("extern void *mono_aot_module_").Append (info).AppendLine ("_info;");
|
|
assembly_aot_modules.Append ("\tmono_aot_register_module (mono_aot_module_").Append (info).AppendLine ("_info);");
|
|
|
|
string sname = s.FileName;
|
|
if (assembly_name != sname && IsBoundAssembly (s)) {
|
|
register_assemblies.Append ("\txamarin_open_and_register (\"").Append (sname).Append ("\", &exception_gchandle);").AppendLine ();
|
|
register_assemblies.AppendLine ("\txamarin_process_managed_exception_gchandle (exception_gchandle);");
|
|
}
|
|
}
|
|
|
|
var frameworks = assemblies.Where ((a) => a.BuildTarget == AssemblyBuildTarget.Framework)
|
|
.OrderBy ((a) => a.Identity, StringComparer.Ordinal);
|
|
foreach (var asm_fw in frameworks) {
|
|
var asm_name = asm_fw.Identity;
|
|
if (asm_fw.BuildTargetName == asm_name)
|
|
continue; // this is deduceable
|
|
var prefix = string.Empty;
|
|
if (!app.HasFrameworksDirectory && asm_fw.IsCodeShared)
|
|
prefix = "../../";
|
|
var suffix = string.Empty;
|
|
if (app.IsSimulatorBuild)
|
|
suffix = "/simulator";
|
|
assembly_location.AppendFormat ("\t{{ \"{0}\", \"{2}Frameworks/{1}.framework/MonoBundle{3}\" }},\n", asm_name, asm_fw.BuildTargetName, prefix, suffix);
|
|
assembly_location_count++;
|
|
}
|
|
|
|
sw.WriteLine ("#include \"xamarin/xamarin.h\"");
|
|
|
|
if (assembly_location.Length > 0) {
|
|
sw.WriteLine ();
|
|
sw.WriteLine ("struct AssemblyLocation assembly_location_entries [] = {");
|
|
sw.WriteLine (assembly_location);
|
|
sw.WriteLine ("};");
|
|
|
|
sw.WriteLine ();
|
|
sw.WriteLine ("struct AssemblyLocations assembly_locations = {{ {0}, assembly_location_entries }};", assembly_location_count);
|
|
}
|
|
|
|
sw.WriteLine ();
|
|
sw.WriteLine (assembly_externs);
|
|
|
|
sw.WriteLine ("void xamarin_register_modules_impl ()");
|
|
sw.WriteLine ("{");
|
|
sw.WriteLine (assembly_aot_modules);
|
|
sw.WriteLine ("}");
|
|
sw.WriteLine ();
|
|
|
|
sw.WriteLine ("void xamarin_register_assemblies_impl ()");
|
|
sw.WriteLine ("{");
|
|
sw.WriteLine (register_assemblies);
|
|
sw.WriteLine ("}");
|
|
sw.WriteLine ();
|
|
|
|
// Burn in a reference to the profiling symbol so that the native linker doesn't remove it
|
|
// On iOS we can pass -u to the native linker, but that doesn't work on tvOS, where
|
|
// we're building with bitcode (even when bitcode is disabled, we still build with the
|
|
// bitcode marker, which makes the linker reject -u).
|
|
if (app.EnableProfiling) {
|
|
sw.WriteLine ("extern \"C\" { void mono_profiler_init_log (); }");
|
|
sw.WriteLine ("typedef void (*xamarin_profiler_symbol_def)();");
|
|
sw.WriteLine ("extern xamarin_profiler_symbol_def xamarin_profiler_symbol;");
|
|
sw.WriteLine ("xamarin_profiler_symbol_def xamarin_profiler_symbol = NULL;");
|
|
}
|
|
|
|
if (app.UseInterpreter) {
|
|
sw.WriteLine ("extern \"C\" { void mono_ee_interp_init (const char *); }");
|
|
sw.WriteLine ("extern \"C\" { void mono_icall_table_init (void); }");
|
|
sw.WriteLine ("extern \"C\" { void mono_marshal_ilgen_init (void); }");
|
|
sw.WriteLine ("extern \"C\" { void mono_method_builder_ilgen_init (void); }");
|
|
sw.WriteLine ("extern \"C\" { void mono_sgen_mono_ilgen_init (void); }");
|
|
}
|
|
|
|
#if NET
|
|
if (app.MonoNativeMode != MonoNativeMode.None) {
|
|
sw.WriteLine ("static const char *xamarin_runtime_libraries_array[] = {");
|
|
foreach (var lib in app.MonoLibraries)
|
|
sw.WriteLine ($"\t\"{Path.GetFileNameWithoutExtension (lib)}\",");
|
|
sw.WriteLine ($"\tNULL");
|
|
sw.WriteLine ("};");
|
|
}
|
|
#endif
|
|
|
|
sw.WriteLine ("void xamarin_setup_impl ()");
|
|
sw.WriteLine ("{");
|
|
|
|
if (app.EnableProfiling)
|
|
sw.WriteLine ("\txamarin_profiler_symbol = mono_profiler_init_log;");
|
|
|
|
if (app.EnableLLVMOnlyBitCode)
|
|
sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY);");
|
|
else if (app.UseInterpreter) {
|
|
sw.WriteLine ("\tmono_icall_table_init ();");
|
|
sw.WriteLine ("\tmono_marshal_ilgen_init ();");
|
|
sw.WriteLine ("\tmono_method_builder_ilgen_init ();");
|
|
sw.WriteLine ("\tmono_sgen_mono_ilgen_init ();");
|
|
#if !NET
|
|
sw.WriteLine ("\tmono_ee_interp_init (NULL);");
|
|
#endif
|
|
sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_INTERP);");
|
|
} else if (app.IsDeviceBuild) {
|
|
sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_FULL);");
|
|
} else if (app.Platform == ApplePlatform.MacCatalyst && ((abi & Abi.ARM64) == Abi.ARM64)) {
|
|
sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_FULL);");
|
|
} else if (app.IsSimulatorBuild && ((abi & Abi.ARM64) == Abi.ARM64)) {
|
|
sw.WriteLine ("\tmono_jit_set_aot_mode (MONO_AOT_MODE_FULL);");
|
|
}
|
|
|
|
if (assembly_location.Length > 0)
|
|
sw.WriteLine ("\txamarin_set_assembly_directories (&assembly_locations);");
|
|
|
|
sw.WriteLine ("\txamarin_invoke_registration_methods ();");
|
|
|
|
if (app.MonoNativeMode != MonoNativeMode.None) {
|
|
#if NET
|
|
// Mono doesn't support dllmaps for Mac Catalyst / macOS in .NET, so we're using an alternative:
|
|
// the PINVOKE_OVERRIDE runtime option. Since we have to use it for Mac Catalyst + macOS, let's
|
|
// just use it everywhere to simplify code. This means that at runtime we need to know how we
|
|
// linked to mono, so store that in the xamarin_libmono_native_link_mode variable.
|
|
// Ref: https://github.com/dotnet/runtime/issues/43204 (macOS) https://github.com/dotnet/runtime/issues/48110 (Mac Catalyst)
|
|
sw.WriteLine ($"\txamarin_libmono_native_link_mode = XamarinNativeLinkMode{app.LibMonoNativeLinkMode};");
|
|
sw.WriteLine ($"\txamarin_runtime_libraries = xamarin_runtime_libraries_array;");
|
|
#else
|
|
string mono_native_lib;
|
|
if (app.LibMonoNativeLinkMode == AssemblyBuildTarget.StaticObject) {
|
|
mono_native_lib = "__Internal";
|
|
} else {
|
|
mono_native_lib = app.GetLibNativeName () + ".dylib";
|
|
}
|
|
sw.WriteLine ();
|
|
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Native\", NULL, \"{mono_native_lib}\", NULL);");
|
|
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Security.Cryptography.Native.Apple\", NULL, \"{mono_native_lib}\", NULL);");
|
|
sw.WriteLine ($"\tmono_dllmap_insert (NULL, \"System.Net.Security.Native\", NULL, \"{mono_native_lib}\", NULL);");
|
|
sw.WriteLine ();
|
|
#endif
|
|
}
|
|
|
|
if (app.EnableDebug)
|
|
sw.WriteLine ("\txamarin_gc_pump = {0};", app.DebugTrack.Value ? "TRUE" : "FALSE");
|
|
sw.WriteLine ("\txamarin_init_mono_debug = {0};", app.PackageManagedDebugSymbols ? "TRUE" : "FALSE");
|
|
sw.WriteLine ("\txamarin_executable_name = \"{0}\";", assembly_name);
|
|
if (app.XamarinRuntime == XamarinRuntime.MonoVM)
|
|
sw.WriteLine ("\tmono_use_llvm = {0};", enable_llvm ? "TRUE" : "FALSE");
|
|
sw.WriteLine ("\txamarin_log_level = {0};", Driver.Verbosity.ToString (CultureInfo.InvariantCulture));
|
|
sw.WriteLine ("\txamarin_arch_name = \"{0}\";", abi.AsArchString ());
|
|
if (!app.IsDefaultMarshalManagedExceptionMode)
|
|
sw.WriteLine ("\txamarin_marshal_managed_exception_mode = MarshalManagedExceptionMode{0};", app.MarshalManagedExceptions);
|
|
sw.WriteLine ("\txamarin_marshal_objectivec_exception_mode = MarshalObjectiveCExceptionMode{0};", app.MarshalObjectiveCExceptions);
|
|
if (app.EnableDebug)
|
|
sw.WriteLine ("\txamarin_debug_mode = TRUE;");
|
|
if (!string.IsNullOrEmpty (app.MonoGCParams))
|
|
sw.WriteLine ("\tsetenv (\"MONO_GC_PARAMS\", \"{0}\", 1);", app.MonoGCParams);
|
|
// Do this last, so that the app developer can override any other environment variable we set.
|
|
foreach (var kvp in app.EnvironmentVariables)
|
|
sw.WriteLine ("\tsetenv (\"{0}\", \"{1}\", 1);", kvp.Key.Replace ("\"", "\\\""), kvp.Value.Replace ("\"", "\\\""));
|
|
sw.WriteLine ("\txamarin_supports_dynamic_registration = {0};", app.DynamicRegistrationSupported ? "TRUE" : "FALSE");
|
|
#if NET
|
|
sw.WriteLine ("\txamarin_runtime_configuration_name = {0};", string.IsNullOrEmpty (app.RuntimeConfigurationFile) ? "NULL" : $"\"{app.RuntimeConfigurationFile}\"");
|
|
#endif
|
|
sw.WriteLine ("}");
|
|
sw.WriteLine ();
|
|
sw.Write ("int ");
|
|
sw.Write (app.IsWatchExtension ? "xamarin_watchextension_main" : "main");
|
|
sw.WriteLine (" (int argc, char **argv)");
|
|
sw.WriteLine ("{");
|
|
sw.WriteLine ("\tNSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];");
|
|
if (app.IsExtension) {
|
|
// the name of the executable must be the bundle id (reverse dns notation)
|
|
// but we do not want to impose that (ugly) restriction to the managed .exe / project name / ...
|
|
sw.WriteLine ("\targv [0] = (char *) \"{0}\";", Path.GetFileNameWithoutExtension (app.RootAssemblies [0]));
|
|
sw.WriteLine ("\tint rv = xamarin_main (argc, argv, XamarinLaunchModeExtension);");
|
|
} else {
|
|
sw.WriteLine ("\tint rv = xamarin_main (argc, argv, XamarinLaunchModeApp);");
|
|
}
|
|
sw.WriteLine ("\t[pool drain];");
|
|
sw.WriteLine ("\treturn rv;");
|
|
sw.WriteLine ("}");
|
|
|
|
string extension_main = null;
|
|
if (app.Platform == ApplePlatform.WatchOS && app.IsWatchExtension) {
|
|
// We're building a watch extension, and we have multiple scenarios, depending on the watchOS version we're executing on:
|
|
//
|
|
// * watchOS 2.0 -> 5.*: we must call a `main` function provided in the WatchKit framework.
|
|
// * watchOS 6.0 -> * : we must call a `WKExtensionMain` function provided in the WatchKit framework.
|
|
// * watchOS 7.0 -> * : The `WKExtensionMain` function uses dlsym to find any `main` functions in the
|
|
// main executable, and calls that function (otherwise WKExtensionMain will call
|
|
// UIApplicationMain and normal startup occurs)
|
|
//
|
|
// * We can't call our entry point "main", because we call WKExtensionMain, and then we run into an
|
|
// infinite loop on watchOS 7.0. So we call it xamarin_watch_extension_main.
|
|
// * The watchOS 6+ SDK helpfully provides a static library (WKExtensionMainLegacy) that has a
|
|
// WKExtensionMain function, which we use when the deployment target is earlier than watchOS 6.0.
|
|
// This means that calling WKExtensionMain works everywhere (as long as we're using the
|
|
// watchOS 6+ SDK to build; otherwise we just call "main" directly and don't link with the
|
|
// WKExtensionMainLegacy library)
|
|
|
|
if (app.SdkVersion.Major >= 6) {
|
|
extension_main = "WKExtensionMain";
|
|
} else {
|
|
extension_main = "main";
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty (extension_main)) {
|
|
sw.WriteLine ($"extern \"C\" {{ int {extension_main} (int argc, char* argv[]); }}");
|
|
sw.WriteLine ();
|
|
}
|
|
|
|
sw.WriteLine ();
|
|
sw.WriteLine ("void xamarin_initialize_callbacks () __attribute__ ((constructor));");
|
|
sw.WriteLine ("void xamarin_initialize_callbacks ()");
|
|
sw.WriteLine ("{");
|
|
sw.WriteLine ("\txamarin_setup = xamarin_setup_impl;");
|
|
sw.WriteLine ("\txamarin_register_assemblies = xamarin_register_assemblies_impl;");
|
|
sw.WriteLine ("\txamarin_register_modules = xamarin_register_modules_impl;");
|
|
if (!string.IsNullOrEmpty (extension_main))
|
|
sw.WriteLine ($"\txamarin_extension_main = {extension_main};");
|
|
sw.WriteLine ("}");
|
|
}
|
|
|
|
static string EncodeAotSymbol (string symbol)
|
|
{
|
|
var sb = new StringBuilder ();
|
|
/* This mimics what the aot-compiler does */
|
|
foreach (var b in System.Text.Encoding.UTF8.GetBytes (symbol)) {
|
|
char c = (char) b;
|
|
if ((c >= '0' && c <= '9') ||
|
|
(c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z')) {
|
|
sb.Append (c);
|
|
continue;
|
|
}
|
|
sb.Append ('_');
|
|
}
|
|
return sb.ToString ();
|
|
}
|
|
|
|
static bool IsBoundAssembly (Assembly s)
|
|
{
|
|
if (s.IsFrameworkAssembly == true)
|
|
return false;
|
|
|
|
AssemblyDefinition ad = s.AssemblyDefinition;
|
|
|
|
foreach (ModuleDefinition md in ad.Modules)
|
|
foreach (TypeDefinition td in md.Types)
|
|
if (td.IsNSObject (s.Target.LinkContext))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|