Merge branch 'objc' of github.com:mono/embeddinator-4000
This commit is contained in:
Коммит
bbdb54218c
43
docs/ObjC.md
43
docs/ObjC.md
|
@ -11,3 +11,46 @@ The use of Automatic Reference Counting (ARC) is **required** to call the genera
|
|||
### NSString support
|
||||
|
||||
API that expose `System.String` types are converted into `NSString`. This makes memory management easier than dealing with `char*`.
|
||||
|
||||
## Main differences with .NET
|
||||
|
||||
### Constructors v.s. Initializers
|
||||
|
||||
In Objective-C, you can call any of the initializer prototypes of any of the parent classes in the inheritance chain unless it is marked as unavailable (NS_UNAVAILABLE).
|
||||
|
||||
In C# you must explicitly declare a constructor member inside a class, this means constructors are not inherited.
|
||||
|
||||
In order to expose the right representation of the C# API to Objective-C, we add `NS_UNAVAILABLE` to any initializer that is not present in the child class from the parent class.
|
||||
|
||||
C# API:
|
||||
|
||||
```csharp
|
||||
public class Unique {
|
||||
public Unique () : this (1)
|
||||
{
|
||||
}
|
||||
|
||||
public Unique (int id)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class SuperUnique : Unique {
|
||||
public SuperUnique () : base (911)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Objective-C surfaced API:
|
||||
|
||||
```objectivec
|
||||
@interface SuperUnique : Unique
|
||||
|
||||
- (instancetype)initWithId:(int)id NS_UNAVAILABLE;
|
||||
- (instancetype)init;
|
||||
|
||||
@end
|
||||
```
|
||||
|
||||
Here we can see that `initWithId:` has been marked as unavailable.
|
|
@ -1,4 +1,4 @@
|
|||
id:{932C3F0C-D968-42D1-BB14-D97C73361983}
|
||||
id:{932C3F0C-D968-42D1-BB14-D97C73361983}
|
||||
title:Embeddinator-4000 errors
|
||||
|
||||
[//]: # (The original file resides under https://github.com/mono/Embeddinator-4000/tree/master/docs/error.md)
|
||||
|
@ -40,21 +40,103 @@ The tool does not support the target `X`. It is possible that another version of
|
|||
|
||||
The tool does not support the compilation target `X`. It is possible that another version of the tool supports it or that it does not apply in this environment.
|
||||
|
||||
<h3><a name="EM0006"/>EM0006: Could not find the Xcode location.</h3>
|
||||
|
||||
The tool could not find the currently selected Xcode location using the `xcode-select -p` command. Please verify that this command succeeds, and returns the correct Xcode location.
|
||||
|
||||
<h3><a name="EM0008"/>EM0008: The architecture '{arch}' is not valid for {platform}. Valid architectures for {platform} are: '{architectures}'.</h3>
|
||||
|
||||
The architecture in the error message is not valid for the targeted platform. Please verify that the --abi option is passed a valid architecture.
|
||||
|
||||
<h3><a name="EM0009"/>EM0009: The feature `X` is not currently implemented by the generator</h3>
|
||||
|
||||
This is a known issue that we intend to fix in a future release of the generator. Contributions are welcome.
|
||||
|
||||
<h3><a name="EM0099"/>EM0099: Internal error *. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).</h3>
|
||||
|
||||
This error message is reported when an internal consistency check in the Embeddinator-4000 fails.
|
||||
|
||||
This indicates a bug in the Embeddinator-4000; please file a bug report at [https://github.com/mono/Embeddinator-4000/issues](https://github.com/mono/Embeddinator-4000/issues) with a test case.
|
||||
|
||||
# EM1xxx: code generation
|
||||
|
||||
<h3><a name="EM1000"/>EM1000: The feature `X` is not currently implemented by the generator</h3>
|
||||
<!-- 1xxx: code processing -->
|
||||
|
||||
This is a known issue that we intend to fix in a future release of the generator. Contributions are welcome.
|
||||
# EM1xxx: Code Processing
|
||||
|
||||
<h3><a name="EM1010"/>Type `T` is not generated because `X` are not supported.</h3>
|
||||
|
||||
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses `X`, a feature that is not supported.
|
||||
|
||||
Note: Supported features will evolve with new versions of the tool.
|
||||
|
||||
|
||||
<h3><a name="EM1011"/>Type `T` is not generated because it lacks a native counterpart.</h3>
|
||||
|
||||
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses it expose something from the .NET framework that has no counterpart in the native platform.
|
||||
|
||||
|
||||
<h3><a name="EM1011"/>Type `T` is not generated because it lacks marshaling code with a native counterpart.</h3>
|
||||
|
||||
This is a **warning** that the type `T` will be ignored (i.e. nothing will be generated) because it uses it expose something from the .NET framework that requires extra marshaling.
|
||||
|
||||
Note: This is something that is might get supported, with some limitations, in a future version of the tool.
|
||||
|
||||
|
||||
<h3><a name="EM1020"/>Constructor `C` is not generated because of parameter type `T` is not supported.</h3>
|
||||
|
||||
This is a **warning** that the constructor `C` will be ignored (i.e. nothing will be generated) because a parameter of type `T` is not supported.
|
||||
|
||||
There should be an earlier warning giving more information why type `T` is not supported.
|
||||
|
||||
Note: Supported features will evolve with new versions of the tool.
|
||||
|
||||
|
||||
<h3><a name="EM1021"/>Constructor `C` parameter `P` has a default value that is not supported.</h3>
|
||||
|
||||
This is a **warning** that the default parameters of constructor `C` are not generating any extra code.
|
||||
|
||||
Note: Supported features will evolve with new versions of the tool.
|
||||
|
||||
|
||||
<h3><a name="EM1030"/>Method `M` is not generated because return type `T` is not supported.</h3>
|
||||
|
||||
This is a **warning** that the method `M` will be ignored (i.e. nothing will be generated) because it's return type `T` is not supported.
|
||||
|
||||
There should be an earlier warning giving more information why type `T` is not supported.
|
||||
|
||||
Note: Supported features will evolve with new versions of the tool.
|
||||
|
||||
|
||||
<h3><a name="EM1031"/>Method `M` is not generated because of parameter type `T` is not supported.</h3>
|
||||
|
||||
This is a **warning** that the method `M` will be ignored (i.e. nothing will be generated) because a parameter of type `T` is not supported.
|
||||
|
||||
There should be an earlier warning giving more information why type `T` is not supported.
|
||||
|
||||
Note: Supported features will evolve with new versions of the tool.
|
||||
|
||||
|
||||
<h3><a name="EM1032"/>Method `M` parameter `P` has a default value that is not supported.</h3>
|
||||
|
||||
This is a **warning** that the default parameters of method `M` are not generating any extra code.
|
||||
|
||||
Note: Supported features will evolve with new versions of the tool.
|
||||
|
||||
|
||||
<h3><a name="EM1040"/>Property `P` is not generated because of parameter type `T` is not supported.</h3>
|
||||
|
||||
This is a **warning** that the property `P` will be ignored (i.e. nothing will be generated) because the exposed type `T` is not supported.
|
||||
|
||||
There should be an earlier warning giving more information why type `T` is not supported.
|
||||
|
||||
Note: Supported features will evolve with new versions of the tool.
|
||||
|
||||
|
||||
<!-- 2xxx: code generation -->
|
||||
|
||||
# EM2xxx: Code Generation
|
||||
|
||||
<!-- 1xxx: code generation -->
|
||||
|
||||
<!-- 2xxx: reserved -->
|
||||
<!-- 3xxx: reserved -->
|
||||
<!-- 4xxx: reserved -->
|
||||
<!-- 5xxx: reserved -->
|
||||
|
|
|
@ -6,4 +6,4 @@ OBJCGEN_FILES := \
|
|||
|
||||
bin/Debug/objcgen.exe: $(OBJCGEN_FILES)
|
||||
nuget restore ../generator.sln
|
||||
xbuild objcgen.csproj
|
||||
msbuild objcgen.csproj
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using IKVM.Reflection;
|
||||
using Type = IKVM.Reflection.Type;
|
||||
|
@ -42,6 +43,7 @@ namespace Embeddinator {
|
|||
|
||||
public static class Driver
|
||||
{
|
||||
public static Embedder CurrentEmbedder { get; private set; }
|
||||
static int Main (string [] args)
|
||||
{
|
||||
try {
|
||||
|
@ -52,23 +54,31 @@ namespace Embeddinator {
|
|||
}
|
||||
}
|
||||
|
||||
public static int Main2 (string [] args)
|
||||
public static int Main2 (params string [] args)
|
||||
{
|
||||
var action = Action.None;
|
||||
var embedder = new Embedder ();
|
||||
|
||||
CurrentEmbedder = embedder;
|
||||
|
||||
var os = new OptionSet {
|
||||
{ "c|compile", "Compiles the generated output", v => CompileCode = true },
|
||||
{ "d|debug", "Build the native library with debug information.", v => Debug = true },
|
||||
{ "gen=", $"Target generator (default {TargetLanguage})", v => SetTarget (v) },
|
||||
{ "o|out|outdir=", "Output directory", v => OutputDirectory = v },
|
||||
{ "p|platform=", $"Target platform (iOS, macOS [default], watchOS, tvOS)", v => SetPlatform (v) },
|
||||
{ "dll|shared", "Compiles as a shared library (default)", v => CompilationTarget = CompilationTarget.SharedLibrary },
|
||||
{ "static", "Compiles as a static library (unsupported)", v => CompilationTarget = CompilationTarget.StaticLibrary },
|
||||
{ "c|compile", "Compiles the generated output", v => embedder.CompileCode = true },
|
||||
{ "d|debug", "Build the native library with debug information.", v => embedder.Debug = true },
|
||||
{ "gen=", $"Target generator (default {embedder.TargetLanguage})", v => embedder.SetTarget (v) },
|
||||
{ "abi=", "A comma-separated list of ABIs to compile. If not specified, all ABIs applicable to the selected platform will be built. Valid values (also depends on platform): i386, x86_64, armv7, armv7s, armv7k, arm64.", (v) =>
|
||||
{
|
||||
embedder.ABIs.AddRange (v.Split (',').Select ((a) => a.ToLowerInvariant ()));
|
||||
}
|
||||
},
|
||||
{ "o|out|outdir=", "Output directory", v => embedder.OutputDirectory = v },
|
||||
{ "p|platform=", $"Target platform (iOS, macOS [default], watchOS, tvOS)", v => embedder.SetPlatform (v) },
|
||||
{ "dll|shared", "Compiles as a shared library (default)", v => embedder.CompilationTarget = CompilationTarget.SharedLibrary },
|
||||
{ "static", "Compiles as a static library (unsupported)", v => embedder.CompilationTarget = CompilationTarget.StaticLibrary },
|
||||
{ "vs=", $"Visual Studio version for compilation (unsupported)", v => { throw new EmbeddinatorException (2, $"Option `--vs` is not supported"); } },
|
||||
{ "h|?|help", "Displays the help", v => action = Action.Help },
|
||||
{ "v|verbose", "generates diagnostic verbose output", v => ErrorHelper.Verbosity++ },
|
||||
{ "version", "Display the version information.", v => action = Action.Version },
|
||||
{ "target=", "The compilation target (static, shared, framework).", v => SetCompilationTarget (v) },
|
||||
{ "target=", "The compilation target (static, shared, framework).", v => embedder.SetCompilationTarget (v) },
|
||||
};
|
||||
|
||||
var assemblies = os.Parse (args);
|
||||
|
@ -91,22 +101,25 @@ namespace Embeddinator {
|
|||
return 0;
|
||||
case Action.Generate:
|
||||
try {
|
||||
var result = Generate (assemblies);
|
||||
if (CompileCode && (result == 0))
|
||||
result = Compile ();
|
||||
var result = embedder.Generate (assemblies);
|
||||
if (embedder.CompileCode && (result == 0))
|
||||
result = embedder.Compile ();
|
||||
Console.WriteLine ("Done");
|
||||
return result;
|
||||
} catch (NotImplementedException e) {
|
||||
throw new EmbeddinatorException (1000, $"The feature `{e.Message}` is not currently supported by the tool");
|
||||
throw new EmbeddinatorException (9, $"The feature `{e.Message}` is not currently supported by the tool");
|
||||
}
|
||||
default:
|
||||
throw ErrorHelper.CreateError (99, "Internal error: invalid action {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues)", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static string outputDirectory = ".";
|
||||
public class Embedder {
|
||||
|
||||
public static string OutputDirectory {
|
||||
string outputDirectory = ".";
|
||||
|
||||
public string OutputDirectory {
|
||||
get { return outputDirectory; }
|
||||
set {
|
||||
if (!Directory.Exists (value)) {
|
||||
|
@ -121,15 +134,17 @@ namespace Embeddinator {
|
|||
}
|
||||
}
|
||||
|
||||
static bool CompileCode { get; set; }
|
||||
public bool CompileCode { get; set; }
|
||||
|
||||
static bool Debug { get; set; }
|
||||
public bool Debug { get; set; }
|
||||
|
||||
static bool Shared { get { return CompilationTarget == CompilationTarget.SharedLibrary; } }
|
||||
public bool Shared { get { return CompilationTarget == CompilationTarget.SharedLibrary; } }
|
||||
|
||||
static string LibraryName { get; set; }
|
||||
public string LibraryName { get; set; }
|
||||
|
||||
public static void SetPlatform (string platform)
|
||||
public List<string> ABIs { get; private set; } = new List<string> ();
|
||||
|
||||
public void SetPlatform (string platform)
|
||||
{
|
||||
switch (platform.ToLowerInvariant ()) {
|
||||
case "osx":
|
||||
|
@ -152,7 +167,7 @@ namespace Embeddinator {
|
|||
}
|
||||
}
|
||||
|
||||
public static void SetTarget (string value)
|
||||
public void SetTarget (string value)
|
||||
{
|
||||
switch (value.ToLowerInvariant ()) {
|
||||
case "objc":
|
||||
|
@ -166,7 +181,7 @@ namespace Embeddinator {
|
|||
}
|
||||
}
|
||||
|
||||
public static void SetCompilationTarget (string value)
|
||||
public void SetCompilationTarget (string value)
|
||||
{
|
||||
switch (value.ToLowerInvariant ()) {
|
||||
case "library":
|
||||
|
@ -186,11 +201,11 @@ namespace Embeddinator {
|
|||
}
|
||||
}
|
||||
|
||||
public static Platform Platform { get; set; } = Platform.macOS;
|
||||
public static TargetLanguage TargetLanguage { get; private set; } = TargetLanguage.ObjectiveC;
|
||||
public static CompilationTarget CompilationTarget { get; set; } = CompilationTarget.SharedLibrary;
|
||||
public Platform Platform { get; set; } = Platform.macOS;
|
||||
public TargetLanguage TargetLanguage { get; private set; } = TargetLanguage.ObjectiveC;
|
||||
public CompilationTarget CompilationTarget { get; set; } = CompilationTarget.SharedLibrary;
|
||||
|
||||
static int Generate (List<string> args)
|
||||
public int Generate (List<string> args)
|
||||
{
|
||||
Console.WriteLine ("Parsing assemblies...");
|
||||
|
||||
|
@ -228,59 +243,316 @@ namespace Embeddinator {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int Compile ()
|
||||
static string xcode_app;
|
||||
static string XcodeApp {
|
||||
get {
|
||||
if (string.IsNullOrEmpty (xcode_app)) {
|
||||
using (var process = new Process ()) {
|
||||
process.StartInfo.FileName = "xcode-select";
|
||||
process.StartInfo.Arguments = "-p";
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.Start ();
|
||||
var output = process.StandardOutput.ReadToEnd ();
|
||||
if (process.ExitCode != 0)
|
||||
throw ErrorHelper.CreateError (6, "Could not find the Xcode location.");
|
||||
xcode_app = Path.GetDirectoryName (Path.GetDirectoryName (output.Trim ()));
|
||||
}
|
||||
}
|
||||
return xcode_app;
|
||||
}
|
||||
}
|
||||
|
||||
class BuildInfo
|
||||
{
|
||||
public string Sdk;
|
||||
public string [] Architectures;
|
||||
public string SdkName; // used in -m{SdkName}-version-min
|
||||
public string MinVersion;
|
||||
public string XamariniOSSDK;
|
||||
public string CompilerFlags;
|
||||
public string LinkerFlags;
|
||||
}
|
||||
|
||||
public int Compile ()
|
||||
{
|
||||
Console.WriteLine ("Compiling binding code...");
|
||||
|
||||
BuildInfo [] build_infos;
|
||||
|
||||
switch (Platform) {
|
||||
case Platform.macOS:
|
||||
build_infos = new BuildInfo [] {
|
||||
new BuildInfo { Sdk = "MacOSX", Architectures = new string [] { "i386", "x86_64" }, SdkName = "macosx", MinVersion = "10.7" },
|
||||
};
|
||||
break;
|
||||
case Platform.iOS:
|
||||
case Platform.watchOS:
|
||||
build_infos = new BuildInfo [] {
|
||||
new BuildInfo { Sdk = "iPhoneOS", Architectures = new string [] { "armv7", "armv7s", "arm64" }, SdkName = "iphoneos", MinVersion = "8.0", XamariniOSSDK = "MonoTouch.iphoneos.sdk" },
|
||||
new BuildInfo { Sdk = "iPhoneSimulator", Architectures = new string [] { "i386", "x86_64" }, SdkName = "ios-simulator", MinVersion = "8.0", XamariniOSSDK = "MonoTouch.iphonesimulator.sdk" },
|
||||
};
|
||||
break;
|
||||
case Platform.tvOS:
|
||||
throw new NotImplementedException ($"platform={Platform}");
|
||||
build_infos = new BuildInfo [] {
|
||||
new BuildInfo { Sdk = "AppleTVOS", Architectures = new string [] { "arm64" }, SdkName = "tvos", MinVersion = "9.0", XamariniOSSDK = "Xamarin.AppleTVOS.sdk", CompilerFlags = "-fembed-bitcode", LinkerFlags = "-fembed-bitcode" },
|
||||
new BuildInfo { Sdk = "AppleTVSimulator", Architectures = new string [] { "x86_64" }, SdkName = "tvos-simulator", MinVersion = "9.0", XamariniOSSDK = "Xamarin.AppleTVSimulator.sdk" },
|
||||
};
|
||||
break;
|
||||
case Platform.watchOS:
|
||||
build_infos = new BuildInfo [] {
|
||||
new BuildInfo { Sdk = "WatchOS", Architectures = new string [] { "armv7k" }, SdkName = "watchos", MinVersion = "2.0", XamariniOSSDK = "Xamarin.WatchOS.sdk", CompilerFlags = "-fembed-bitcode", LinkerFlags = "-fembed-bitcode" },
|
||||
new BuildInfo { Sdk = "WatchSimulator", Architectures = new string [] { "i386" }, SdkName = "watchos-simulator", MinVersion = "2.0", XamariniOSSDK = "Xamarin.WatchSimulator.sdk" },
|
||||
};
|
||||
break;
|
||||
default:
|
||||
throw ErrorHelper.CreateError (99, "Internal error: invalid platform {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", Platform);
|
||||
}
|
||||
|
||||
// filter/validate ABIs
|
||||
if (ABIs.Count > 0) {
|
||||
// Validate
|
||||
var all_architectures = build_infos.SelectMany ((v) => v.Architectures);
|
||||
var invalid_architectures = ABIs.Except (all_architectures).ToArray ();
|
||||
if (invalid_architectures.Length > 0) {
|
||||
var arch = invalid_architectures [0];
|
||||
throw ErrorHelper.CreateError (8, $"The architecture '{arch}' is not valid for {Platform}. Valid architectures for {Platform} are: {string.Join (", ", all_architectures)}");
|
||||
}
|
||||
|
||||
// Filter
|
||||
foreach (var info in build_infos)
|
||||
info.Architectures = info.Architectures.Where ((v) => ABIs.Contains (v)).ToArray ();
|
||||
}
|
||||
|
||||
switch (CompilationTarget) {
|
||||
case CompilationTarget.SharedLibrary:
|
||||
case CompilationTarget.StaticLibrary:
|
||||
break;
|
||||
case CompilationTarget.Framework:
|
||||
case CompilationTarget.StaticLibrary:
|
||||
throw new NotImplementedException ($"Compilation target: {CompilationTarget}");
|
||||
default:
|
||||
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
|
||||
}
|
||||
|
||||
StringBuilder options = new StringBuilder ("clang ");
|
||||
if (Debug)
|
||||
options.Append ("-g -O0 ");
|
||||
options.Append ("-fobjc-arc ");
|
||||
options.Append ("-DMONO_EMBEDDINATOR_DLL_EXPORT ");
|
||||
options.Append ("-framework CoreFoundation ");
|
||||
options.Append ("-framework Foundation ");
|
||||
options.Append ("-I\"/Library/Frameworks/Mono.framework/Versions/Current/include/mono-2.0\" -L\"/Library/Frameworks/Mono.framework/Versions/Current/lib/\" -lmonosgen-2.0 ");
|
||||
options.Append ("glib.c mono_embeddinator.c objc-support.m bindings.m ");
|
||||
options.Append ("-ObjC -lobjc ");
|
||||
var lipo_files = new List<string> ();
|
||||
var output_file = string.Empty;
|
||||
|
||||
var files = new string [] {
|
||||
Path.Combine (OutputDirectory, "glib.c"),
|
||||
Path.Combine (OutputDirectory, "mono_embeddinator.c"),
|
||||
Path.Combine (OutputDirectory, "objc-support.m"),
|
||||
Path.Combine (OutputDirectory, "bindings.m"),
|
||||
};
|
||||
|
||||
switch (CompilationTarget) {
|
||||
case CompilationTarget.SharedLibrary:
|
||||
options.Append ($"-dynamiclib ");
|
||||
options.Append ($"-install_name @rpath/lib{LibraryName}.dylib ");
|
||||
options.Append ($"-o lib{LibraryName}.dylib ");
|
||||
output_file = $"lib{LibraryName}.dylib";
|
||||
break;
|
||||
case CompilationTarget.StaticLibrary:
|
||||
throw new NotImplementedException ("compile to static library");
|
||||
case CompilationTarget.Framework:
|
||||
throw new NotImplementedException ("compile to framework");
|
||||
output_file = $"{LibraryName}.a";
|
||||
break;
|
||||
default:
|
||||
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
|
||||
}
|
||||
|
||||
Console.WriteLine ($"\tInvoking: xcrun {options}");
|
||||
var p = Process.Start ("xcrun", options.ToString ());
|
||||
p.WaitForExit ();
|
||||
return p.ExitCode;
|
||||
int exitCode;
|
||||
|
||||
foreach (var build_info in build_infos) {
|
||||
foreach (var arch in build_info.Architectures) {
|
||||
var archOutputDirectory = Path.Combine (OutputDirectory, arch);
|
||||
Directory.CreateDirectory (archOutputDirectory);
|
||||
|
||||
var common_options = new StringBuilder ("clang ");
|
||||
if (Debug)
|
||||
common_options.Append ("-g -O0 ");
|
||||
else
|
||||
common_options.Append ("-O2 -DTOKENLOOKUP ");
|
||||
common_options.Append ("-fobjc-arc ");
|
||||
common_options.Append ("-ObjC ");
|
||||
common_options.Append ("-Wall ");
|
||||
common_options.Append ($"-arch {arch} ");
|
||||
common_options.Append ($"-isysroot {XcodeApp}/Contents/Developer/Platforms/{build_info.Sdk}.platform/Developer/SDKs/{build_info.Sdk}.sdk ");
|
||||
common_options.Append ($"-m{build_info.SdkName}-version-min={build_info.MinVersion} ");
|
||||
common_options.Append ("-I/Library/Frameworks/Mono.framework/Versions/Current/include/mono-2.0 ");
|
||||
|
||||
// Build each file to a .o
|
||||
var object_files = new List<string> ();
|
||||
foreach (var file in files) {
|
||||
var compiler_options = new StringBuilder (common_options.ToString ());
|
||||
compiler_options.Append ("-DMONO_EMBEDDINATOR_DLL_EXPORT ");
|
||||
compiler_options.Append (build_info.CompilerFlags).Append (" ");
|
||||
compiler_options.Append ("-c ");
|
||||
compiler_options.Append (Quote (file)).Append (" ");
|
||||
var objfile = Path.Combine (archOutputDirectory, Path.ChangeExtension (Path.GetFileName (file), "o"));
|
||||
compiler_options.Append ($"-o {Quote (objfile)} ");
|
||||
object_files.Add (objfile);
|
||||
if (!Xcrun (compiler_options, out exitCode))
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
switch (CompilationTarget) {
|
||||
case CompilationTarget.SharedLibrary:
|
||||
// Link all the .o files into a .dylib
|
||||
var options = new StringBuilder (common_options.ToString ());
|
||||
options.Append ($"-dynamiclib ");
|
||||
options.Append (build_info.LinkerFlags).Append (" ");
|
||||
options.Append ("-lobjc ");
|
||||
options.Append ("-framework CoreFoundation ");
|
||||
options.Append ("-framework Foundation ");
|
||||
options.Append ($"-install_name {Quote ("@rpath/" + output_file)} ");
|
||||
|
||||
foreach (var objfile in object_files)
|
||||
options.Append (Quote (objfile)).Append (" ");
|
||||
|
||||
var dynamic_ofile = Path.Combine (archOutputDirectory, output_file);
|
||||
options.Append ($"-o ").Append (Quote (dynamic_ofile)).Append (" ");
|
||||
lipo_files.Add (dynamic_ofile);
|
||||
if (!string.IsNullOrEmpty (build_info.XamariniOSSDK)) {
|
||||
options.Append ($"-L/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/SDKs/{build_info.XamariniOSSDK}/usr/lib ");
|
||||
} else {
|
||||
options.Append ("-L/Library/Frameworks/Mono.framework/Versions/Current/lib/ ");
|
||||
}
|
||||
options.Append ("-lmonosgen-2.0 ");
|
||||
if (!Xcrun (options, out exitCode))
|
||||
return exitCode;
|
||||
break;
|
||||
case CompilationTarget.StaticLibrary:
|
||||
// Archive all the .o files into a .a
|
||||
var archive_options = new StringBuilder ("ar cru ");
|
||||
var static_ofile = Path.Combine (archOutputDirectory, output_file);
|
||||
archive_options.Append (static_ofile).Append (" ");
|
||||
lipo_files.Add (static_ofile);
|
||||
foreach (var objfile in object_files)
|
||||
archive_options.Append (objfile).Append (" ");
|
||||
if (!Xcrun (archive_options, out exitCode))
|
||||
return exitCode;
|
||||
break;
|
||||
case CompilationTarget.Framework:
|
||||
throw new NotImplementedException ("compile to framework");
|
||||
default:
|
||||
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var output_path = Path.Combine (OutputDirectory, output_file);
|
||||
if (!Lipo (lipo_files, output_path, out exitCode))
|
||||
return exitCode;
|
||||
|
||||
if (!DSymUtil (output_path, out exitCode))
|
||||
return exitCode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
string GetTargetFramework ()
|
||||
{
|
||||
switch (Platform) {
|
||||
case Platform.macOS:
|
||||
throw new NotImplementedException ("target framework for macOS");
|
||||
case Platform.iOS:
|
||||
return "Xamarin.iOS,v1.0";
|
||||
case Platform.tvOS:
|
||||
return "Xamarin.TVOS,v1.0";
|
||||
case Platform.watchOS:
|
||||
return "Xamarin.WatchOS,v1.0";
|
||||
default:
|
||||
throw ErrorHelper.CreateError (99, "Internal error: invalid platform {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", Platform);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RunProcess (string filename, string arguments, out int exitCode, out string stdout)
|
||||
{
|
||||
Console.WriteLine($"\t{filename} {arguments}");
|
||||
using (var p = new Process ()) {
|
||||
p.StartInfo.FileName = filename;
|
||||
p.StartInfo.Arguments = arguments;
|
||||
p.StartInfo.UseShellExecute = false;
|
||||
p.StartInfo.RedirectStandardOutput = true;
|
||||
p.Start();
|
||||
stdout = p.StandardOutput.ReadToEnd();
|
||||
exitCode = p.ExitCode;
|
||||
return exitCode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static int RunProcess (string filename, string arguments)
|
||||
{
|
||||
Console.WriteLine ($"\t{filename} {arguments}");
|
||||
using (var p = Process.Start (filename, arguments)) {
|
||||
p.WaitForExit ();
|
||||
return p.ExitCode;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool RunProcess (string filename, string arguments, out int exitCode)
|
||||
{
|
||||
exitCode = RunProcess (filename, arguments);
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
public static bool Xcrun (StringBuilder options, out int exitCode)
|
||||
{
|
||||
return RunProcess ("xcrun", options.ToString (), out exitCode);
|
||||
}
|
||||
|
||||
bool DSymUtil (string input, out int exitCode)
|
||||
{
|
||||
exitCode = 0;
|
||||
|
||||
if (!Debug)
|
||||
return true;
|
||||
|
||||
string output;
|
||||
switch (CompilationTarget) {
|
||||
case CompilationTarget.StaticLibrary:
|
||||
return true;
|
||||
case CompilationTarget.SharedLibrary:
|
||||
output = input + ".dSYM";
|
||||
break;
|
||||
default:
|
||||
throw ErrorHelper.CreateError (99, "Internal error: invalid compilation target {0}. Please file a bug report with a test case (https://github.com/mono/Embeddinator-4000/issues).", CompilationTarget);
|
||||
}
|
||||
|
||||
var dsymutil_options = new StringBuilder ("dsymutil ");
|
||||
dsymutil_options.Append (Quote (input)).Append (" ");
|
||||
dsymutil_options.Append ($"-o {Quote (output)} ");
|
||||
return Xcrun (dsymutil_options, out exitCode);
|
||||
}
|
||||
|
||||
public static bool Lipo (List<string> inputs, string output, out int exitCode)
|
||||
{
|
||||
Directory.CreateDirectory (Path.GetDirectoryName (output));
|
||||
if (inputs.Count == 1) {
|
||||
File.Copy (inputs [0], output, true);
|
||||
exitCode = 0;
|
||||
return true;
|
||||
} else {
|
||||
var lipo_options = new StringBuilder ("lipo ");
|
||||
foreach (var file in inputs)
|
||||
lipo_options.Append (file).Append (" ");
|
||||
lipo_options.Append ("-create -output ");
|
||||
lipo_options.Append (Quote (output));
|
||||
return Xcrun (lipo_options, out exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
public static string Quote (string f)
|
||||
{
|
||||
if (f.IndexOf (' ') == -1 && f.IndexOf ('\'') == -1 && f.IndexOf (',') == -1 && f.IndexOf ('$') == -1)
|
||||
return f;
|
||||
|
||||
var s = new StringBuilder ();
|
||||
|
||||
s.Append ('"');
|
||||
foreach (var c in f) {
|
||||
if (c == '"' || c == '\\')
|
||||
s.Append ('\\');
|
||||
|
||||
s.Append (c);
|
||||
}
|
||||
s.Append ('"');
|
||||
|
||||
return s.ToString ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
|
||||
using IKVM.Reflection;
|
||||
using Type = IKVM.Reflection.Type;
|
||||
|
||||
namespace Embeddinator {
|
||||
|
||||
public static class TypeExtensions {
|
||||
|
||||
public static bool Is (this Type self, string @namespace, string name)
|
||||
{
|
||||
return (self.Namespace == @namespace) && (self.Name == name);
|
||||
}
|
||||
|
||||
public static bool HasCustomAttribute (this Type self, string @namespace, string name)
|
||||
{
|
||||
foreach (var ca in self.CustomAttributes) {
|
||||
if (ca.AttributeType.Is (@namespace, name))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -306,11 +306,18 @@
|
|||
<Compile Include="error.cs" />
|
||||
<Compile Include="ErrorHelper.cs" />
|
||||
<Compile Include="Version.generated.cs" />
|
||||
<Compile Include="objcgenerator-helpers.cs" />
|
||||
<Compile Include="objcgenerator-processor.cs" />
|
||||
<Compile Include="extensions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="support\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\support\embeddinator.h">
|
||||
<Link>support\embeddinator.h</Link>
|
||||
<LogicalName>embeddinator.h</LogicalName>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="..\support\glib.c">
|
||||
<Link>support\glib.c</Link>
|
||||
<LogicalName>glib.c</LogicalName>
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using IKVM.Reflection;
|
||||
using Type = IKVM.Reflection.Type;
|
||||
|
||||
using Embeddinator;
|
||||
namespace ObjC {
|
||||
public partial class ObjCGenerator {
|
||||
|
||||
// get a name that is safe to use from ObjC code
|
||||
public static string GetObjCName (Type t)
|
||||
{
|
||||
return t.FullName.Replace ('.', '_');
|
||||
}
|
||||
|
||||
void GetSignatures (string objName, string monoName, MemberInfo info, ParameterInfo [] parameters, out string objcSignature, out string monoSignature)
|
||||
{
|
||||
var method = (info as MethodBase); // else it's a PropertyInfo
|
||||
// special case for setter-only - the underscore looks ugly
|
||||
if ((method != null) && method.IsSpecialName)
|
||||
objName = objName.Replace ("_", String.Empty);
|
||||
StringBuilder objc = new StringBuilder (objName);
|
||||
var mono = new StringBuilder (monoName);
|
||||
mono.Append ('(');
|
||||
int n = 0;
|
||||
foreach (var p in parameters) {
|
||||
if (objc.Length > objName.Length) {
|
||||
objc.Append (' ');
|
||||
mono.Append (',');
|
||||
}
|
||||
if (method != null) {
|
||||
if (n == 0) {
|
||||
if (method.IsConstructor || !method.IsSpecialName)
|
||||
objc.Append (PascalCase (p.Name));
|
||||
} else
|
||||
objc.Append (p.Name.ToLowerInvariant ());
|
||||
}
|
||||
var pt = p.ParameterType;
|
||||
var ptname = GetTypeName (p.ParameterType);
|
||||
if (types.Contains (pt))
|
||||
ptname += " *";
|
||||
objc.Append (":(").Append (ptname).Append (") ").Append (p.Name);
|
||||
mono.Append (GetMonoName (p.ParameterType));
|
||||
n++;
|
||||
}
|
||||
mono.Append (')');
|
||||
|
||||
objcSignature = objc.ToString ();
|
||||
monoSignature = mono.ToString ();
|
||||
}
|
||||
|
||||
public IEnumerable<ConstructorInfo> GetUnavailableParentCtors (Type type, List<ConstructorInfo> typeCtors)
|
||||
{
|
||||
var baseType = type.BaseType;
|
||||
if (baseType.Namespace == "System" && baseType.Name == "Object")
|
||||
return Enumerable.Empty<ConstructorInfo> ();
|
||||
|
||||
List<ConstructorInfo> parentCtors;
|
||||
if (!ctors.TryGetValue (baseType, out parentCtors))
|
||||
return Enumerable.Empty<ConstructorInfo> ();
|
||||
|
||||
var finalList = new List<ConstructorInfo> ();
|
||||
foreach (var pctor in parentCtors) {
|
||||
var pctorParams = pctor.GetParameters ();
|
||||
foreach (var ctor in typeCtors) {
|
||||
var ctorParams = ctor.GetParameters ();
|
||||
if (pctorParams.Any (pc => !ctorParams.Any (p => p.Position == pc.Position && pc.ParameterType == p.ParameterType))) {
|
||||
finalList.Add (pctor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finalList;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,211 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using IKVM.Reflection;
|
||||
using Type = IKVM.Reflection.Type;
|
||||
|
||||
using Embeddinator;
|
||||
|
||||
namespace ObjC {
|
||||
|
||||
public partial class ObjCGenerator {
|
||||
|
||||
List<Exception> delayed = new List<Exception> ();
|
||||
HashSet<Type> unsupported = new HashSet<Type> ();
|
||||
|
||||
bool IsSupported (Type t)
|
||||
{
|
||||
if (t.IsByRef)
|
||||
return IsSupported (t.GetElementType ());
|
||||
|
||||
if (unsupported.Contains (t))
|
||||
return false;
|
||||
|
||||
// FIXME protocols
|
||||
if (t.IsInterface) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1010, $"Type `{t}` is not generated because `interfaces` are not supported."));
|
||||
unsupported.Add (t);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (t.IsGenericParameter || t.IsGenericType) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1010, $"Type `{t}` is not generated because `generics` are not supported."));
|
||||
unsupported.Add (t);
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (t.Namespace) {
|
||||
case "System":
|
||||
switch (t.Name) {
|
||||
case "Object": // we cannot accept arbitrary NSObject (which we might not have bound) into mono
|
||||
case "Exception":
|
||||
case "IFormatProvider":
|
||||
case "Type":
|
||||
delayed.Add (ErrorHelper.CreateWarning (1011, $"Type `{t}` is not generated because it lacks a native counterpart."));
|
||||
unsupported.Add (t);
|
||||
return false;
|
||||
case "DateTime": // FIXME: NSDateTime
|
||||
case "Decimal": // FIXME: NSDecimal
|
||||
case "TimeSpan":
|
||||
delayed.Add (ErrorHelper.CreateWarning (1012, $"Type `{t}` is not generated because it lacks a marshaling code with a native counterpart."));
|
||||
unsupported.Add (t);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected IEnumerable<Type> GetTypes (Assembly a)
|
||||
{
|
||||
foreach (var t in a.GetTypes ()) {
|
||||
if (!t.IsPublic)
|
||||
continue;
|
||||
|
||||
if (!IsSupported (t))
|
||||
continue;
|
||||
|
||||
yield return t;
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<ConstructorInfo> GetConstructors (Type t)
|
||||
{
|
||||
foreach (var ctor in t.GetConstructors ()) {
|
||||
// .cctor not to be called directly by native code
|
||||
if (ctor.IsStatic)
|
||||
continue;
|
||||
if (!ctor.IsPublic)
|
||||
continue;
|
||||
|
||||
bool pcheck = true;
|
||||
foreach (var p in ctor.GetParameters ()) {
|
||||
var pt = p.ParameterType;
|
||||
if (!IsSupported (pt)) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1020, $"Constructor `{ctor}` is not generated because of parameter type `{pt}` is not supported."));
|
||||
pcheck = false;
|
||||
} else if (p.HasDefaultValue) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1021, $"Constructor `{ctor}` parameter `{p.Name}` has a default value that is not supported."));
|
||||
}
|
||||
}
|
||||
if (!pcheck)
|
||||
continue;
|
||||
|
||||
yield return ctor;
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<MethodInfo> GetMethods (Type t)
|
||||
{
|
||||
foreach (var mi in t.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
|
||||
if (!mi.IsPublic)
|
||||
continue;
|
||||
|
||||
var rt = mi.ReturnType;
|
||||
if (!IsSupported (rt)) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1030, $"Method `{mi}` is not generated because return type `{rt}` is not supported."));
|
||||
continue;
|
||||
}
|
||||
|
||||
bool pcheck = true;
|
||||
foreach (var p in mi.GetParameters ()) {
|
||||
var pt = p.ParameterType;
|
||||
if (!IsSupported (pt)) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1031, $"Method `{mi}` is not generated because of parameter type `{pt}` is not supported."));
|
||||
pcheck = false;
|
||||
} else if (p.HasDefaultValue) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1032, $"Method `{mi}` parameter `{p.Name}` has a default value that is not supported."));
|
||||
}
|
||||
}
|
||||
if (!pcheck)
|
||||
continue;
|
||||
|
||||
yield return mi;
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<PropertyInfo> GetProperties (Type t)
|
||||
{
|
||||
foreach (var pi in t.GetProperties (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
|
||||
var pt = pi.PropertyType;
|
||||
if (!IsSupported (pt)) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1040, $"Property `{pi}` is not generated because of parameter type `{pt}` is not supported."));
|
||||
continue;
|
||||
}
|
||||
yield return pi;
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<FieldInfo> GetFields (Type t)
|
||||
{
|
||||
foreach (var fi in t.GetFields (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
|
||||
if (!fi.IsPublic)
|
||||
continue;
|
||||
var ft = fi.FieldType;
|
||||
if (!IsSupported (ft)) {
|
||||
delayed.Add (ErrorHelper.CreateWarning (1050, $"Field `{fi}` is not generated because of field type `{ft}` is not supported."));
|
||||
continue;
|
||||
}
|
||||
yield return fi;
|
||||
}
|
||||
}
|
||||
|
||||
List<Type> enums = new List<Type> ();
|
||||
List<Type> types = new List<Type> ();
|
||||
Dictionary<Type, List<ConstructorInfo>> ctors = new Dictionary<Type, List<ConstructorInfo>> ();
|
||||
Dictionary<Type, List<MethodInfo>> methods = new Dictionary<Type, List<MethodInfo>> ();
|
||||
Dictionary<Type, List<PropertyInfo>> properties = new Dictionary<Type, List<PropertyInfo>> ();
|
||||
Dictionary<Type, List<FieldInfo>> fields = new Dictionary<Type, List<FieldInfo>> ();
|
||||
|
||||
public override void Process (IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
foreach (var a in assemblies) {
|
||||
foreach (var t in GetTypes (a)) {
|
||||
if (t.IsEnum)
|
||||
enums.Add (t);
|
||||
else
|
||||
types.Add (t);
|
||||
|
||||
var constructors = GetConstructors (t).OrderBy ((arg) => arg.ParameterCount).ToList ();
|
||||
if (constructors.Count > 0)
|
||||
ctors.Add (t, constructors);
|
||||
|
||||
var meths = GetMethods (t).OrderBy ((arg) => arg.Name).ToList ();
|
||||
if (meths.Count > 0)
|
||||
methods.Add (t, meths);
|
||||
|
||||
var props = new List<PropertyInfo> ();
|
||||
foreach (var pi in GetProperties (t)) {
|
||||
var getter = pi.GetGetMethod ();
|
||||
var setter = pi.GetSetMethod ();
|
||||
// setter only property are valid in .NET and we need to generate a method in ObjC (there's no writeonly properties)
|
||||
if (getter == null)
|
||||
continue;
|
||||
// indexers are implemented as methods
|
||||
if ((getter.ParameterCount > 0) || ((setter != null) && setter.ParameterCount > 1))
|
||||
continue;
|
||||
// we can do better than methods for the more common cases (readonly and readwrite)
|
||||
meths.Remove (getter);
|
||||
meths.Remove (setter);
|
||||
props.Add (pi);
|
||||
}
|
||||
props = props.OrderBy ((arg) => arg.Name).ToList ();
|
||||
if (props.Count > 0)
|
||||
properties.Add (t, props);
|
||||
|
||||
// fields will need to be wrapped within properties - except for enums
|
||||
if (!t.IsEnum) {
|
||||
var f = GetFields (t).OrderBy ((arg) => arg.Name).ToList ();
|
||||
if (f.Count > 0)
|
||||
fields.Add (t, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
types = types.OrderBy ((arg) => arg.FullName).OrderBy ((arg) => types.Contains (arg.BaseType)).ToList ();
|
||||
Console.WriteLine ($"\t{types.Count} types found");
|
||||
|
||||
ErrorHelper.Show (delayed);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,68 +11,14 @@ using Embeddinator;
|
|||
|
||||
namespace ObjC {
|
||||
|
||||
public class ObjCGenerator : Generator {
|
||||
public partial class ObjCGenerator : Generator {
|
||||
|
||||
static TextWriter headers = new StringWriter ();
|
||||
static TextWriter implementation = new StringWriter ();
|
||||
|
||||
static ParameterInfo [] NoParameters = new ParameterInfo [0];
|
||||
|
||||
List<Type> types = new List<Type> ();
|
||||
Dictionary<Type, List<ConstructorInfo>> ctors = new Dictionary<Type, List<ConstructorInfo>> ();
|
||||
Dictionary<Type, List<MethodInfo>> methods = new Dictionary<Type, List<MethodInfo>> ();
|
||||
Dictionary<Type, List<PropertyInfo>> properties = new Dictionary<Type, List<PropertyInfo>> ();
|
||||
|
||||
public override void Process (IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
foreach (var a in assemblies) {
|
||||
foreach (var t in a.GetTypes ()) {
|
||||
if (!t.IsPublic)
|
||||
continue;
|
||||
// gather types for forward declarations
|
||||
types.Add (t);
|
||||
|
||||
var constructors = new List<ConstructorInfo> ();
|
||||
foreach (var ctor in t.GetConstructors ()) {
|
||||
// .cctor not to be called directly by native code
|
||||
if (ctor.IsStatic)
|
||||
continue;
|
||||
if (!ctor.IsPublic)
|
||||
continue;
|
||||
constructors.Add (ctor);
|
||||
}
|
||||
constructors = constructors.OrderBy ((arg) => arg.ParameterCount).ToList ();
|
||||
ctors.Add (t, constructors);
|
||||
|
||||
var meths = new List<MethodInfo> ();
|
||||
foreach (var mi in t.GetMethods (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
|
||||
meths.Add (mi);
|
||||
}
|
||||
methods.Add (t, meths);
|
||||
|
||||
var props = new List<PropertyInfo> ();
|
||||
foreach (var pi in t.GetProperties (BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)) {
|
||||
var getter = pi.GetGetMethod ();
|
||||
var setter = pi.GetSetMethod ();
|
||||
// setter only property are valid in .NET and we need to generate a method in ObjC (there's no writeonly properties)
|
||||
if (getter == null)
|
||||
continue;
|
||||
// we can do better than methods for the more common cases (readonly and readwrite)
|
||||
meths.Remove (getter);
|
||||
meths.Remove (setter);
|
||||
props.Add (pi);
|
||||
}
|
||||
props = props.OrderBy ((arg) => arg.Name).ToList ();
|
||||
properties.Add (t, props);
|
||||
}
|
||||
}
|
||||
types = types.OrderBy ((arg) => arg.FullName).OrderBy ((arg) => types.Contains (arg.BaseType)).ToList ();
|
||||
Console.WriteLine ($"\t{types.Count} types found");
|
||||
}
|
||||
|
||||
public override void Generate (IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
headers.WriteLine ("#include \"mono_embeddinator.h\"");
|
||||
headers.WriteLine ("#include \"embeddinator.h\"");
|
||||
headers.WriteLine ("#import <Foundation/Foundation.h>");
|
||||
headers.WriteLine ();
|
||||
headers.WriteLine ();
|
||||
|
@ -80,8 +26,6 @@ namespace ObjC {
|
|||
headers.WriteLine ("#error Embeddinator code must be built with ARC.");
|
||||
headers.WriteLine ("#endif");
|
||||
headers.WriteLine ();
|
||||
headers.WriteLine ("MONO_EMBEDDINATOR_BEGIN_DECLS");
|
||||
headers.WriteLine ();
|
||||
|
||||
headers.WriteLine ("// forward declarations");
|
||||
foreach (var t in types)
|
||||
|
@ -91,6 +35,7 @@ namespace ObjC {
|
|||
implementation.WriteLine ("#include \"bindings.h\"");
|
||||
implementation.WriteLine ("#include \"glib.h\"");
|
||||
implementation.WriteLine ("#include \"objc-support.h\"");
|
||||
implementation.WriteLine ("#include \"mono_embeddinator.h\"");
|
||||
implementation.WriteLine ("#include <mono/jit/jit.h>");
|
||||
implementation.WriteLine ("#include <mono/metadata/assembly.h>");
|
||||
implementation.WriteLine ("#include <mono/metadata/object.h>");
|
||||
|
@ -118,9 +63,6 @@ namespace ObjC {
|
|||
implementation.WriteLine ();
|
||||
|
||||
base.Generate (assemblies);
|
||||
|
||||
headers.WriteLine ();
|
||||
headers.WriteLine ("MONO_EMBEDDINATOR_END_DECLS");
|
||||
}
|
||||
|
||||
protected override void Generate (Assembly a)
|
||||
|
@ -136,11 +78,49 @@ namespace ObjC {
|
|||
implementation.WriteLine ("}");
|
||||
implementation.WriteLine ();
|
||||
|
||||
foreach (var t in enums) {
|
||||
GenerateEnum (t);
|
||||
}
|
||||
|
||||
foreach (var t in types) {
|
||||
Generate (t);
|
||||
}
|
||||
}
|
||||
|
||||
void GenerateEnum (Type t)
|
||||
{
|
||||
var managed_name = GetObjCName (t);
|
||||
var underlying_type = t.GetEnumUnderlyingType ();
|
||||
var base_type = GetTypeName (underlying_type);
|
||||
|
||||
// it's nicer to expose flags as unsigned integers - but .NET defaults to `int`
|
||||
bool flags = t.HasCustomAttribute ("System", "FlagsAttribute");
|
||||
if (flags) {
|
||||
switch (Type.GetTypeCode (underlying_type)) {
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
base_type = "unsigned " + base_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var macro = flags ? "NS_OPTIONS" : "NS_ENUM";
|
||||
headers.WriteLine ($"typedef {macro}({base_type}, {managed_name}) {{");
|
||||
foreach (var name in t.GetEnumNames ()) {
|
||||
var value = t.GetField (name).GetRawConstantValue ();
|
||||
headers.Write ($"\t{managed_name}{name} = ");
|
||||
if (flags)
|
||||
headers.Write ($"0x{value:x}");
|
||||
else
|
||||
headers.Write (value);
|
||||
headers.WriteLine (',');
|
||||
}
|
||||
headers.WriteLine ("};");
|
||||
headers.WriteLine ();
|
||||
}
|
||||
|
||||
protected override void Generate (Type t)
|
||||
{
|
||||
var has_bound_base_class = types.Contains (t.BaseType);
|
||||
|
@ -151,15 +131,17 @@ namespace ObjC {
|
|||
var native_name = GetTypeName (t);
|
||||
headers.WriteLine ();
|
||||
headers.WriteLine ($"// {t.AssemblyQualifiedName}");
|
||||
headers.WriteLine ($"@interface {native_name} : {GetTypeName (t.BaseType)}");
|
||||
headers.WriteLine ($"@interface {native_name} : {GetTypeName (t.BaseType)} {{");
|
||||
if (!static_type && !has_bound_base_class) {
|
||||
headers.WriteLine ("\t@public MonoEmbedObject* _object;");
|
||||
}
|
||||
headers.WriteLine ("}");
|
||||
headers.WriteLine ();
|
||||
|
||||
implementation.WriteLine ();
|
||||
implementation.WriteLine ($"// {t.AssemblyQualifiedName}");
|
||||
implementation.WriteLine ($"@implementation {native_name} {{");
|
||||
// our internal field is only needed once in the type hierarchy
|
||||
if (!static_type && !has_bound_base_class)
|
||||
implementation.WriteLine ("\t@public MonoEmbedObject* _object;");
|
||||
implementation.WriteLine ("}");
|
||||
implementation.WriteLine ();
|
||||
|
||||
|
@ -169,7 +151,12 @@ namespace ObjC {
|
|||
implementation.WriteLine ("\t\treturn;");
|
||||
var aname = t.Assembly.GetName ().Name;
|
||||
implementation.WriteLine ($"\t__lookup_assembly_{aname} ();");
|
||||
|
||||
implementation.WriteLine ("#if TOKENLOOKUP");
|
||||
implementation.WriteLine ($"\t{managed_name}_class = mono_class_get (__{aname}_image, 0x{t.MetadataToken:X8});");
|
||||
implementation.WriteLine ("#else");
|
||||
implementation.WriteLine ($"\t{managed_name}_class = mono_class_from_name (__{aname}_image, \"{t.Namespace}\", \"{t.Name}\");");
|
||||
implementation.WriteLine ("#endif");
|
||||
implementation.WriteLine ("}");
|
||||
implementation.WriteLine ();
|
||||
|
||||
|
@ -185,76 +172,67 @@ namespace ObjC {
|
|||
var default_init = false;
|
||||
List<ConstructorInfo> constructors;
|
||||
if (ctors.TryGetValue (t, out constructors)) {
|
||||
// First get the unavailable init ctor selectors in parent class
|
||||
var unavailableCtors = GetUnavailableParentCtors (t, constructors);
|
||||
if (unavailableCtors.Count () > 0) {
|
||||
// TODO: Print a #pragma mark once we have a well defined header structure http://nshipster.com/pragma/
|
||||
headers.WriteLine ("// List of unavailable initializers. See Constructors v.s. Initializers in our docs");
|
||||
headers.WriteLine ("// https://github.com/mono/Embeddinator-4000/blob/master/docs/ObjC.md");
|
||||
foreach (var uctor in unavailableCtors) {
|
||||
var ctorparams = uctor.GetParameters ();
|
||||
string name = "init";
|
||||
string signature = ".ctor()";
|
||||
if (ctorparams.Length > 0)
|
||||
GetSignatures ("initWith", uctor.Name, uctor, ctorparams, out name, out signature);
|
||||
headers.WriteLine ($"- (instancetype){name} NS_UNAVAILABLE;");
|
||||
}
|
||||
headers.WriteLine ();
|
||||
}
|
||||
|
||||
foreach (var ctor in constructors) {
|
||||
var pcount = ctor.ParameterCount;
|
||||
default_init |= pcount == 0;
|
||||
|
||||
var parameters = ctor.GetParameters ();
|
||||
StringBuilder name = new StringBuilder ("init");
|
||||
foreach (var p in parameters) {
|
||||
if (name.Length == 4)
|
||||
name.Append ("With");
|
||||
else
|
||||
name.Append (' ');
|
||||
name.Append (PascalCase (p.Name));
|
||||
name.Append (":(").Append (GetTypeName (p.ParameterType)).Append (") ").Append (p.Name);
|
||||
}
|
||||
|
||||
var signature = new StringBuilder (".ctor(");
|
||||
foreach (var p in parameters) {
|
||||
if (signature.Length > 6)
|
||||
signature.Append (',');
|
||||
signature.Append (GetMonoName (p.ParameterType));
|
||||
}
|
||||
signature.Append (")");
|
||||
string name = "init";
|
||||
string signature = ".ctor()";
|
||||
if (parameters.Length > 0)
|
||||
GetSignatures ("initWith", ctor.Name, ctor, parameters, out name, out signature);
|
||||
|
||||
headers.WriteLine ($"- (instancetype){name};");
|
||||
|
||||
implementation.WriteLine ($"- (instancetype){name}");
|
||||
implementation.WriteLine ("{");
|
||||
implementation.WriteLine ($"\tconst char __method_name [] = \"{t.FullName}:{signature}\";");
|
||||
implementation.WriteLine ("\tstatic MonoMethod* __method = nil;");
|
||||
implementation.WriteLine ("\tif (!__method) {");
|
||||
implementation.WriteLine ("#if TOKENLOOKUP");
|
||||
implementation.WriteLine ($"\t\t__method = mono_get_method (__{aname}_image, 0x{ctor.MetadataToken:X8}, {managed_name}_class);");
|
||||
implementation.WriteLine ("#else");
|
||||
implementation.WriteLine ($"\t\tconst char __method_name [] = \"{t.FullName}:{signature}\";");
|
||||
implementation.WriteLine ($"\t\t__method = mono_embeddinator_lookup_method (__method_name, {managed_name}_class);");
|
||||
implementation.WriteLine ("#endif");
|
||||
implementation.WriteLine ("\t}");
|
||||
// TODO: this logic will need to be update for managed NSObject types (e.g. from XI / XM) not to call [super init]
|
||||
implementation.WriteLine ("\tif (!_object) {");
|
||||
implementation.WriteLine ($"\t\tMonoObject* __instance = mono_object_new (__mono_context.domain, {managed_name}_class);");
|
||||
implementation.WriteLine ("\t\tMonoObject* __exception = nil;");
|
||||
string postInvoke = String.Empty;
|
||||
var args = "nil";
|
||||
if (pcount > 0) {
|
||||
implementation.WriteLine ($"\t\tvoid* __args [{pcount}];");
|
||||
for (int i = 0; i < pcount; i++) {
|
||||
var p = parameters [i];
|
||||
switch (Type.GetTypeCode (p.ParameterType)) {
|
||||
case TypeCode.String:
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = mono_string_new (__mono_context.domain, [{p.Name} UTF8String]);");
|
||||
break;
|
||||
case TypeCode.Boolean:
|
||||
case TypeCode.Char:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Double:
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = &{p.Name};");
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException ($"Converting type {p.ParameterType.FullName} to mono code");
|
||||
}
|
||||
}
|
||||
Generate (parameters, out postInvoke);
|
||||
args = "__args";
|
||||
}
|
||||
implementation.WriteLine ($"\t\tmono_runtime_invoke (__method, __instance, {args}, &__exception);");
|
||||
var instance = "__instance";
|
||||
if (t.IsValueType) {
|
||||
implementation.WriteLine ($"\t\tvoid* __unboxed = mono_object_unbox (__instance);");
|
||||
instance = "__unboxed";
|
||||
}
|
||||
implementation.WriteLine ($"\t\tmono_runtime_invoke (__method, {instance}, {args}, &__exception);");
|
||||
implementation.WriteLine ("\t\tif (__exception)");
|
||||
// TODO: Apple often do NSLog (or asserts but they are more brutal) and returning nil is allowed (and common)
|
||||
implementation.WriteLine ("\t\t\treturn nil;");
|
||||
//implementation.WriteLine ("\t\t\tmono_embeddinator_throw_exception (__exception);");
|
||||
implementation.Write (postInvoke);
|
||||
implementation.WriteLine ("\t\t_object = mono_embeddinator_create_object (__instance);");
|
||||
implementation.WriteLine ("\t}");
|
||||
if (types.Contains (t.BaseType))
|
||||
|
@ -270,6 +248,7 @@ namespace ObjC {
|
|||
if (static_type)
|
||||
headers.WriteLine ("// a .net static type cannot be initialized");
|
||||
headers.WriteLine ("- (instancetype)init NS_UNAVAILABLE;");
|
||||
headers.WriteLine ("+ (instancetype)new NS_UNAVAILABLE;");
|
||||
}
|
||||
|
||||
// TODO we should re-use the base `init` when it exists
|
||||
|
@ -287,20 +266,25 @@ namespace ObjC {
|
|||
implementation.WriteLine ();
|
||||
}
|
||||
|
||||
headers.WriteLine ();
|
||||
List<PropertyInfo> props;
|
||||
if (properties.TryGetValue (t, out props)) {
|
||||
headers.WriteLine ();
|
||||
foreach (var pi in props)
|
||||
Generate (pi);
|
||||
headers.WriteLine ();
|
||||
}
|
||||
|
||||
headers.WriteLine ();
|
||||
List<FieldInfo> f;
|
||||
if (fields.TryGetValue (t, out f)) {
|
||||
headers.WriteLine ();
|
||||
foreach (var fi in f)
|
||||
Generate (fi);
|
||||
}
|
||||
|
||||
List<MethodInfo> meths;
|
||||
if (methods.TryGetValue (t, out meths)) {
|
||||
headers.WriteLine ();
|
||||
foreach (var mi in meths)
|
||||
Generate (mi);
|
||||
headers.WriteLine ();
|
||||
}
|
||||
|
||||
headers.WriteLine ("@end");
|
||||
|
@ -310,6 +294,58 @@ namespace ObjC {
|
|||
implementation.WriteLine ();
|
||||
}
|
||||
|
||||
void Generate (ParameterInfo [] parameters, out string postInvoke)
|
||||
{
|
||||
StringBuilder post = new StringBuilder ();
|
||||
var pcount = parameters.Length;
|
||||
implementation.WriteLine ($"\t\tvoid* __args [{pcount}];");
|
||||
for (int i = 0; i < pcount; i++) {
|
||||
var p = parameters [i];
|
||||
|
||||
var pt = p.ParameterType;
|
||||
var is_by_ref = pt.IsByRef;
|
||||
if (is_by_ref)
|
||||
pt = pt.GetElementType ();
|
||||
|
||||
switch (Type.GetTypeCode (pt)) {
|
||||
case TypeCode.String:
|
||||
if (is_by_ref) {
|
||||
implementation.WriteLine ($"\t\tMonoString* __string = *{p.Name} ? mono_string_new (__mono_context.domain, [*{p.Name} UTF8String]) : nil;");
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = &__string;");
|
||||
post.AppendLine ($"\t\t*{p.Name} = mono_embeddinator_get_nsstring (__string);");
|
||||
} else
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = {p.Name} ? mono_string_new (__mono_context.domain, [{p.Name} UTF8String]) : nil;");
|
||||
break;
|
||||
case TypeCode.Boolean:
|
||||
case TypeCode.Char:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
case TypeCode.Single:
|
||||
case TypeCode.Double:
|
||||
if (is_by_ref)
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = {p.Name};");
|
||||
else
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = &{p.Name};");
|
||||
break;
|
||||
default:
|
||||
if (pt.IsValueType)
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = mono_object_unbox (mono_gchandle_get_target ({p.Name}->_object->_handle));");
|
||||
else if (types.Contains (pt))
|
||||
implementation.WriteLine ($"\t\t__args [{i}] = mono_gchandle_get_target ({p.Name}->_object->_handle);");
|
||||
else
|
||||
throw new NotImplementedException ($"Converting type {pt.FullName} to mono code");
|
||||
break;
|
||||
}
|
||||
}
|
||||
postInvoke = post.ToString ();
|
||||
}
|
||||
|
||||
protected override void Generate (PropertyInfo pi)
|
||||
{
|
||||
var getter = pi.GetGetMethod ();
|
||||
|
@ -327,14 +363,70 @@ namespace ObjC {
|
|||
headers.Write (", readonly");
|
||||
else
|
||||
headers.Write (", readwrite");
|
||||
var property_type = GetTypeName (pi.PropertyType);
|
||||
var pt = pi.PropertyType;
|
||||
var property_type = GetTypeName (pt);
|
||||
if (types.Contains (pt))
|
||||
property_type += " *";
|
||||
headers.WriteLine ($") {property_type} {name};");
|
||||
|
||||
ImplementMethod (getter.IsStatic, getter.ReturnType, name, NoParameters, pi.DeclaringType, getter.Name);
|
||||
ImplementMethod (getter, name, pi);
|
||||
if (setter == null)
|
||||
return;
|
||||
|
||||
ImplementMethod (setter.IsStatic, setter.ReturnType, "set" + pi.Name, setter.GetParameters (), pi.DeclaringType, setter.Name);
|
||||
ImplementMethod (setter, "set" + pi.Name, pi);
|
||||
}
|
||||
|
||||
protected void Generate (FieldInfo fi)
|
||||
{
|
||||
bool read_only = ((fi.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly);
|
||||
|
||||
headers.Write ("@property (nonatomic");
|
||||
if (fi.IsStatic)
|
||||
headers.Write (", class");
|
||||
if (read_only)
|
||||
headers.Write (", readonly");
|
||||
else
|
||||
headers.Write (", readwrite");
|
||||
var ft = fi.FieldType;
|
||||
var field_type = GetTypeName (ft);
|
||||
if (types.Contains (ft))
|
||||
field_type += " *";
|
||||
|
||||
var name = CamelCase (fi.Name);
|
||||
headers.WriteLine ($") {field_type} {name};");
|
||||
|
||||
// it's similar, but different from implementing a method
|
||||
|
||||
var type = fi.DeclaringType;
|
||||
var managed_type_name = GetObjCName (type);
|
||||
var return_type = GetReturnType (type, fi.FieldType);
|
||||
|
||||
implementation.Write (fi.IsStatic ? '+' : '-');
|
||||
implementation.WriteLine ($" ({return_type}) {CamelCase (fi.Name)}");
|
||||
implementation.WriteLine ("{");
|
||||
implementation.WriteLine ("\tstatic MonoClassField* __field = nil;");
|
||||
implementation.WriteLine ("\tif (!__field) {");
|
||||
implementation.WriteLine ("#if TOKENLOOKUP");
|
||||
var aname = type.Assembly.GetName ().Name;
|
||||
implementation.WriteLine ($"\t\t__field = mono_class_get_field ({managed_type_name}_class, 0x{fi.MetadataToken:X8});");
|
||||
implementation.WriteLine ("#else");
|
||||
implementation.WriteLine ($"\t\tconst char __field_name [] = \"{fi.Name}\";");
|
||||
implementation.WriteLine ($"\t\t__field = mono_class_get_field_from_name ({managed_type_name}_class, __field_name);");
|
||||
implementation.WriteLine ("#endif");
|
||||
implementation.WriteLine ("\t}");
|
||||
var instance = "nil";
|
||||
if (!fi.IsStatic) {
|
||||
implementation.WriteLine ($"\tMonoObject* __instance = mono_gchandle_get_target (_object->_handle);");
|
||||
instance = "__instance";
|
||||
}
|
||||
implementation.WriteLine ($"\tMonoObject* __result = mono_field_get_value_object (__mono_context.domain, __field, {instance});");
|
||||
ReturnValue (fi.FieldType);
|
||||
implementation.WriteLine ("}");
|
||||
implementation.WriteLine ();
|
||||
|
||||
if (read_only)
|
||||
return;
|
||||
// TODO write support
|
||||
}
|
||||
|
||||
public string GetReturnType (Type declaringType, Type returnType)
|
||||
|
@ -349,82 +441,78 @@ namespace ObjC {
|
|||
}
|
||||
|
||||
// TODO override with attribute ? e.g. [ObjC.Selector ("foo")]
|
||||
void ImplementMethod (bool isStatic, Type returnType, string name, ParameterInfo [] parametersInfo, Type type, string managed_name)
|
||||
void ImplementMethod (MethodInfo info, string name, PropertyInfo pi = null)
|
||||
{
|
||||
var type = info.DeclaringType;
|
||||
var managed_type_name = GetObjCName (type);
|
||||
var return_type = GetReturnType (type, returnType);
|
||||
StringBuilder parameters = new StringBuilder ();
|
||||
StringBuilder managed_parameters = new StringBuilder ();
|
||||
foreach (var p in parametersInfo) {
|
||||
if (parameters.Length > 0) {
|
||||
parameters.Append (' ');
|
||||
managed_parameters.Append (' ');
|
||||
}
|
||||
parameters.Append (":(").Append (GetTypeName (p.ParameterType)).Append (")").Append (p.Name);
|
||||
managed_parameters.Append (GetMonoName (p.ParameterType));
|
||||
var managed_name = info.Name;
|
||||
var return_type = GetReturnType (type, info.ReturnType);
|
||||
var parametersInfo = info.GetParameters ();
|
||||
|
||||
string objcsig;
|
||||
string monosig;
|
||||
GetSignatures (name, managed_name, (MemberInfo) pi ?? info, parametersInfo, out objcsig, out monosig);
|
||||
|
||||
if (pi == null) {
|
||||
headers.Write (info.IsStatic ? '+' : '-');
|
||||
headers.WriteLine ($" ({return_type}){objcsig};");
|
||||
}
|
||||
|
||||
implementation.Write (isStatic ? '+' : '-');
|
||||
implementation.WriteLine ($" ({return_type}) {name}{parameters}");
|
||||
implementation.Write (info.IsStatic ? '+' : '-');
|
||||
implementation.WriteLine ($" ({return_type}) {objcsig}");
|
||||
implementation.WriteLine ("{");
|
||||
implementation.WriteLine ($"\tconst char __method_name [] = \"{type.FullName}:{managed_name}({managed_parameters})\";");
|
||||
implementation.WriteLine ("\tstatic MonoMethod* __method = nil;");
|
||||
implementation.WriteLine ("\tif (!__method) {");
|
||||
//implementation.WriteLine ($"\t\t__lookup_class_{managed_type_name} ();");
|
||||
implementation.WriteLine ("#if TOKENLOOKUP");
|
||||
var aname = type.Assembly.GetName ().Name;
|
||||
implementation.WriteLine ($"\t\t__method = mono_get_method (__{aname}_image, 0x{info.MetadataToken:X8}, {managed_type_name}_class);");
|
||||
implementation.WriteLine ("#else");
|
||||
implementation.WriteLine ($"\t\tconst char __method_name [] = \"{type.FullName}:{monosig}\";");
|
||||
implementation.WriteLine ($"\t\t__method = mono_embeddinator_lookup_method (__method_name, {managed_type_name}_class);");
|
||||
implementation.WriteLine ("#endif");
|
||||
implementation.WriteLine ("\t}");
|
||||
|
||||
string postInvoke = String.Empty;
|
||||
var args = "nil";
|
||||
if (parametersInfo.Length > 0) {
|
||||
Generate (parametersInfo, out postInvoke);
|
||||
args = "__args";
|
||||
implementation.WriteLine ($"\tvoid* __args [{parametersInfo.Length}];");
|
||||
for (int i = 0; i < parametersInfo.Length; i++) {
|
||||
implementation.WriteLine ($"\t__args [{i}] = &{parametersInfo [i].Name};");
|
||||
}
|
||||
}
|
||||
|
||||
implementation.WriteLine ("\tMonoObject* __exception = nil;");
|
||||
var instance = "nil";
|
||||
if (!isStatic) {
|
||||
implementation.WriteLine ($"\tMonoObject* instance = mono_gchandle_get_target (_object->_handle);");
|
||||
instance = "instance";
|
||||
if (!info.IsStatic) {
|
||||
implementation.WriteLine ($"\tMonoObject* __instance = mono_gchandle_get_target (_object->_handle);");
|
||||
if (type.IsValueType) {
|
||||
implementation.WriteLine ($"\t\tvoid* __unboxed = mono_object_unbox (__instance);");
|
||||
instance = "__unboxed";
|
||||
} else {
|
||||
instance = "__instance";
|
||||
}
|
||||
}
|
||||
|
||||
implementation.Write ("\t");
|
||||
if (!IsVoid (returnType))
|
||||
if (!info.ReturnType.Is ("System", "Void"))
|
||||
implementation.Write ("MonoObject* __result = ");
|
||||
implementation.WriteLine ($"mono_runtime_invoke (__method, {instance}, {args}, &__exception);");
|
||||
|
||||
implementation.WriteLine ("\tif (__exception)");
|
||||
implementation.WriteLine ("\t\tmono_embeddinator_throw_exception (__exception);");
|
||||
ReturnValue (returnType);
|
||||
// ref and out parameters might need to be converted back
|
||||
implementation.Write (postInvoke);
|
||||
ReturnValue (info.ReturnType);
|
||||
implementation.WriteLine ("}");
|
||||
implementation.WriteLine ();
|
||||
}
|
||||
|
||||
public static bool IsVoid (Type t)
|
||||
{
|
||||
if (t.Name != "Void")
|
||||
return false;
|
||||
return (t.Namespace == "System");
|
||||
}
|
||||
|
||||
protected override void Generate (MethodInfo mi)
|
||||
{
|
||||
StringBuilder parameters = new StringBuilder ();
|
||||
foreach (var p in mi.GetParameters ()) {
|
||||
if (parameters.Length > 0)
|
||||
parameters.Append (' ');
|
||||
parameters.Append (":(").Append (GetTypeName (p.ParameterType)).Append (")").Append (p.Name);
|
||||
}
|
||||
|
||||
var return_type = GetReturnType (mi.DeclaringType, mi.ReturnType);
|
||||
var name = CamelCase (mi.Name);
|
||||
|
||||
headers.Write (mi.IsStatic ? '+' : '-');
|
||||
headers.WriteLine ($" ({return_type}){name}{parameters};");
|
||||
|
||||
ImplementMethod (mi.IsStatic, mi.ReturnType, name, mi.GetParameters (), mi.DeclaringType, mi.Name);
|
||||
string name;
|
||||
if (mi.IsSpecialName && mi.IsStatic && mi.Name.StartsWith ("op_", StringComparison.Ordinal))
|
||||
name = CamelCase (mi.Name.Substring (3));
|
||||
else
|
||||
name = CamelCase (mi.Name);
|
||||
ImplementMethod (mi, name);
|
||||
}
|
||||
|
||||
void ReturnValue (Type t)
|
||||
|
@ -480,12 +568,19 @@ namespace ObjC {
|
|||
// TODO override with attribute ? e.g. [Obj.Name ("XAMType")]
|
||||
public static string GetTypeName (Type t)
|
||||
{
|
||||
if (t.IsByRef)
|
||||
return GetTypeName (t.GetElementType ()) + "*";
|
||||
|
||||
if (t.IsEnum)
|
||||
return GetObjCName (t);
|
||||
|
||||
switch (Type.GetTypeCode (t)) {
|
||||
case TypeCode.Object:
|
||||
switch (t.Namespace) {
|
||||
case "System":
|
||||
switch (t.Name) {
|
||||
case "Object":
|
||||
case "ValueType":
|
||||
return "NSObject";
|
||||
case "Void":
|
||||
return "void";
|
||||
|
@ -520,7 +615,7 @@ namespace ObjC {
|
|||
case TypeCode.UInt64:
|
||||
return "unsigned long long";
|
||||
case TypeCode.String:
|
||||
return "NSString*";
|
||||
return "NSString *";
|
||||
default:
|
||||
throw new NotImplementedException ($"Converting type {t.Name} to a native type name");
|
||||
}
|
||||
|
@ -528,20 +623,24 @@ namespace ObjC {
|
|||
|
||||
public static string GetMonoName (Type t)
|
||||
{
|
||||
if (t.IsByRef)
|
||||
return GetMonoName (t.GetElementType ()) + "&";
|
||||
|
||||
if (t.IsEnum)
|
||||
return t.FullName;
|
||||
|
||||
switch (Type.GetTypeCode (t)) {
|
||||
case TypeCode.Object:
|
||||
switch (t.Namespace) {
|
||||
case "System":
|
||||
switch (t.Name) {
|
||||
case "Object":
|
||||
return "object";
|
||||
case "Void":
|
||||
return "void";
|
||||
default:
|
||||
throw new NotImplementedException ($"Converting type {t.Name} to a mono type name");
|
||||
return "object";
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException ($"Converting type {t.Name} to a mono type name");
|
||||
return t.FullName;
|
||||
}
|
||||
case TypeCode.Boolean:
|
||||
return "bool";
|
||||
|
@ -591,11 +690,5 @@ namespace ObjC {
|
|||
return String.Empty;
|
||||
return Char.ToUpperInvariant (s [0]) + s.Substring (1, s.Length - 1);
|
||||
}
|
||||
|
||||
// get a name that is safe to use from ObjC code
|
||||
public static string GetObjCName (Type t)
|
||||
{
|
||||
return t.FullName.Replace ('.', '_');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Definitions needed for the generated header files.
|
||||
*
|
||||
* (C) 2017 Microsoft, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Arrays
|
||||
*/
|
||||
|
||||
typedef struct MonoEmbedArray MonoEmbedArray;
|
||||
|
||||
/**
|
||||
* Objects
|
||||
*/
|
||||
|
||||
typedef struct MonoEmbedObject MonoEmbedObject;
|
|
@ -1,4 +1,4 @@
|
|||
/*
|
||||
/*
|
||||
* Mono managed-to-native support code.
|
||||
*
|
||||
* Author:
|
||||
|
@ -33,16 +33,14 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <errno.h>
|
||||
#include <libproc.h>
|
||||
#include <unistd.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#if defined(__OBJC__)
|
||||
#include <objc/runtime.h>
|
||||
#include <objc/objc-runtime.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -76,6 +74,10 @@ int mono_embeddinator_init(mono_embeddinator_context_t* ctx, const char* domain)
|
|||
|
||||
mono_embeddinator_set_context(ctx);
|
||||
|
||||
char cwd[PATH_MAX];
|
||||
getcwd(cwd, PATH_MAX);
|
||||
mono_domain_set_config(ctx->domain, cwd, "app.config");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -101,14 +103,10 @@ static GString* get_current_executable_path()
|
|||
if (path_override)
|
||||
return path_override;
|
||||
#if defined(__APPLE__)
|
||||
int ret;
|
||||
pid_t pid;
|
||||
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
|
||||
|
||||
pid = getpid();
|
||||
ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
|
||||
|
||||
return (ret > 0) ? g_string_new(pathbuf) : 0;
|
||||
char pathbuf [1024];
|
||||
uint32_t bufsize = sizeof (pathbuf);
|
||||
int ret = _NSGetExecutablePath (pathbuf, &bufsize);
|
||||
return ret == 0 ? g_string_new (pathbuf) : 0;
|
||||
#elif defined(_WIN32)
|
||||
HMODULE hModule = GetModuleHandleW(0);
|
||||
CHAR pathbuf[MAX_PATH];
|
||||
|
@ -159,8 +157,6 @@ MonoImage* mono_embeddinator_load_assembly(mono_embeddinator_context_t* ctx, con
|
|||
|
||||
MonoAssembly* mono_assembly = mono_domain_assembly_open(ctx->domain, path);
|
||||
|
||||
g_free (path);
|
||||
|
||||
if (!mono_assembly)
|
||||
{
|
||||
mono_embeddinator_error_t error;
|
||||
|
@ -168,9 +164,13 @@ MonoImage* mono_embeddinator_load_assembly(mono_embeddinator_context_t* ctx, con
|
|||
error.string = path;
|
||||
mono_embeddinator_error(error);
|
||||
|
||||
g_free (path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_free (path);
|
||||
|
||||
return mono_assembly_get_image(mono_assembly);
|
||||
}
|
||||
|
||||
|
@ -191,7 +191,6 @@ MonoClass* mono_embeddinator_search_class(const char* assembly, const char* _nam
|
|||
|
||||
char* path = mono_embeddinator_search_assembly(assembly);
|
||||
MonoAssembly* mono_assembly = mono_domain_assembly_open(ctx->domain, path);
|
||||
g_free (path);
|
||||
|
||||
if (mono_assembly == 0)
|
||||
{
|
||||
|
@ -201,6 +200,8 @@ MonoClass* mono_embeddinator_search_class(const char* assembly, const char* _nam
|
|||
mono_embeddinator_error(error);
|
||||
}
|
||||
|
||||
g_free (path);
|
||||
|
||||
MonoImage* image = mono_assembly_get_image(mono_assembly);
|
||||
MonoClass* klass = mono_class_from_name(image, _namespace, name);
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
#define MONO_EMBEDDINATOR_API MONO_EMBEDDINATOR_API_IMPORT
|
||||
#endif
|
||||
|
||||
#include "embeddinator.h"
|
||||
typedef uint16_t gunichar2;
|
||||
|
||||
typedef struct _GArray GArray;
|
||||
|
@ -202,24 +203,17 @@ typedef void (*mono_embeddinator_error_report_hook_t)(mono_embeddinator_error_t)
|
|||
MONO_EMBEDDINATOR_API
|
||||
void* mono_embeddinator_install_error_report_hook(mono_embeddinator_error_report_hook_t hook);
|
||||
|
||||
/**
|
||||
* Arrays
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
struct MonoEmbedArray
|
||||
{
|
||||
GArray* array;
|
||||
} MonoEmbedArray;
|
||||
};
|
||||
|
||||
/**
|
||||
* Objects
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
struct MonoEmbedObject
|
||||
{
|
||||
MonoClass* _class;
|
||||
uint32_t _handle;
|
||||
} MonoEmbedObject;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a MonoEmbedObject support object from a Mono object instance.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
bin/Debug/managed.dll: $(wildcard *.cs) managed.csproj
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/msbuild $(BUILD_FLAGS) managed.csproj || /Library/Frameworks/Mono.framework/Versions/Current/Commands/xbuild $(BUILD_FLAGS) managed.csproj
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/msbuild $(BUILD_FLAGS) managed.csproj
|
||||
|
||||
all: bin/Debug/managed.dll
|
||||
|
||||
|
|
|
@ -1,35 +1,42 @@
|
|||
#if NON_OBJC_SUPPORTED_TESTS
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System;
|
||||
|
||||
namespace Enums {
|
||||
|
||||
public enum Enum
|
||||
{
|
||||
Two = 2,
|
||||
Three
|
||||
public enum ByteEnum : byte {
|
||||
Zero = 0,
|
||||
Max = 255,
|
||||
}
|
||||
|
||||
public enum EnumByte : byte
|
||||
{
|
||||
Two = 2,
|
||||
Three
|
||||
public enum IntEnum /* default */ {
|
||||
Min = Int32.MinValue,
|
||||
Max = Int32.MaxValue,
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum EnumFlags
|
||||
{
|
||||
FlagOne = 1 << 0,
|
||||
FlagTwo = 1 << 2
|
||||
public enum ByteFlags {
|
||||
Empty = 0x0,
|
||||
Bit0 = 0x1,
|
||||
Bit1 = 0x2,
|
||||
Bit2 = 0x4,
|
||||
Bit3 = 0x8,
|
||||
Bit4 = 0x10,
|
||||
Bit5 = 0x20,
|
||||
Bit6 = 0x40,
|
||||
Bit7 = 0x80,
|
||||
}
|
||||
|
||||
public static class EnumTypes {
|
||||
public enum ShortEnum : short {
|
||||
Min = Int16.MinValue,
|
||||
Max = Int16.MaxValue,
|
||||
}
|
||||
|
||||
public static int PassEnum (Enum e) { return (int)e; }
|
||||
public static byte PassEnumByte (EnumByte e) { return (byte)e; }
|
||||
public static int PassEnumFlags (EnumFlags e) { return (int)e; }
|
||||
public static class Enumer {
|
||||
|
||||
public static ByteFlags Test (ByteEnum b, ref IntEnum i, out ShortEnum s)
|
||||
{
|
||||
s = b == ByteEnum.Max ? ShortEnum.Max : ShortEnum.Min;
|
||||
i = i == IntEnum.Min ? IntEnum.Max : IntEnum.Min;
|
||||
return ByteFlags.Bit5 | ByteFlags.Bit1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -31,13 +31,14 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="arrays.cs" />
|
||||
<Compile Include="enums.cs" />
|
||||
<Compile Include="properties.cs" />
|
||||
<Compile Include="namespaces.cs" />
|
||||
<Compile Include="types.cs" />
|
||||
<Compile Include="exceptions.cs" />
|
||||
<Compile Include="constructors.cs" />
|
||||
<Compile Include="methods.cs" />
|
||||
<Compile Include="structs.cs" />
|
||||
<Compile Include="enums.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Methods {
|
||||
|
||||
|
@ -19,4 +20,69 @@ namespace Methods {
|
|||
// only getter will be generated
|
||||
public int Id { get; private set; }
|
||||
}
|
||||
|
||||
public class Parameters {
|
||||
|
||||
public static string Concat (string first, string second)
|
||||
{
|
||||
if (first == null)
|
||||
return second;
|
||||
if (second == null)
|
||||
return first;
|
||||
return first + second;
|
||||
}
|
||||
|
||||
public static void Ref (ref bool boolean, ref string @string)
|
||||
{
|
||||
boolean = !boolean;
|
||||
@string = @string == null ? "hello" : null;
|
||||
}
|
||||
|
||||
public static void Out (string @string, out int length, out string upper)
|
||||
{
|
||||
length = @string == null ? 0 : @string.Length;
|
||||
upper = @string == null ? null : @string.ToUpperInvariant ();
|
||||
}
|
||||
}
|
||||
|
||||
public class Item {
|
||||
|
||||
// not generated
|
||||
internal Item (int id)
|
||||
{
|
||||
Integer = id;
|
||||
}
|
||||
|
||||
public int Integer { get; private set; }
|
||||
}
|
||||
|
||||
public static class Factory {
|
||||
|
||||
public static Item CreateItem (int id)
|
||||
{
|
||||
return new Item (id);
|
||||
}
|
||||
}
|
||||
|
||||
public class Collection {
|
||||
|
||||
List<Item> c = new List<Item> ();
|
||||
|
||||
public void Add (Item item)
|
||||
{
|
||||
c.Add (item);
|
||||
}
|
||||
|
||||
public void Remove (Item item)
|
||||
{
|
||||
c.Remove (item);
|
||||
}
|
||||
|
||||
public int Count => c.Count;
|
||||
|
||||
public Item this [int index] {
|
||||
get { return c [index]; }
|
||||
set { c [index] = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
using System;
|
||||
|
||||
namespace Structs {
|
||||
|
||||
public struct Point {
|
||||
|
||||
public static readonly Point Zero;
|
||||
|
||||
public Point (float x, float y)
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
public float X { get; private set; }
|
||||
|
||||
public float Y { get; private set; }
|
||||
|
||||
public static bool operator == (Point left, Point right)
|
||||
{
|
||||
return ((left.X == right.X) && (left.Y == right.Y));
|
||||
}
|
||||
|
||||
public static bool operator != (Point left, Point right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
public static Point operator + (Point left, Point right)
|
||||
{
|
||||
return new Point (left.X + right.X, left.Y + right.Y);
|
||||
}
|
||||
|
||||
public static Point operator - (Point left, Point right)
|
||||
{
|
||||
return new Point (left.X - right.X, left.Y - right.Y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,5 @@
|
|||
glib.c
|
||||
glib.h
|
||||
mono_embeddinator.c
|
||||
mono_embeddinator.h
|
||||
objc-support.m
|
||||
objc-support.h
|
||||
perf-cli
|
||||
test-cli
|
||||
debug
|
||||
release/
|
||||
# Xcode
|
||||
.DS_Store
|
||||
build/
|
||||
|
@ -24,3 +18,6 @@ profile
|
|||
*.moved-aside
|
||||
DerivedData
|
||||
.idea/
|
||||
i386
|
||||
x86_64
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
all: run-test perf xctest test-leaks
|
||||
all: run-test perf xctest test-leaks test-static test-dynamic
|
||||
|
||||
MONO=/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono
|
||||
|
||||
OBJC_GEN_DIR=../../objcgen
|
||||
OBJC_GEN=$(OBJC_GEN_DIR)/bin/Debug/objcgen.exe
|
||||
|
@ -10,45 +12,71 @@ MANAGED_DLL=$(MANAGED_DLL_DIR)/bin/Debug/managed.dll
|
|||
$(MANAGED_DLL): $(wildcard $(MANAGED_DLL_DIR)/*.cs)
|
||||
make -C $(MANAGED_DLL_DIR)
|
||||
|
||||
managed.dll: $(MANAGED_DLL)
|
||||
%/managed.dll: $(MANAGED_DLL)
|
||||
mkdir -p $(dir $@)
|
||||
cp $< $@
|
||||
|
||||
libmanaged.dylib: managed.dll $(OBJC_GEN)
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono --debug $(OBJC_GEN) --debug managed.dll -c
|
||||
debug/libmanaged.dylib: debug/managed.dll $(OBJC_GEN)
|
||||
$(MONO) --debug $(OBJC_GEN) --debug debug/managed.dll -c -o debug
|
||||
|
||||
test-cli: test-managed.m libmanaged.dylib
|
||||
clang -g $< -lmanaged -L. -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
|
||||
release/libmanaged.dylib: release/managed.dll $(OBJC_GEN)
|
||||
$(MONO) --debug $(OBJC_GEN) release/managed.dll -c -o release
|
||||
|
||||
perf-cli: perf-test.m libmanaged.dylib
|
||||
clang -g $< -lmanaged -L. -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
|
||||
debug/test-cli: test-managed.m debug/libmanaged.dylib
|
||||
clang -g -O0 $< -lmanaged -Ldebug -Idebug -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
|
||||
|
||||
run-test: test-cli
|
||||
./test-cli
|
||||
release/test-cli: test-managed.m release/libmanaged.dylib
|
||||
clang -O2 $< -lmanaged -Lrelease -Irelease -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
|
||||
|
||||
perf: perf-cli
|
||||
time ./perf-cli
|
||||
debug/perf-cli: perf-test.m debug/libmanaged.dylib
|
||||
clang -g -O0 $< -lmanaged -Ldebug -Idebug -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
|
||||
|
||||
release/perf-cli: perf-test.m release/libmanaged.dylib
|
||||
clang -O2 $< -lmanaged -Lrelease -Irelease -framework Foundation -o $@ -rpath @executable_path -fobjc-arc
|
||||
|
||||
run-%-cli-test: %/test-cli
|
||||
@echo "Test ($*)"
|
||||
./$<
|
||||
|
||||
run-%-perf-test: %/perf-cli
|
||||
@echo "Performance ($*)"
|
||||
time ./$<
|
||||
|
||||
run-test: run-debug-cli-test run-release-cli-test
|
||||
|
||||
perf: run-debug-perf-test run-release-perf-test
|
||||
|
||||
clean:
|
||||
@rm -f *.h glib.c mono_embeddinator.c bindings.m *.dylib *.dll test-cli perf-cli
|
||||
@rm -rf debug release
|
||||
|
||||
xctest:
|
||||
xctest: debug/libmanaged.dylib
|
||||
xcodebuild test -project libmanaged/*.xcodeproj -scheme Tests
|
||||
|
||||
test-leaks: test-cli-leaks test-perf-leaks test-xctest-leaks
|
||||
|
||||
../leaktest/bin/Debug/leaktest.exe: $(wildcard ../leaktest/*.cs) libLeakCheckAtExit.dylib
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/xbuild /verbosity:quiet /nologo ../leaktest/leaktest.csproj
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/msbuild /verbosity:quiet /nologo ../leaktest/leaktest.csproj
|
||||
|
||||
test-cli-leaks: test-cli ../leaktest/bin/Debug/leaktest.exe
|
||||
test-cli-leaks: debug/test-cli ../leaktest/bin/Debug/leaktest.exe
|
||||
mono --debug ../leaktest/bin/Debug/leaktest.exe $(abspath $<)
|
||||
|
||||
test-perf-leaks: perf-cli ../leaktest/bin/Debug/leaktest.exe
|
||||
test-perf-leaks: debug/perf-cli ../leaktest/bin/Debug/leaktest.exe
|
||||
mono --debug ../leaktest/bin/Debug/leaktest.exe $(abspath $<)
|
||||
|
||||
test-xctest-leaks: ../leaktest/bin/Debug/leaktest.exe $(MANAGED_DLL) libmanaged.dylib
|
||||
test-xctest-leaks: ../leaktest/bin/Debug/leaktest.exe $(MANAGED_DLL) debug/libmanaged.dylib
|
||||
rm -Rf libmanaged/Tests.xctest
|
||||
xcodebuild -quiet build-for-testing -project libmanaged/*.xcodeproj -scheme Tests CONFIGURATION_BUILD_DIR=$(abspath libmanaged)
|
||||
mono --debug ../leaktest/bin/Debug/leaktest.exe `xcrun -f xctest` -XCTest All $(abspath libmanaged/Tests.xctest)
|
||||
|
||||
libLeakCheckAtExit.dylib: leak-at-exit.c
|
||||
clang -arch i386 -arch x86_64 -shared $< -o $@
|
||||
|
||||
test-static: test-static-macos test-static-ios test-static-tvos test-static-watchos
|
||||
|
||||
test-static-%: $(MANAGED_DLL) $(OBJC_GEN)
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono --debug $(OBJC_GEN) --debug $(MANAGED_DLL) -c --outdir=build/$@-temp-dir --target=staticlibrary --platform=$*
|
||||
|
||||
test-dynamic: test-dynamic-macos test-dynamic-ios test-dynamic-tvos test-dynamic-watchos
|
||||
|
||||
test-dynamic-%: $(MANAGED_DLL) $(OBJC_GEN)
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono --debug $(OBJC_GEN) --debug $(MANAGED_DLL) -c --outdir=build/$@-temp-dir --target=dylib --platform=$*
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
XCTAssertFalse ([query isSecret], "instance property getter only 3");
|
||||
// setter only property turned into method, so different syntax
|
||||
[query set_Secret: 1];
|
||||
[query setSecret: 1];
|
||||
XCTAssertTrue ([query isSecret], "instance property getter only 4");
|
||||
}
|
||||
|
||||
|
@ -90,30 +90,108 @@
|
|||
|
||||
id super_unique_default_init = [[Constructors_SuperUnique alloc] init];
|
||||
XCTAssert ([super_unique_default_init id] == 411, "super id");
|
||||
|
||||
// FIXME - this should not be allowed, that .ctor is not available to call in .NET as it is not re-declared in SuperUnique
|
||||
id super_unique_init_id = [[Constructors_SuperUnique alloc] initWithId:42];
|
||||
XCTAssert ([super_unique_init_id id] == 42, "id");
|
||||
|
||||
Constructors_Implicit* implicit = [[Constructors_Implicit alloc] init];
|
||||
XCTAssertEqualObjects (@"OK", [implicit testResult], "implicit");
|
||||
|
||||
Constructors_AllTypeCode* all1 = [[Constructors_AllTypeCode alloc] initWithB1:true C2:USHRT_MAX S:@"Mono"];
|
||||
Constructors_AllTypeCode* all1 = [[Constructors_AllTypeCode alloc] initWithB1:true c2:USHRT_MAX s:@"Mono"];
|
||||
XCTAssertTrue ([all1 testResult], "all1");
|
||||
|
||||
Constructors_AllTypeCode* all2 = [[Constructors_AllTypeCode alloc] initWithI8:SCHAR_MAX I16:SHRT_MAX I32:INT_MAX I64:LONG_MAX];
|
||||
Constructors_AllTypeCode* all2 = [[Constructors_AllTypeCode alloc] initWithI8:SCHAR_MAX i16:SHRT_MAX i32:INT_MAX i64:LONG_MAX];
|
||||
XCTAssertTrue ([all2 testResult], "all2");
|
||||
|
||||
Constructors_AllTypeCode* all3 = [[Constructors_AllTypeCode alloc] initWithU8:UCHAR_MAX U16:USHRT_MAX U32:UINT_MAX U64:ULONG_MAX];
|
||||
Constructors_AllTypeCode* all3 = [[Constructors_AllTypeCode alloc] initWithU8:UCHAR_MAX u16:USHRT_MAX u32:UINT_MAX u64:ULONG_MAX];
|
||||
XCTAssertTrue ([all3 testResult], "all3");
|
||||
|
||||
Constructors_AllTypeCode* all4 = [[Constructors_AllTypeCode alloc] initWithF32:FLT_MAX F64:DBL_MAX];
|
||||
Constructors_AllTypeCode* all4 = [[Constructors_AllTypeCode alloc] initWithF32:FLT_MAX f64:DBL_MAX];
|
||||
XCTAssertTrue ([all4 testResult], "all4");
|
||||
}
|
||||
|
||||
- (void)testMethods {
|
||||
id static_method = [Methods_Static create: 1];
|
||||
id static_method = [Methods_Static createId: 1];
|
||||
XCTAssert ([static_method id] == 1, "create id");
|
||||
|
||||
XCTAssertNil ([Methods_Parameters concatFirst:nil second:nil], "string input nil + nil]");
|
||||
XCTAssertEqualObjects (@"first", [Methods_Parameters concatFirst:@"first" second:nil], "string first + nil");
|
||||
XCTAssertEqualObjects (@"second", [Methods_Parameters concatFirst:nil second:@"second"], "string nil + second");
|
||||
XCTAssertEqualObjects (@"firstsecond", [Methods_Parameters concatFirst:@"first" second:@"second"], "string first + second");
|
||||
|
||||
bool b = true;
|
||||
NSString* s = nil;
|
||||
[Methods_Parameters refBoolean:&b string:&s];
|
||||
XCTAssertFalse (b, "ref bool 1");
|
||||
XCTAssertEqualObjects (@"hello", s, "ref string 1");
|
||||
|
||||
[Methods_Parameters refBoolean:&b string:&s];
|
||||
XCTAssertTrue (b, "ref bool 2");
|
||||
XCTAssertNil (s, "ref string 2");
|
||||
|
||||
int l;
|
||||
[Methods_Parameters outString:nil length:&l upper:&s];
|
||||
XCTAssert (l == 0, "out int 1");
|
||||
XCTAssertNil (s, "out string 1");
|
||||
|
||||
[Methods_Parameters outString:@"Xamarin" length:&l upper:&s];
|
||||
XCTAssert (l == 7, "out int 2");
|
||||
XCTAssertEqualObjects (@"XAMARIN", s, "ref string 2");
|
||||
|
||||
id item = [Methods_Factory createItemId:1];
|
||||
XCTAssert ([item integer] == 1, "indirect creation 1");
|
||||
|
||||
Methods_Collection *collection = [[Methods_Collection alloc] init];
|
||||
XCTAssert ([collection count] == 0, "count 0");
|
||||
[collection addItem:item];
|
||||
XCTAssert ([collection count] == 1, "count 1");
|
||||
XCTAssert ([[collection getItem:0] integer] == [item integer], "get 1");
|
||||
|
||||
id item2 = [Methods_Factory createItemId:2];
|
||||
[collection setItem:0 value:item2];
|
||||
XCTAssert ([collection count] == 1, "count 2");
|
||||
XCTAssert ([[collection getItem:0] integer] == [item2 integer], "get 2");
|
||||
|
||||
[collection removeItem:item]; // not there
|
||||
XCTAssert ([collection count] == 1, "count 3");
|
||||
|
||||
[collection removeItem:item2];
|
||||
XCTAssert ([collection count] == 0, "count 4");
|
||||
}
|
||||
|
||||
- (void) testStructs {
|
||||
id p1 = [[Structs_Point alloc] initWithX:1.0f y:-1.0f];
|
||||
XCTAssert ([p1 x] == 1.0f, "x 1");
|
||||
XCTAssert ([p1 y] == -1.0f, "y 1");
|
||||
|
||||
id p2 = [[Structs_Point alloc] initWithX:2.0f y:-2.0f];
|
||||
XCTAssert ([p2 x] == 2.0f, "x 2");
|
||||
XCTAssert ([p2 y] == -2.0f, "y 2");
|
||||
|
||||
XCTAssert ([Structs_Point equality:p1 right:p1], "p1 == p1");
|
||||
XCTAssert ([Structs_Point equality:p2 right:p2], "p2 == p2");
|
||||
XCTAssert ([Structs_Point inequality:p1 right:p2], "p1 != p2");
|
||||
|
||||
id p3 = [Structs_Point addition:p1 right:p2];
|
||||
XCTAssert ([p3 x] == 3.0f, "x 3");
|
||||
XCTAssert ([p3 y] == -3.0f, "y 3");
|
||||
|
||||
id p4 = [Structs_Point subtraction:p3 right:p2];
|
||||
XCTAssert ([Structs_Point equality:p4 right:p1], "p4 == p1");
|
||||
|
||||
id z = [Structs_Point zero];
|
||||
XCTAssert ([z x] == 0.0f, "x 4");
|
||||
XCTAssert ([z y] == 0.0f, "y 4");
|
||||
}
|
||||
|
||||
- (void) testEnums {
|
||||
Enums_IntEnum i = Enums_IntEnumMin;
|
||||
Enums_ShortEnum s;
|
||||
Enums_ByteFlags f = [Enums_Enumer testB:Enums_ByteEnumMax i:&i s:&s];
|
||||
XCTAssert (f == 0x22, "return flag 1");
|
||||
XCTAssert (i == Enums_IntEnumMax, "ref enum 1");
|
||||
XCTAssert (s == Enums_ShortEnumMax, "out enum 1");
|
||||
|
||||
f = [Enums_Enumer testB:Enums_ByteEnumZero i:&i s:&s];
|
||||
XCTAssert (i == Enums_IntEnumMin, "ref enum 2");
|
||||
XCTAssert (s == Enums_ShortEnumMin, "out enum 2");
|
||||
}
|
||||
|
||||
- (void)testStaticCallPerformance {
|
||||
|
|
|
@ -29,8 +29,8 @@
|
|||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
7E3455791E92B06100FCAD74 /* libmanaged.dylib.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = libmanaged.dylib.dSYM; path = ../libmanaged.dylib.dSYM; sourceTree = "<group>"; };
|
||||
7E5081F01E928C24008125B5 /* libmanaged.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmanaged.dylib; path = ../libmanaged.dylib; sourceTree = "<group>"; };
|
||||
7E3455791E92B06100FCAD74 /* libmanaged.dylib.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = libmanaged.dylib.dSYM; path = ../debug/libmanaged.dylib.dSYM; sourceTree = "<group>"; };
|
||||
7E5081F01E928C24008125B5 /* libmanaged.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmanaged.dylib; path = ../debug/libmanaged.dylib; sourceTree = "<group>"; };
|
||||
D1B0CD801E904EA6008FF17D /* managed.dll */ = {isa = PBXFileReference; lastKnownFileType = file; name = managed.dll; path = ../../../managed/bin/Debug/managed.dll; sourceTree = "<group>"; };
|
||||
D1B0CD811E904EA6008FF17D /* managed.pdb */ = {isa = PBXFileReference; lastKnownFileType = file; name = managed.pdb; path = ../../../managed/bin/Debug/managed.pdb; sourceTree = "<group>"; };
|
||||
D1E2B21F1E8F4FBB00846AF8 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -166,7 +166,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "make -C .. libmanaged.dylib\n";
|
||||
shellScript = "make -C .. debug/libmanaged.dylib\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
@ -276,11 +276,11 @@
|
|||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
HEADER_SEARCH_PATHS = ..;
|
||||
HEADER_SEARCH_PATHS = ../debug;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
..,
|
||||
../debug,
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.xamarin.Tests;
|
||||
|
@ -294,11 +294,11 @@
|
|||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = YES;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
HEADER_SEARCH_PATHS = ..;
|
||||
HEADER_SEARCH_PATHS = ../debug;
|
||||
INFOPLIST_FILE = Tests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
..,
|
||||
../debug,
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.xamarin.Tests;
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Xamarin
|
||||
{
|
||||
// A class that creates temporary directories next to the test assembly, and cleans the output on startup
|
||||
// Advantages:
|
||||
// * The temporary directories are automatically cleaned on Wrench (unlike /tmp, which isn't)
|
||||
// * The temporary directories stay after a test is run (until a new test run is started),
|
||||
// which makes it easier to re-run (copy-paste) commands that failed.
|
||||
public static class Cache // Not really a cache (since the root directory is cleaned in the cctor), but I couldn't come up with a better name.
|
||||
{
|
||||
static string root;
|
||||
static int last_number;
|
||||
|
||||
static Cache ()
|
||||
{
|
||||
root = Path.Combine (Path.GetDirectoryName (System.Reflection.Assembly.GetExecutingAssembly ().Location), "tmp-test-dir");
|
||||
if (Directory.Exists (root))
|
||||
Directory.Delete (root, true);
|
||||
Directory.CreateDirectory (root);
|
||||
}
|
||||
|
||||
[DllImport ("libc", SetLastError = true)]
|
||||
static extern int mkdir (string path, ushort mode);
|
||||
|
||||
public static string CreateTemporaryDirectory (string name = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty (name)) {
|
||||
var calling_method = new System.Diagnostics.StackFrame (1).GetMethod ();
|
||||
if (calling_method != null) {
|
||||
name = calling_method.DeclaringType.FullName + "." + calling_method.Name;
|
||||
} else {
|
||||
name = "unknown-test";
|
||||
}
|
||||
}
|
||||
|
||||
var rv = Path.Combine (root, name);
|
||||
for (int i = last_number; i < 10000 + last_number; i++) {
|
||||
// There's no way to know if Directory.CreateDirectory
|
||||
// created the directory or not (which would happen if the directory
|
||||
// already existed). Checking if the directory exists before
|
||||
// creating it would result in a race condition if multiple
|
||||
// threads create temporary directories at the same time.
|
||||
if (mkdir (rv, Convert.ToUInt16 ("777", 8)) == 0) {
|
||||
last_number = i;
|
||||
return rv;
|
||||
}
|
||||
rv = Path.Combine (root, name + i);
|
||||
}
|
||||
|
||||
throw new Exception ("Could not create temporary directory");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,11 +14,12 @@ namespace DriverTest {
|
|||
Random rnd = new Random ();
|
||||
string valid = Path.Combine (Path.GetTempPath (), "output-" + rnd.Next ().ToString ());
|
||||
try {
|
||||
Driver.OutputDirectory = valid;
|
||||
var embedder = new Embedder ();
|
||||
embedder.OutputDirectory = valid;
|
||||
Assert.That (Directory.Exists (valid), "valid");
|
||||
|
||||
try {
|
||||
Driver.OutputDirectory = "/:output";
|
||||
embedder.OutputDirectory = "/:output";
|
||||
Assert.Fail ("invalid");
|
||||
} catch (EmbeddinatorException ee) {
|
||||
Assert.True (ee.Error, "Error");
|
||||
|
@ -34,8 +35,9 @@ namespace DriverTest {
|
|||
{
|
||||
var valid = new string [] { "ObjC", "Obj-C", "ObjectiveC", "objective-c" };
|
||||
foreach (var t in valid) {
|
||||
Driver.SetTarget (t);
|
||||
Assert.That (Driver.TargetLanguage, Is.EqualTo (TargetLanguage.ObjectiveC), t);
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetTarget (t);
|
||||
Assert.That (embedder.TargetLanguage, Is.EqualTo (TargetLanguage.ObjectiveC), t);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,7 +47,8 @@ namespace DriverTest {
|
|||
var notsupported = new string [] { "C", "C++", "Java" };
|
||||
foreach (var t in notsupported) {
|
||||
try {
|
||||
Driver.SetTarget (t);
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetTarget (t);
|
||||
}
|
||||
catch (EmbeddinatorException ee) {
|
||||
Assert.True (ee.Error, "Error");
|
||||
|
@ -58,7 +61,8 @@ namespace DriverTest {
|
|||
public void Target_Invalid ()
|
||||
{
|
||||
try {
|
||||
Driver.SetTarget ("invalid");
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetTarget ("invalid");
|
||||
}
|
||||
catch (EmbeddinatorException ee) {
|
||||
Assert.True (ee.Error, "Error");
|
||||
|
@ -71,23 +75,27 @@ namespace DriverTest {
|
|||
{
|
||||
var valid = new string [] { "OSX", "MacOSX", "MacOS", "Mac" };
|
||||
foreach (var p in valid) {
|
||||
Driver.SetPlatform (p);
|
||||
Assert.That (Driver.Platform, Is.EqualTo (Platform.macOS), p);
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetPlatform (p);
|
||||
Assert.That (embedder.Platform, Is.EqualTo (Platform.macOS), p);
|
||||
}
|
||||
|
||||
foreach (var p in new string [] { "ios", "iOS" }) {
|
||||
Driver.SetPlatform (p);
|
||||
Assert.That (Driver.Platform, Is.EqualTo (Platform.iOS), p);
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetPlatform (p);
|
||||
Assert.That (embedder.Platform, Is.EqualTo (Platform.iOS), p);
|
||||
}
|
||||
|
||||
foreach (var p in new string [] { "tvos", "tvOS" }) {
|
||||
Driver.SetPlatform (p);
|
||||
Assert.That (Driver.Platform, Is.EqualTo (Platform.tvOS), p);
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetPlatform (p);
|
||||
Assert.That (embedder.Platform, Is.EqualTo (Platform.tvOS), p);
|
||||
}
|
||||
|
||||
foreach (var p in new string [] { "watchos", "watchOS" }) {
|
||||
Driver.SetPlatform (p);
|
||||
Assert.That (Driver.Platform, Is.EqualTo (Platform.watchOS), p);
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetPlatform (p);
|
||||
Assert.That (embedder.Platform, Is.EqualTo (Platform.watchOS), p);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +105,8 @@ namespace DriverTest {
|
|||
var notsupported = new string [] { "Windows", "Android", "iOS", "tvOS", "watchOS" };
|
||||
foreach (var p in notsupported) {
|
||||
try {
|
||||
Driver.SetPlatform (p);
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetPlatform (p);
|
||||
} catch (EmbeddinatorException ee) {
|
||||
Assert.True (ee.Error, "Error");
|
||||
Assert.That (ee.Code, Is.EqualTo (3), "Code");
|
||||
|
@ -109,7 +118,8 @@ namespace DriverTest {
|
|||
public void Platform_Invalid ()
|
||||
{
|
||||
try {
|
||||
Driver.SetPlatform ("invalid");
|
||||
var embedder = new Embedder ();
|
||||
embedder.SetPlatform ("invalid");
|
||||
} catch (EmbeddinatorException ee) {
|
||||
Assert.True (ee.Error, "Error");
|
||||
Assert.That (ee.Code, Is.EqualTo (3), "Code");
|
||||
|
@ -141,21 +151,33 @@ namespace DriverTest {
|
|||
[Test]
|
||||
public void CompilationTarget ()
|
||||
{
|
||||
var embedder = new Embedder ();
|
||||
Asserts.ThrowsEmbeddinatorException (5, "The compilation target `invalid` is not valid.", () => Driver.Main2 (new [] { "--target=invalid" }));
|
||||
Asserts.ThrowsEmbeddinatorException (5, "The compilation target `invalid` is not valid.", () => Driver.SetCompilationTarget ("invalid"));
|
||||
Asserts.ThrowsEmbeddinatorException (5, "The compilation target `invalid` is not valid.", () => embedder.SetCompilationTarget ("invalid"));
|
||||
|
||||
foreach (var ct in new string [] { "library", "sharedlibrary", "dylib" }) {
|
||||
Driver.SetCompilationTarget (ct);
|
||||
Assert.That (Driver.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.SharedLibrary), ct);
|
||||
embedder.SetCompilationTarget (ct);
|
||||
Assert.That (embedder.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.SharedLibrary), ct);
|
||||
}
|
||||
foreach (var ct in new string [] { "framework" }) {
|
||||
Driver.SetCompilationTarget (ct);
|
||||
Assert.That (Driver.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.Framework), ct);
|
||||
embedder.SetCompilationTarget (ct);
|
||||
Assert.That (embedder.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.Framework), ct);
|
||||
}
|
||||
foreach (var ct in new string [] { "static", "staticlibrary" }) {
|
||||
Driver.SetCompilationTarget (ct);
|
||||
Assert.That (Driver.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.StaticLibrary), ct);
|
||||
embedder.SetCompilationTarget (ct);
|
||||
Assert.That (embedder.CompilationTarget, Is.EqualTo (global::Embeddinator.CompilationTarget.StaticLibrary), ct);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ABI ()
|
||||
{
|
||||
Driver.Main2 (new string [] { "--abi=armv7" });
|
||||
CollectionAssert.AreEquivalent (new string [] { "armv7" }, Driver.CurrentEmbedder.ABIs, "armv7");
|
||||
|
||||
// We don't validate when setting the option
|
||||
Driver.Main2 (new string [] { "--abi=any" });
|
||||
CollectionAssert.AreEquivalent (new string [] { "any" }, Driver.CurrentEmbedder.ABIs, "any");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using NUnit.Framework;
|
||||
using Embeddinator;
|
||||
|
||||
namespace DriverTest
|
||||
{
|
||||
[TestFixture]
|
||||
public class EmbedderTest
|
||||
{
|
||||
[Test]
|
||||
[TestCase (Platform.iOS, "armv7k")]
|
||||
[TestCase (Platform.macOS, "armv7")]
|
||||
[TestCase (Platform.tvOS, "armv7")]
|
||||
[TestCase (Platform.watchOS, "arm64")]
|
||||
[TestCase (Platform.iOS, "invalid")]
|
||||
public void Invalid_ABI (Platform platform, string abi)
|
||||
{
|
||||
var dll = CompileLibrary (platform);
|
||||
string valid;
|
||||
switch (platform) {
|
||||
case Platform.iOS:
|
||||
valid = "armv7, armv7s, arm64, i386, x86_64";
|
||||
break;
|
||||
case Platform.macOS:
|
||||
valid = "i386, x86_64";
|
||||
break;
|
||||
case Platform.tvOS:
|
||||
valid = "arm64, x86_64";
|
||||
break;
|
||||
case Platform.watchOS:
|
||||
valid = "armv7k, i386";
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
Asserts.ThrowsEmbeddinatorException (8, $"The architecture '{abi}' is not valid for {platform}. Valid architectures for {platform} are: {valid}", () => Driver.Main2 ("--platform", platform.ToString (), "--abi", abi, "-c", dll, "-o", Xamarin.Cache.CreateTemporaryDirectory ()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCase (Platform.macOS, "x86_64")]
|
||||
public void ABI (Platform platform, string abi)
|
||||
{
|
||||
var dll = CompileLibrary (platform);
|
||||
var tmpdir = Xamarin.Cache.CreateTemporaryDirectory ();
|
||||
Driver.Main2 ("--platform", platform.ToString (), "--abi", abi, "-c", dll, "-o", tmpdir);
|
||||
|
||||
string output;
|
||||
int exitCode;
|
||||
Assert.IsTrue (Embedder.RunProcess ("xcrun", $"lipo -info {tmpdir}/libLibrary.dylib", out exitCode, out output), "lipo");
|
||||
StringAssert.IsMatch ($"Non-fat file: .* is architecture: {abi}", output, "architecture");
|
||||
}
|
||||
|
||||
string CompileLibrary (Platform platform, string code = null)
|
||||
{
|
||||
int exitCode;
|
||||
var tmpdir = Xamarin.Cache.CreateTemporaryDirectory ();
|
||||
var cs_path = Path.Combine (tmpdir, "library.cs");
|
||||
var dll_path = Path.Combine (tmpdir, "library.dll");
|
||||
|
||||
if (code == null)
|
||||
code = "public class Test { public static void X () {} }";
|
||||
|
||||
File.WriteAllText (cs_path, code);
|
||||
|
||||
if (!Embedder.RunProcess ("/Library/Frameworks/Mono.framework/Versions/Current/bin/csc", $"/target:library {Embedder.Quote (cs_path)} /out:{Embedder.Quote (dll_path)}", out exitCode))
|
||||
Assert.Fail ("Failed to compile test code");
|
||||
|
||||
return dll_path;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using ObjC;
|
||||
using Embeddinator;
|
||||
|
||||
using IKVM.Reflection;
|
||||
using Type = IKVM.Reflection.Type;
|
||||
|
||||
namespace ObjCGeneratorTest {
|
||||
|
||||
[TestFixture]
|
||||
public class ExtensionsTest {
|
||||
|
||||
public static Universe Universe { get; } = new Universe (UniverseOptions.None);
|
||||
|
||||
public static Assembly mscorlib { get; } = Universe.Load ("mscorlib.dll");
|
||||
|
||||
[Test]
|
||||
public void Is ()
|
||||
{
|
||||
Assert.True (mscorlib.GetType ("System.Void").Is ("System", "Void"), "void");
|
||||
Assert.False (mscorlib.GetType ("System.Object").Is ("System", "Void"), "object");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HasAttribute ()
|
||||
{
|
||||
var fa = mscorlib.GetType ("System.IO.FileAccess");
|
||||
Assert.True (fa.HasCustomAttribute ("System", "FlagsAttribute"), "Has");
|
||||
Assert.False (fa.HasCustomAttribute ("System", "Flags"), "Has Not");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -47,7 +47,7 @@ namespace ObjCGeneratorTest {
|
|||
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.UInt64")), Is.EqualTo ("unsigned long long"), "ulong");
|
||||
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Single")), Is.EqualTo ("float"), "float");
|
||||
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Double")), Is.EqualTo ("double"), "double");
|
||||
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.String")), Is.EqualTo ("NSString*"), "string");
|
||||
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.String")), Is.EqualTo ("NSString *"), "string");
|
||||
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Object")), Is.EqualTo ("NSObject"), "object");
|
||||
Assert.That (ObjCGenerator.GetTypeName (mscorlib.GetType ("System.Void")), Is.EqualTo ("void"), "void");
|
||||
}
|
||||
|
@ -78,13 +78,6 @@ namespace ObjCGeneratorTest {
|
|||
Assert.That (ObjCGenerator.GetMonoName (mscorlib.GetType ("System.Object")), Is.EqualTo ("object"), "object");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsVoid ()
|
||||
{
|
||||
Assert.True (ObjCGenerator.IsVoid (mscorlib.GetType ("System.Void")), "void");
|
||||
Assert.False (ObjCGenerator.IsVoid (mscorlib.GetType ("System.Object")), "object");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetObjCName ()
|
||||
{
|
||||
|
|
|
@ -34,6 +34,9 @@
|
|||
<Compile Include="ObjCGeneratorTest.cs" />
|
||||
<Compile Include="DriverTest.cs" />
|
||||
<Compile Include="Asserts.cs" />
|
||||
<Compile Include="EmbedderTest.cs" />
|
||||
<Compile Include="Cache.cs" />
|
||||
<Compile Include="ExtensionsTest.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче