2017-01-19 14:42:27 +03:00
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading ;
using Xamarin.MacDev ;
using Xamarin.Utils ;
namespace Xamarin.Bundler
{
public abstract class ProcessTask : BuildTask
{
public ProcessStartInfo ProcessStartInfo ;
protected StringBuilder Output ;
protected string Command {
get {
var result = new StringBuilder ( ) ;
if ( ProcessStartInfo . EnvironmentVariables . ContainsKey ( "MONO_PATH" ) ) {
result . Append ( "MONO_PATH=" ) ;
result . Append ( ProcessStartInfo . EnvironmentVariables [ "MONO_PATH" ] ) ;
result . Append ( ' ' ) ;
}
result . Append ( ProcessStartInfo . FileName ) ;
result . Append ( ' ' ) ;
result . Append ( ProcessStartInfo . Arguments ) ;
return result . ToString ( ) ;
}
}
protected int Start ( )
{
if ( Driver . Verbosity > 0 )
Console . WriteLine ( Command ) ;
var info = ProcessStartInfo ;
var stdout_completed = new ManualResetEvent ( false ) ;
var stderr_completed = new ManualResetEvent ( false ) ;
Output = new StringBuilder ( ) ;
using ( var p = Process . Start ( info ) ) {
p . OutputDataReceived + = ( sender , e ) = >
{
if ( e . Data ! = null ) {
lock ( Output )
Output . AppendLine ( e . Data ) ;
} else {
stdout_completed . Set ( ) ;
}
} ;
p . ErrorDataReceived + = ( sender , e ) = >
{
if ( e . Data ! = null ) {
lock ( Output )
Output . AppendLine ( e . Data ) ;
} else {
stderr_completed . Set ( ) ;
}
} ;
p . BeginOutputReadLine ( ) ;
p . BeginErrorReadLine ( ) ;
p . WaitForExit ( ) ;
stderr_completed . WaitOne ( TimeSpan . FromSeconds ( 1 ) ) ;
stdout_completed . WaitOne ( TimeSpan . FromSeconds ( 1 ) ) ;
GC . Collect ( ) ; // Workaround for: https://bugzilla.xamarin.com/show_bug.cgi?id=43462#c14
if ( Driver . Verbosity > = 2 & & Output . Length > 0 )
Console . Error . WriteLine ( Output . ToString ( ) ) ;
2017-01-24 17:34:49 +03:00
return p . ExitCode ;
}
2017-01-19 14:42:27 +03:00
}
}
2017-01-24 17:21:41 +03:00
class GenerateMainTask : BuildTask
{
public Target Target ;
public Abi Abi ;
public string MainM ;
public IList < string > RegistrationMethods ;
public IEnumerable < string > Inputs {
get {
foreach ( var asm in Target . Assemblies )
yield return asm . FullPath ;
}
}
2017-01-25 14:25:47 +03:00
protected override void Execute ( )
2017-01-24 17:21:41 +03:00
{
Driver . GenerateMain ( Target . App , Target . Assemblies , Target . App . AssemblyName , Abi , MainM , RegistrationMethods ) ;
}
}
class CompileMainTask : CompileTask
2017-01-19 14:42:27 +03:00
{
2017-01-25 16:59:13 +03:00
protected override void CompilationFailed ( int exitCode )
2017-01-19 14:42:27 +03:00
{
2017-01-25 17:06:56 +03:00
throw ErrorHelper . CreateError ( 5103 , "Failed to compile the file(s) '{0}'. Please file a bug report at http://bugzilla.xamarin.com" , string . Join ( "', '" , CompilerFlags . SourceFiles . ToArray ( ) ) ) ;
2017-01-19 14:42:27 +03:00
}
}
2017-01-24 17:11:40 +03:00
class PinvokesTask : CompileTask
2017-01-19 14:42:27 +03:00
{
2017-01-25 16:59:13 +03:00
protected override void CompilationFailed ( int exitCode )
2017-01-19 14:42:27 +03:00
{
2017-01-25 16:59:13 +03:00
throw ErrorHelper . CreateError ( 4002 , "Failed to compile the generated code for P/Invoke methods. Please file a bug report at http://bugzilla.xamarin.com" ) ;
2017-01-19 14:42:27 +03:00
}
}
2017-01-25 14:06:42 +03:00
class RunRegistrarTask : BuildTask
{
public Target Target ;
public string RegistrarM ;
public string RegistrarH ;
2017-01-25 14:25:47 +03:00
protected override void Execute ( )
2017-01-25 14:06:42 +03:00
{
Target . StaticRegistrar . Generate ( Target . Assemblies . Select ( ( a ) = > a . AssemblyDefinition ) , RegistrarH , RegistrarM ) ;
}
}
2017-01-24 17:13:39 +03:00
class CompileRegistrarTask : CompileTask
2017-01-19 14:42:27 +03:00
{
2017-01-25 16:59:13 +03:00
protected override void CompilationFailed ( int exitCode )
2017-01-19 14:42:27 +03:00
{
2017-01-25 16:59:13 +03:00
throw ErrorHelper . CreateError ( 4109 , "Failed to compile the generated registrar code. Please file a bug report at http://bugzilla.xamarin.com" ) ;
2017-01-19 14:42:27 +03:00
}
}
public class AOTTask : ProcessTask
{
public string AssemblyName ;
public bool AddBitcodeMarkerSection ;
public string AssemblyPath ; // path to the .s file.
// executed with Parallel.ForEach
2017-01-25 14:25:47 +03:00
protected override void Execute ( )
2017-01-19 14:42:27 +03:00
{
var exit_code = base . Start ( ) ;
if ( exit_code = = 0 ) {
if ( AddBitcodeMarkerSection )
File . AppendAllText ( AssemblyPath , @ "
. section __LLVM , __bitcode
. byte 0
. section __LLVM , __cmdline
. byte 0
");
return ;
}
Console . Error . WriteLine ( "AOT Compilation exited with code {0}, command:\n{1}{2}" , exit_code , Command , Output . Length > 0 ? ( "\n" + Output . ToString ( ) ) : string . Empty ) ;
if ( Output . Length > 0 ) {
List < Exception > exceptions = new List < Exception > ( ) ;
foreach ( var line in Output . ToString ( ) . Split ( '\n' ) ) {
if ( line . StartsWith ( "AOT restriction: Method '" , StringComparison . Ordinal ) & & line . Contains ( "must be static since it is decorated with [MonoPInvokeCallback]" ) ) {
exceptions . Add ( new MonoTouchException ( 3002 , true , line ) ) ;
}
}
if ( exceptions . Count > 0 )
throw new AggregateException ( exceptions . ToArray ( ) ) ;
}
throw new MonoTouchException ( 3001 , true , "Could not AOT the assembly '{0}'" , AssemblyName ) ;
}
}
2017-01-25 15:11:20 +03:00
public class NativeLinkTask : BuildTask
{
public Target Target ;
public string OutputFile ;
public CompilerFlags CompilerFlags ;
protected override void Execute ( )
{
// always show the native linker warnings since many of them turn out to be very important
// and very hard to diagnose otherwise when hidden from the build output. Ref: bug #2430
var linker_errors = new List < Exception > ( ) ;
var output = new StringBuilder ( ) ;
var code = Driver . RunCommand ( Target . App . CompilerPath , CompilerFlags . ToString ( ) , null , output ) ;
Application . ProcessNativeLinkerOutput ( Target , output . ToString ( ) , CompilerFlags . AllLibraries , linker_errors , code ! = 0 ) ;
if ( code ! = 0 ) {
// if the build failed - it could be because of missing frameworks / libraries we identified earlier
foreach ( var assembly in Target . Assemblies ) {
if ( assembly . UnresolvedModuleReferences = = null )
continue ;
foreach ( var mr in assembly . UnresolvedModuleReferences ) {
// TODO: add more diagnose information on the warnings
var name = Path . GetFileNameWithoutExtension ( mr . Name ) ;
linker_errors . Add ( new MonoTouchException ( 5215 , false , "References to '{0}' might require additional -framework=XXX or -lXXX instructions to the native linker" , name ) ) ;
}
}
// mtouch does not validate extra parameters given to GCC when linking (--gcc_flags)
if ( ! String . IsNullOrEmpty ( Target . App . UserGccFlags ) )
linker_errors . Add ( new MonoTouchException ( 5201 , true , "Native linking failed. Please review the build log and the user flags provided to gcc: {0}" , Target . App . UserGccFlags ) ) ;
linker_errors . Add ( new MonoTouchException ( 5202 , true , "Native linking failed. Please review the build log." , Target . App . UserGccFlags ) ) ;
}
ErrorHelper . Show ( linker_errors ) ;
// the native linker can prefer private (and existing) over public (but non-existing) framework when weak_framework are used
// on an iOS target version where the framework does not exists, e.g. targeting iOS6 for JavaScriptCore added in iOS7 results in
// /System/Library/PrivateFrameworks/JavaScriptCore.framework/JavaScriptCore instead of
// /System/Library/Frameworks/JavaScriptCore.framework/JavaScriptCore
// more details in https://bugzilla.xamarin.com/show_bug.cgi?id=31036
if ( Target . WeakFrameworks . Count > 0 )
Target . AdjustDylibs ( ) ;
Driver . Watch ( "Native Link" , 1 ) ;
}
public void Link ( )
{
Execute ( ) ;
}
}
2017-01-19 14:42:27 +03:00
public class LinkTask : CompileTask
{
2017-01-25 16:59:13 +03:00
protected override void CompilationFailed ( int exitCode )
{
throw ErrorHelper . CreateError ( 5216 , "Native linking failed for '{0}'. Please file a bug report at http://bugzilla.xamarin.com" , OutputFile ) ;
}
2017-01-19 14:42:27 +03:00
}
public class CompileTask : BuildTask
{
public Target Target ;
public Application App { get { return Target . App ; } }
public bool SharedLibrary ;
public string OutputFile ;
public Abi Abi ;
public string AssemblyName ;
public string InstallName ;
public string Language ;
2017-01-24 20:26:36 +03:00
public bool IsAssembler {
get {
return Language = = "assembler" ;
}
}
2017-01-25 17:06:56 +03:00
public string InputFile {
set {
// This is an accumulative setter-only property,
// to make it possible add dependencies using object initializers.
CompilerFlags . AddSourceFile ( value ) ;
}
}
2017-01-19 14:42:27 +03:00
CompilerFlags compiler_flags ;
public CompilerFlags CompilerFlags {
get { return compiler_flags ? ? ( compiler_flags = new CompilerFlags ( ) { Target = Target } ) ; }
set { compiler_flags = value ; }
}
public static void GetArchFlags ( CompilerFlags flags , params Abi [ ] abis )
{
GetArchFlags ( flags , ( IEnumerable < Abi > ) abis ) ;
}
public static void GetArchFlags ( CompilerFlags flags , IEnumerable < Abi > abis )
{
bool enable_thumb = false ;
foreach ( var abi in abis ) {
var arch = abi . AsArchString ( ) ;
flags . AddOtherFlag ( $"-arch {arch}" ) ;
enable_thumb | = ( abi & Abi . Thumb ) ! = 0 ;
}
if ( enable_thumb )
flags . AddOtherFlag ( "-mthumb" ) ;
}
2017-01-24 20:26:36 +03:00
public static void GetCompilerFlags ( Application app , CompilerFlags flags , bool is_assembler , string language = null )
2017-01-19 14:42:27 +03:00
{
2017-01-24 20:26:36 +03:00
if ( ! is_assembler )
2017-01-19 14:42:27 +03:00
flags . AddOtherFlag ( "-gdwarf-2" ) ;
2017-01-24 20:26:36 +03:00
if ( ! is_assembler ) {
2017-01-19 14:42:27 +03:00
if ( string . IsNullOrEmpty ( language ) | | ! language . Contains ( "++" ) ) {
// error: invalid argument '-std=c99' not allowed with 'C++/ObjC++'
flags . AddOtherFlag ( "-std=c99" ) ;
}
flags . AddOtherFlag ( $"-I{Driver.Quote (Path.Combine (Driver.GetProductSdkDirectory (app), " usr ", " include "))}" ) ;
}
flags . AddOtherFlag ( $"-isysroot {Driver.Quote (Driver.GetFrameworkDirectory (app))}" ) ;
flags . AddOtherFlag ( "-Qunused-arguments" ) ; // don't complain about unused arguments (clang reports -std=c99 and -Isomething as unused).
}
2017-01-24 20:26:36 +03:00
public static void GetSimulatorCompilerFlags ( CompilerFlags flags , bool is_assembler , Application app , string language = null )
2017-01-19 14:42:27 +03:00
{
2017-01-24 20:26:36 +03:00
GetCompilerFlags ( app , flags , is_assembler , language ) ;
2017-01-19 14:42:27 +03:00
string sim_platform = Driver . GetPlatformDirectory ( app ) ;
string plist = Path . Combine ( sim_platform , "Info.plist" ) ;
var dict = Driver . FromPList ( plist ) ;
var dp = dict . Get < PDictionary > ( "DefaultProperties" ) ;
if ( dp . GetString ( "GCC_OBJC_LEGACY_DISPATCH" ) = = "YES" )
flags . AddOtherFlag ( "-fobjc-legacy-dispatch" ) ;
string objc_abi = dp . GetString ( "OBJC_ABI_VERSION" ) ;
if ( ! String . IsNullOrWhiteSpace ( objc_abi ) )
flags . AddOtherFlag ( $"-fobjc-abi-version={objc_abi}" ) ;
plist = Path . Combine ( Driver . GetFrameworkDirectory ( app ) , "SDKSettings.plist" ) ;
string min_prefix = app . CompilerPath . Contains ( "clang" ) ? Driver . GetTargetMinSdkName ( app ) : "iphoneos" ;
dict = Driver . FromPList ( plist ) ;
dp = dict . Get < PDictionary > ( "DefaultProperties" ) ;
if ( app . DeploymentTarget = = new Version ( ) ) {
string target = dp . GetString ( "IPHONEOS_DEPLOYMENT_TARGET" ) ;
if ( ! String . IsNullOrWhiteSpace ( target ) )
flags . AddOtherFlag ( $"-m{min_prefix}-version-min={target}" ) ;
} else {
flags . AddOtherFlag ( $"-m{min_prefix}-version-min={app.DeploymentTarget}" ) ;
}
string defines = dp . GetString ( "GCC_PRODUCT_TYPE_PREPROCESSOR_DEFINITIONS" ) ;
if ( ! String . IsNullOrWhiteSpace ( defines ) )
flags . AddDefine ( defines . Replace ( " " , String . Empty ) ) ;
}
2017-01-24 20:26:36 +03:00
void GetDeviceCompilerFlags ( CompilerFlags flags , bool is_assembler )
2017-01-19 14:42:27 +03:00
{
2017-01-24 20:26:36 +03:00
GetCompilerFlags ( App , flags , is_assembler , Language ) ;
2017-01-19 14:42:27 +03:00
flags . AddOtherFlag ( $"-m{Driver.GetTargetMinSdkName (App)}-version-min={App.DeploymentTarget.ToString ()}" ) ;
}
void GetSharedCompilerFlags ( CompilerFlags flags , string install_name )
{
if ( string . IsNullOrEmpty ( install_name ) )
throw new ArgumentNullException ( nameof ( install_name ) ) ;
flags . AddOtherFlag ( "-shared" ) ;
if ( ! App . EnableMarkerOnlyBitCode )
flags . AddOtherFlag ( "-read_only_relocs suppress" ) ;
flags . LinkWithMono ( ) ;
2017-01-24 22:24:32 +03:00
flags . AddOtherFlag ( "-install_name " + Driver . Quote ( $"@rpath/{install_name}" ) ) ;
2017-01-19 14:42:27 +03:00
flags . AddOtherFlag ( "-fapplication-extension" ) ; // fixes this: warning MT5203: Native linking warning: warning: linking against dylib not safe for use in application extensions: [..]/actionextension.dll.arm64.dylib
}
void GetStaticCompilerFlags ( CompilerFlags flags )
{
flags . AddOtherFlag ( "-c" ) ;
}
void GetBitcodeCompilerFlags ( CompilerFlags flags )
{
flags . AddOtherFlag ( App . EnableMarkerOnlyBitCode ? "-fembed-bitcode-marker" : "-fembed-bitcode" ) ;
}
2017-01-25 14:25:47 +03:00
protected override void Execute ( )
2017-01-19 14:42:27 +03:00
{
2017-01-25 16:59:13 +03:00
int exitCode = Compile ( ) ;
if ( exitCode ! = 0 )
CompilationFailed ( exitCode ) ;
}
protected virtual void CompilationFailed ( int exitCode )
{
throw ErrorHelper . CreateError ( 3001 , "Could not AOT the assembly '{0}'" , AssemblyName ) ;
2017-01-19 14:42:27 +03:00
}
public int Compile ( )
{
if ( App . IsDeviceBuild ) {
2017-01-24 20:26:36 +03:00
GetDeviceCompilerFlags ( CompilerFlags , IsAssembler ) ;
2017-01-19 14:42:27 +03:00
} else {
2017-01-24 20:26:36 +03:00
GetSimulatorCompilerFlags ( CompilerFlags , IsAssembler , App , Language ) ;
2017-01-19 14:42:27 +03:00
}
if ( App . EnableBitCode )
GetBitcodeCompilerFlags ( CompilerFlags ) ;
GetArchFlags ( CompilerFlags , Abi ) ;
if ( SharedLibrary ) {
GetSharedCompilerFlags ( CompilerFlags , InstallName ) ;
} else {
GetStaticCompilerFlags ( CompilerFlags ) ;
}
if ( App . EnableDebug )
CompilerFlags . AddDefine ( "DEBUG" ) ;
CompilerFlags . AddOtherFlag ( $"-o {Driver.Quote (OutputFile)}" ) ;
if ( ! string . IsNullOrEmpty ( Language ) )
CompilerFlags . AddOtherFlag ( $"-x {Language}" ) ;
var rv = Driver . RunCommand ( App . CompilerPath , CompilerFlags . ToString ( ) , null , null ) ;
return rv ;
}
}
2017-01-24 17:09:32 +03:00
public class BitCodeifyTask : BuildTask
2017-01-19 14:42:27 +03:00
{
public string Input { get ; set ; }
public string OutputFile { get ; set ; }
public ApplePlatform Platform { get ; set ; }
public Abi Abi { get ; set ; }
public Version DeploymentTarget { get ; set ; }
2017-01-25 14:25:47 +03:00
protected override void Execute ( )
2017-01-19 14:42:27 +03:00
{
new BitcodeConverter ( Input , OutputFile , Platform , Abi , DeploymentTarget ) . Convert ( ) ;
}
}
}