2020-09-24 15:57:05 +03:00
#pragma warning disable 0649 // Field 'X' is never assigned to, and will always have its default value null
2017-07-10 13:46:31 +03:00
using System ;
using System.Collections.Generic ;
using System.IO ;
2017-11-20 16:55:16 +03:00
using System.Linq ;
2017-07-10 13:46:31 +03:00
using System.Text ;
using NUnit.Framework ;
2017-11-20 16:55:16 +03:00
using Mono.Cecil ;
using Mono.Cecil.Cil ;
2017-07-10 13:46:31 +03:00
using Xamarin.Utils ;
2023-05-04 08:39:56 +03:00
#nullable enable
2022-11-18 01:04:21 +03:00
namespace Xamarin.Tests {
class BGenTool : Tool {
2020-09-24 15:57:05 +03:00
public const string None = "None" ;
2023-05-04 08:39:56 +03:00
AssemblyDefinition ? assembly ;
2017-11-20 16:55:16 +03:00
2017-07-10 13:46:31 +03:00
public Profile Profile ;
2019-03-19 20:06:02 +03:00
public bool InProcess = true ; // if executed using an in-process bgen. Ignored if using the classic bgen (for XM/Classic), in which case we'll always use the out-of-process bgen.
2017-11-13 19:54:21 +03:00
public bool ProcessEnums ;
2017-07-10 13:46:31 +03:00
public List < string > ApiDefinitions = new List < string > ( ) ;
2017-11-20 16:55:16 +03:00
public List < string > Sources = new List < string > ( ) ;
2018-05-04 19:02:39 +03:00
public List < string > References = new List < string > ( ) ;
2022-02-23 11:36:43 +03:00
#if NET
2023-05-04 08:39:56 +03:00
public List < string > ? CompileCommand = null ;
2022-02-23 11:36:43 +03:00
#endif
2018-05-04 19:02:39 +03:00
2020-09-24 15:57:05 +03:00
// If BaseLibrary and AttributeLibrary are null, we calculate a default value
#if NET
2023-05-04 08:39:56 +03:00
public string? BaseLibrary ;
public string? AttributeLibrary ;
2020-09-24 15:57:05 +03:00
public bool ReferenceBclByDefault = true ;
#else
public string BaseLibrary = None ;
public string AttributeLibrary = None ;
public bool ReferenceBclByDefault = false ;
#endif
2023-05-04 08:39:56 +03:00
public string [ ] ? Defines ;
public string? TmpDirectory ;
public string? ResponseFile ;
public string? WarnAsError ; // Set to empty string to pass /warnaserror, set to non-empty string to pass /warnaserror:<nonemptystring>
public string? NoWarn ; // Set to empty string to pass /nowarn, set to non-empty string to pass /nowarn:<nonemptystring>
public string? Out ;
2020-09-24 15:57:05 +03:00
public int Verbosity = 1 ;
2017-07-10 13:46:31 +03:00
2018-02-02 09:09:04 +03:00
protected override string ToolPath { get { return Profile = = Profile . macOSClassic ? Configuration . BGenClassicPath : Configuration . BGenPath ; } }
2017-07-10 13:46:31 +03:00
protected override string MessagePrefix { get { return "BI" ; } }
2018-02-02 09:09:04 +03:00
protected override string MessageToolName { get { return Profile = = Profile . macOSClassic ? "bgen-classic" : "bgen" ; } }
2017-07-10 13:46:31 +03:00
2017-11-20 16:55:16 +03:00
public BGenTool ( )
{
2023-08-18 17:04:59 +03:00
if ( Environment . OSVersion . Platform = = PlatformID . Win32NT ) {
EnvironmentVariables = new Dictionary < string , string > ( ) ;
} else {
EnvironmentVariables = new Dictionary < string , string > {
{ "MD_MTOUCH_SDK_ROOT" , Configuration . SdkRootXI } ,
{ "XamarinMacFrameworkRoot" , Configuration . SdkRootXM } ,
} ;
}
2017-11-20 16:55:16 +03:00
}
public void AddTestApiDefinition ( string filename )
{
ApiDefinitions . Add ( Path . Combine ( Configuration . SourceRoot , "tests" , "generator" , filename ) ) ;
}
public AssemblyDefinition ApiAssembly {
get {
2023-05-04 08:39:56 +03:00
return LoadAssembly ( ) ;
2017-11-20 16:55:16 +03:00
}
}
2022-10-14 11:01:59 +03:00
public static string GetTargetFramework ( Profile profile )
2017-07-10 13:46:31 +03:00
{
2022-10-14 11:01:59 +03:00
switch ( profile ) {
2020-09-24 15:57:05 +03:00
#if NET
case Profile . iOS :
2022-10-14 11:01:59 +03:00
return TargetFramework . DotNet_iOS_String ;
2020-09-24 15:57:05 +03:00
case Profile . tvOS :
2022-10-14 11:01:59 +03:00
return TargetFramework . DotNet_tvOS_String ;
2020-09-24 15:57:05 +03:00
case Profile . watchOS :
2022-10-14 11:01:59 +03:00
return TargetFramework . DotNet_watchOS_String ;
2023-01-25 11:27:58 +03:00
case Profile . MacCatalyst :
return TargetFramework . DotNet_MacCatalyst_String ;
2020-09-24 15:57:05 +03:00
case Profile . macOSMobile :
2022-10-14 11:01:59 +03:00
return TargetFramework . DotNet_macOS_String ;
2020-09-24 15:57:05 +03:00
case Profile . macOSFull :
case Profile . macOSSystem :
throw new InvalidOperationException ( $"Only the Mobile profile can be specified for .NET" ) ;
#else
2017-07-10 13:46:31 +03:00
case Profile . iOS :
2022-10-14 11:01:59 +03:00
return "Xamarin.iOS,v1.0" ;
2017-07-10 13:46:31 +03:00
case Profile . tvOS :
2022-10-14 11:01:59 +03:00
return "Xamarin.TVOS,v1.0" ;
2017-07-10 13:46:31 +03:00
case Profile . watchOS :
2022-10-14 11:01:59 +03:00
return "Xamarin.WatchOS,v1.0" ;
2018-02-02 09:09:04 +03:00
case Profile . macOSClassic :
2022-10-14 11:01:59 +03:00
return "XamMac,v1.0" ;
2018-02-02 09:09:04 +03:00
case Profile . macOSFull :
2022-10-14 11:01:59 +03:00
return "Xamarin.Mac,Version=v4.5,Profile=Full" ;
2018-02-02 09:09:04 +03:00
case Profile . macOSMobile :
2022-10-14 11:01:59 +03:00
return "Xamarin.Mac,Version=v2.0,Profile=Mobile" ;
2018-02-02 09:09:04 +03:00
case Profile . macOSSystem :
2022-10-14 11:01:59 +03:00
return "Xamarin.Mac,Version=v4.5,Profile=System" ;
#endif
2017-07-10 13:46:31 +03:00
default :
2022-10-14 11:01:59 +03:00
throw new NotImplementedException ( $"Profile: {profile}" ) ;
2017-07-10 13:46:31 +03:00
}
2022-10-14 11:01:59 +03:00
}
string [ ] BuildArgumentArray ( )
{
var sb = new List < string > ( ) ;
2023-05-04 08:39:56 +03:00
var targetFramework = ( string? ) null ;
2022-10-14 11:01:59 +03:00
if ( Profile ! = Profile . None )
targetFramework = GetTargetFramework ( Profile ) ;
2020-09-24 15:57:05 +03:00
2022-02-23 11:36:43 +03:00
#if NET
if ( CompileCommand is null ) {
if ( ! StringUtils . TryParseArguments ( Configuration . DotNetCscCommand , out var args , out var ex ) )
throw new InvalidOperationException ( $"Unable to parse the .NET csc command '{Configuration.DotNetCscCommand}': {ex.Message}" ) ;
CompileCommand = new List < string > ( args ) ;
}
if ( CompileCommand . Count > 0 ) {
sb . Add ( $"--compile-command" ) ;
sb . Add ( string . Join ( " " , StringUtils . QuoteForProcess ( CompileCommand . ToArray ( ) ) ) ) ;
}
#endif
2020-09-24 15:57:05 +03:00
TargetFramework ? tf = null ;
2023-05-04 08:39:56 +03:00
if ( targetFramework is not null )
2020-09-24 15:57:05 +03:00
tf = TargetFramework . Parse ( targetFramework ) ;
2023-05-04 08:39:56 +03:00
if ( BaseLibrary is null ) {
2020-09-24 15:57:05 +03:00
if ( tf . HasValue )
sb . Add ( $"--baselib={Configuration.GetBaseLibrary (tf.Value)}" ) ;
} else if ( BaseLibrary ! = None ) {
sb . Add ( $"--baselib={BaseLibrary}" ) ;
}
2023-05-04 08:39:56 +03:00
if ( AttributeLibrary is null ) {
2020-09-24 15:57:05 +03:00
if ( tf . HasValue )
sb . Add ( $"--attributelib={Configuration.GetBindingAttributePath (tf.Value)}" ) ;
} else if ( AttributeLibrary ! = None ) {
sb . Add ( $"--attributelib={AttributeLibrary}" ) ;
}
2017-07-10 13:46:31 +03:00
if ( ! string . IsNullOrEmpty ( targetFramework ) )
2019-03-19 20:06:02 +03:00
sb . Add ( $"--target-framework={targetFramework}" ) ;
2017-11-20 16:55:16 +03:00
2017-07-10 13:46:31 +03:00
foreach ( var ad in ApiDefinitions )
2019-03-19 20:06:02 +03:00
sb . Add ( $"--api={ad}" ) ;
2017-11-20 16:55:16 +03:00
foreach ( var s in Sources )
2019-03-19 20:06:02 +03:00
sb . Add ( $"-s={s}" ) ;
2017-11-20 16:55:16 +03:00
2020-09-24 15:57:05 +03:00
if ( ReferenceBclByDefault ) {
2023-05-04 08:39:56 +03:00
if ( tf is null ) {
2020-09-24 15:57:05 +03:00
// do nothing
} else if ( tf . Value . IsDotNet = = true ) {
2022-03-23 10:07:34 +03:00
References . AddRange ( Directory . GetFiles ( Configuration . DotNetBclDir , "*.dll" ) ) ;
2020-09-24 15:57:05 +03:00
} else {
throw new NotImplementedException ( "ReferenceBclByDefault" ) ;
}
}
2018-05-04 19:02:39 +03:00
foreach ( var r in References )
2019-03-19 20:06:02 +03:00
sb . Add ( $"-r={r}" ) ;
2018-05-04 19:02:39 +03:00
2017-07-10 13:46:31 +03:00
if ( ! string . IsNullOrEmpty ( TmpDirectory ) )
2019-03-19 20:06:02 +03:00
sb . Add ( $"--tmpdir={TmpDirectory}" ) ;
2017-07-10 13:46:31 +03:00
2017-11-16 18:02:34 +03:00
if ( ! string . IsNullOrEmpty ( ResponseFile ) )
2019-03-19 20:06:02 +03:00
sb . Add ( $"@{ResponseFile}" ) ;
if ( ! string . IsNullOrEmpty ( Out ) )
sb . Add ( $"--out={Out}" ) ;
2017-11-16 18:02:34 +03:00
2017-11-13 19:54:21 +03:00
if ( ProcessEnums )
2019-03-19 20:06:02 +03:00
sb . Add ( "--process-enums" ) ;
2017-11-13 19:54:21 +03:00
2023-05-04 08:39:56 +03:00
if ( Defines is not null ) {
2017-11-20 16:55:16 +03:00
foreach ( var d in Defines )
2019-03-19 20:06:02 +03:00
sb . Add ( $"-d={d}" ) ;
2017-11-20 16:55:16 +03:00
}
2023-05-04 08:39:56 +03:00
if ( WarnAsError is not null ) {
2019-03-19 20:06:02 +03:00
var arg = "--warnaserror" ;
2017-11-20 16:55:16 +03:00
if ( WarnAsError . Length > 0 )
2019-03-19 20:06:02 +03:00
arg + = ":" + WarnAsError ;
sb . Add ( arg ) ;
2017-11-20 16:55:16 +03:00
}
2023-05-04 08:39:56 +03:00
if ( NoWarn is not null ) {
2019-03-19 20:06:02 +03:00
var arg = "--nowarn" ;
2017-11-20 16:55:16 +03:00
if ( NoWarn . Length > 0 )
2019-03-19 20:06:02 +03:00
arg + = ":" + NoWarn ;
sb . Add ( arg ) ;
2017-11-20 16:55:16 +03:00
}
2020-09-24 15:57:05 +03:00
if ( Verbosity ! = 0 )
sb . Add ( "-" + new string ( Verbosity > 0 ? 'v' : 'q' , Math . Abs ( Verbosity ) ) ) ;
2019-03-19 20:06:02 +03:00
return sb . ToArray ( ) ;
2017-07-10 13:46:31 +03:00
}
public void AssertExecute ( string message )
{
2019-03-19 20:06:02 +03:00
Assert . AreEqual ( 0 , Execute ( ) , message ) ;
2017-07-10 13:46:31 +03:00
}
public void AssertExecuteError ( string message )
{
2019-03-19 20:06:02 +03:00
Assert . AreNotEqual ( 0 , Execute ( ) , message ) ;
}
int Execute ( )
{
var arguments = BuildArgumentArray ( ) ;
var in_process = InProcess & & Profile ! = Profile . macOSClassic ;
if ( in_process ) {
2019-04-11 09:35:21 +03:00
int rv ;
2023-05-04 08:39:56 +03:00
var previous_environment = new Dictionary < string , string? > ( ) ;
2019-05-31 00:19:26 +03:00
foreach ( var kvp in EnvironmentVariables ) {
previous_environment [ kvp . Key ] = Environment . GetEnvironmentVariable ( kvp . Key ) ;
Environment . SetEnvironmentVariable ( kvp . Key , kvp . Value ) ;
}
2019-04-11 09:35:21 +03:00
ThreadStaticTextWriter . ReplaceConsole ( Output ) ;
try {
rv = BindingTouch . Main ( arguments ) ;
} finally {
ThreadStaticTextWriter . RestoreConsole ( ) ;
2019-05-31 00:19:26 +03:00
foreach ( var kvp in previous_environment ) {
Environment . SetEnvironmentVariable ( kvp . Key , kvp . Value ) ;
}
2019-04-11 09:35:21 +03:00
}
2019-03-19 20:06:02 +03:00
Console . WriteLine ( Output ) ;
ParseMessages ( ) ;
return rv ;
}
2019-10-14 17:18:46 +03:00
return Execute ( arguments , always_show_output : true ) ;
2017-07-10 13:46:31 +03:00
}
2017-11-20 16:55:16 +03:00
public void AssertApiCallsMethod ( string caller_namespace , string caller_type , string caller_method , string @called_method , string message )
{
var type = ApiAssembly . MainModule . GetType ( caller_namespace , caller_type ) ;
var method = type . Methods . First ( ( v ) = > v . Name = = caller_method ) ;
AssertApiCallsMethod ( method , called_method , message ) ;
}
public void AssertApiCallsMethod ( MethodReference method , string called_method , string message )
{
var instructions = method . Resolve ( ) . Body . Instructions ;
foreach ( var ins in instructions ) {
if ( ins . OpCode . FlowControl ! = FlowControl . Call )
continue ;
var mr = ins . Operand as MethodReference ;
2023-05-04 08:39:56 +03:00
if ( mr is null )
2017-11-20 16:55:16 +03:00
continue ;
if ( mr . Name = = called_method )
return ;
}
Assert . Fail ( $"Could not find any instructions calling {called_method} in {method.FullName}: {message}\n\t{string.Join (" \ n \ t ", instructions)}" ) ;
}
public void AssertApiLoadsField ( string caller_type , string caller_method , string declaring_type , string field , string message )
{
var type = ApiAssembly . MainModule . GetTypes ( ) . First ( ( v ) = > v . FullName = = caller_type ) ;
var method = type . Methods . First ( ( v ) = > v . Name = = caller_method ) ;
AssertApiLoadsField ( method , declaring_type , field , message ) ;
}
public void AssertApiLoadsField ( MethodReference method , string declaring_type , string field , string message )
{
var instructions = method . Resolve ( ) . Body . Instructions ;
foreach ( var ins in instructions ) {
if ( ins . OpCode . Code ! = Code . Ldsfld & & ins . OpCode . Code ! = Code . Ldfld )
continue ;
var fr = ins . Operand as FieldReference ;
2023-05-04 08:39:56 +03:00
if ( fr is null )
2017-11-20 16:55:16 +03:00
continue ;
if ( fr . DeclaringType . FullName ! = declaring_type )
continue ;
if ( fr . Name = = field )
return ;
}
Assert . Fail ( $"Could not find any instructions loading the field {declaring_type}.{field} in {method.FullName}: {message}\n\t{string.Join (" \ n \ t ", instructions)}" ) ;
}
2023-05-04 08:39:56 +03:00
public void AssertPublicTypeCount ( int count , string? message = null )
2017-11-20 16:55:16 +03:00
{
2023-05-04 08:39:56 +03:00
var assembly = LoadAssembly ( ) ;
2017-11-20 16:55:16 +03:00
var actual = assembly . MainModule . Types . Where ( ( v ) = > v . IsPublic | | v . IsNestedPublic ) ;
if ( actual . Count ( ) ! = count )
Assert . Fail ( $"Expected {count} public type(s), found {actual} public type(s). {message}\n\t{string.Join (" \ n \ t ", actual.Select ((v) => v.FullName).ToArray ())}" ) ;
}
2023-05-04 08:39:56 +03:00
public void AssertPublicMethodCount ( string typename , int count , string? message = null )
2017-11-20 16:55:16 +03:00
{
2023-05-04 08:39:56 +03:00
var assembly = LoadAssembly ( ) ;
2017-11-20 16:55:16 +03:00
2023-01-17 19:05:03 +03:00
var t = assembly . MainModule . Types . First ( ( v ) = > v . FullName = = typename ) ;
var actual = t . Methods . Where ( ( v ) = > {
2017-11-20 16:55:16 +03:00
if ( v . IsPrivate | | v . IsFamily | | v . IsFamilyAndAssembly )
return false ;
return true ;
} ) ;
2023-01-17 19:05:03 +03:00
if ( actual . Count ( ) ! = count ) {
Assert . Fail ( $"Expected {count} publicly accessible method(s) in {typename}, found {actual} publicly accessible method(s): {message}\n\t{string.Join (" \ n \ t ", actual.Select (v => v.FullName).OrderBy (v => v))}" ) ;
}
2017-11-20 16:55:16 +03:00
}
2023-05-04 08:39:56 +03:00
public void AssertType ( string fullname , TypeAttributes ? attributes = null , string? message = null )
2017-11-20 16:55:16 +03:00
{
2023-05-04 08:39:56 +03:00
var assembly = LoadAssembly ( ) ;
2017-11-20 16:55:16 +03:00
var allTypes = assembly . MainModule . GetTypes ( ) . ToArray ( ) ;
var t = allTypes . FirstOrDefault ( ( v ) = > v . FullName = = fullname ) ;
2023-05-04 08:39:56 +03:00
if ( t is null ) {
2017-11-20 16:55:16 +03:00
Assert . Fail ( $"No type named '{fullname}' in the generated assembly. {message}\nList of types:\n\t{string.Join (" \ n \ t ", allTypes.Select ((v) => v.FullName))}" ) ;
2023-05-04 08:39:56 +03:00
return ;
}
if ( attributes is not null )
2017-11-20 16:55:16 +03:00
Assert . AreEqual ( attributes . Value , t . Attributes , $"Incorrect attributes for type {fullname}." ) ;
}
2023-01-17 19:05:03 +03:00
public void AssertMethod ( string typename , string method , params string [ ] parameterTypes )
{
AssertMethod ( typename , method , null , null , parameterTypes ) ;
}
2023-05-04 08:39:56 +03:00
public void AssertMethod ( string typename , string method , string? returnType = null , params string [ ] parameterTypes )
2017-11-20 16:55:16 +03:00
{
AssertMethod ( typename , method , null , returnType , parameterTypes ) ;
}
2023-05-04 08:39:56 +03:00
public void AssertMethod ( string typename , string method , MethodAttributes ? attributes = null , string? returnType = null , params string [ ] parameterTypes )
2017-11-20 16:55:16 +03:00
{
2023-01-17 19:05:03 +03:00
var m = FindMethod ( typename , method , returnType , parameterTypes ) ;
if ( m is null ) {
Assert . Fail ( $"No method '{method}' with signature '{string.Join (" ' , ' ", parameterTypes)}' on the type '{typename}' was found." ) ;
return ;
}
if ( attributes . HasValue )
Assert . AreEqual ( attributes . Value , m . Attributes , "Attributes for {0}" , m . FullName ) ;
}
2023-06-05 11:42:33 +03:00
public void AssertNoMethod ( string typename , string method , string? returnType = null , params string [ ] parameterTypes )
2023-01-17 19:05:03 +03:00
{
var m = FindMethod ( typename , method , returnType , parameterTypes ) ;
if ( m is not null )
Assert . Fail ( $"Unexpectedly found method '{method}' with signature '{string.Join (" ' , ' ", parameterTypes)}' on the type '{typename}'." ) ;
}
2023-06-05 11:42:33 +03:00
MethodDefinition ? FindMethod ( string typename , string method , string? returnType , params string [ ] parameterTypes )
2023-01-17 19:05:03 +03:00
{
var assembly = LoadAssembly ( ) ;
var t = assembly . MainModule . Types . FirstOrDefault ( ( v ) = > v . FullName = = typename ) ;
if ( t is null )
return null ;
2017-11-20 16:55:16 +03:00
var m = t . Methods . FirstOrDefault ( ( v ) = > {
if ( v . Name ! = method )
return false ;
if ( v . Parameters . Count ! = parameterTypes . Length )
return false ;
for ( int i = 0 ; i < v . Parameters . Count ; i + + )
if ( v . Parameters [ i ] . ParameterType . FullName ! = parameterTypes [ i ] )
return false ;
return true ;
} ) ;
2023-01-17 19:05:03 +03:00
return m ;
2017-11-20 16:55:16 +03:00
}
2023-01-17 19:05:03 +03:00
AssemblyDefinition LoadAssembly ( )
2017-11-20 16:55:16 +03:00
{
2022-12-16 17:20:06 +03:00
if ( assembly is null ) {
var parameters = new ReaderParameters ( ) ;
var resolver = new DefaultAssemblyResolver ( ) ;
2023-01-18 20:54:15 +03:00
#if NET
var searchdir = Path . GetDirectoryName ( Configuration . GetBaseLibrary ( Profile . AsPlatform ( ) , true ) ) ;
#else
2022-12-16 17:20:06 +03:00
var searchdir = Path . GetDirectoryName ( Configuration . GetBaseLibrary ( Profile ) ) ;
2023-01-18 20:54:15 +03:00
#endif
2022-12-16 17:20:06 +03:00
resolver . AddSearchDirectory ( searchdir ) ;
parameters . AssemblyResolver = resolver ;
2023-05-04 08:39:56 +03:00
var tmpDirectory = EnsureTempDir ( ) ;
assembly = AssemblyDefinition . ReadAssembly ( Out ? ? ( Path . Combine ( tmpDirectory , Path . GetFileNameWithoutExtension ( ApiDefinitions [ 0 ] ) . Replace ( '-' , '_' ) + ".dll" ) ) , parameters ) ;
2022-12-16 17:20:06 +03:00
}
2023-01-17 19:05:03 +03:00
return assembly ;
2017-11-20 16:55:16 +03:00
}
2023-05-04 08:39:56 +03:00
string EnsureTempDir ( )
2017-07-10 13:46:31 +03:00
{
2023-05-04 08:39:56 +03:00
if ( TmpDirectory is null )
2017-07-10 13:46:31 +03:00
TmpDirectory = Cache . CreateTemporaryDirectory ( ) ;
2023-05-04 08:39:56 +03:00
return TmpDirectory ;
2017-07-10 13:46:31 +03:00
}
2017-11-20 16:55:16 +03:00
public void CreateTemporaryBinding ( params string [ ] api_definition )
2017-07-10 13:46:31 +03:00
{
2023-05-04 08:39:56 +03:00
var tmpDirectory = EnsureTempDir ( ) ;
2017-11-20 16:55:16 +03:00
for ( int i = 0 ; i < api_definition . Length ; i + + ) {
2023-05-04 08:39:56 +03:00
var api = Path . Combine ( tmpDirectory , $"api{i}.cs" ) ;
2017-11-20 16:55:16 +03:00
File . WriteAllText ( api , api_definition [ i ] ) ;
ApiDefinitions . Add ( api ) ;
}
2017-07-10 13:46:31 +03:00
WorkingDirectory = TmpDirectory ;
2023-05-04 08:39:56 +03:00
Out = Path . Combine ( tmpDirectory , "api0.dll" ) ;
2017-07-10 13:46:31 +03:00
}
2017-11-20 16:55:16 +03:00
public static string [ ] GetDefaultDefines ( Profile profile )
{
switch ( profile ) {
2018-02-02 09:09:04 +03:00
case Profile . macOSFull :
case Profile . macOSMobile :
case Profile . macOSSystem :
2017-11-20 16:55:16 +03:00
return new string [ ] { "MONOMAC" , "XAMCORE_2_0" } ;
2018-02-02 09:09:04 +03:00
case Profile . macOSClassic :
2017-11-20 16:55:16 +03:00
return new string [ ] { "MONOMAC" } ;
case Profile . iOS :
return new string [ ] { "IOS" , "XAMCORE_2_0" } ;
2023-01-25 11:27:58 +03:00
case Profile . MacCatalyst :
return new string [ ] { "MACCATALYST" } ;
2017-11-20 16:55:16 +03:00
default :
throw new NotImplementedException ( profile . ToString ( ) ) ;
}
}
2017-07-10 13:46:31 +03:00
}
2019-04-11 09:35:21 +03:00
// This class will replace stdout/stderr with its own thread-static storage for stdout/stderr.
// This means we're capturing stdout/stderr per thread.
2022-11-18 01:04:21 +03:00
class ThreadStaticTextWriter : TextWriter {
2019-04-11 09:35:21 +03:00
[ThreadStatic]
2023-05-04 08:39:56 +03:00
static TextWriter ? current_writer ;
2019-04-11 09:35:21 +03:00
static ThreadStaticTextWriter instance = new ThreadStaticTextWriter ( ) ;
static object lock_obj = new object ( ) ;
static int counter ;
2023-05-04 08:39:56 +03:00
static TextWriter ? original_stdout ;
static TextWriter ? original_stderr ;
2019-04-11 09:35:21 +03:00
public static void ReplaceConsole ( StringBuilder sb )
{
2022-11-18 01:04:21 +03:00
lock ( lock_obj ) {
2019-04-11 09:35:21 +03:00
if ( counter = = 0 ) {
original_stdout = Console . Out ;
original_stderr = Console . Error ;
Console . SetOut ( instance ) ;
Console . SetError ( instance ) ;
}
counter + + ;
current_writer = new StringWriter ( sb ) ;
}
}
public static void RestoreConsole ( )
2022-11-18 01:04:21 +03:00
{
2019-04-11 09:35:21 +03:00
lock ( lock_obj ) {
2023-05-04 08:39:56 +03:00
current_writer ? . Dispose ( ) ;
2019-04-11 09:35:21 +03:00
current_writer = null ;
counter - - ;
if ( counter = = 0 ) {
2023-05-04 08:39:56 +03:00
Console . SetOut ( original_stdout ! ) ;
Console . SetError ( original_stderr ! ) ;
2019-04-11 09:35:21 +03:00
original_stdout = null ;
original_stderr = null ;
}
}
}
ThreadStaticTextWriter ( )
{
}
public TextWriter CurrentWriter {
get {
2021-09-20 14:39:24 +03:00
lock ( lock_obj ) {
2023-05-04 08:39:56 +03:00
if ( current_writer is null )
return original_stdout ? ? Console . Out ;
2021-09-20 14:39:24 +03:00
return current_writer ;
}
2019-04-11 09:35:21 +03:00
}
}
public override Encoding Encoding = > Encoding . UTF8 ;
public override void WriteLine ( )
{
lock ( lock_obj )
CurrentWriter . WriteLine ( ) ;
}
public override void Write ( char value )
{
lock ( lock_obj )
CurrentWriter . Write ( value ) ;
}
2023-05-04 08:39:56 +03:00
public override void Write ( string? value )
2019-04-11 09:35:21 +03:00
{
lock ( lock_obj )
CurrentWriter . Write ( value ) ;
}
2023-05-04 08:39:56 +03:00
public override void Write ( char [ ] ? buffer )
2019-04-11 09:35:21 +03:00
{
lock ( lock_obj )
CurrentWriter . Write ( buffer ) ;
}
2023-05-04 08:39:56 +03:00
public override void WriteLine ( string? value )
2019-04-11 09:35:21 +03:00
{
lock ( lock_obj )
CurrentWriter . WriteLine ( value ) ;
}
}
2017-07-10 13:46:31 +03:00
}