[objc] Add support fat and static libraries for all platforms. (#97)

[objc] Add support fat and static libraries for all platforms.
This commit is contained in:
Rolf Bjarne Kvinge 2017-04-11 15:25:40 +02:00 коммит произвёл GitHub
Родитель 2633f39627
Коммит 7de0e3f0cc
4 изменённых файлов: 246 добавлений и 27 удалений

Просмотреть файл

@ -228,59 +228,263 @@ namespace Embeddinator {
return 0; return 0;
} }
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;
}
static int Compile () static int Compile ()
{ {
Console.WriteLine ("Compiling binding code..."); Console.WriteLine ("Compiling binding code...");
BuildInfo [] build_infos;
switch (Platform) { switch (Platform) {
case Platform.macOS: case Platform.macOS:
build_infos = new BuildInfo [] {
new BuildInfo { Sdk = "MacOSX", Architectures = new string [] { "i386", "x86_64" }, SdkName = "macosx", MinVersion = "10.7" },
};
break; break;
case Platform.iOS: 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: 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: 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); 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);
} }
switch (CompilationTarget) { switch (CompilationTarget) {
case CompilationTarget.SharedLibrary: case CompilationTarget.SharedLibrary:
case CompilationTarget.StaticLibrary:
break; break;
case CompilationTarget.Framework: case CompilationTarget.Framework:
case CompilationTarget.StaticLibrary:
throw new NotImplementedException ($"Compilation target: {CompilationTarget}"); throw new NotImplementedException ($"Compilation target: {CompilationTarget}");
default: 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); 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 "); var lipo_files = new List<string> ();
if (Debug) var output_file = string.Empty;
options.Append ("-g -O0 ");
options.Append ("-fobjc-arc "); var files = new string [] {
options.Append ("-DMONO_EMBEDDINATOR_DLL_EXPORT "); Path.Combine (OutputDirectory, "glib.c"),
options.Append ("-framework CoreFoundation "); Path.Combine (OutputDirectory, "mono_embeddinator.c"),
options.Append ("-framework Foundation "); Path.Combine (OutputDirectory, "objc-support.m"),
options.Append ("-I\"/Library/Frameworks/Mono.framework/Versions/Current/include/mono-2.0\" -L\"/Library/Frameworks/Mono.framework/Versions/Current/lib/\" -lmonosgen-2.0 "); Path.Combine (OutputDirectory, "bindings.m"),
options.Append ("glib.c mono_embeddinator.c objc-support.m bindings.m "); };
options.Append ("-ObjC -lobjc ");
switch (CompilationTarget) { switch (CompilationTarget) {
case CompilationTarget.SharedLibrary: case CompilationTarget.SharedLibrary:
options.Append ($"-dynamiclib "); output_file = $"lib{LibraryName}.dylib";
options.Append ($"-install_name @rpath/lib{LibraryName}.dylib ");
options.Append ($"-o lib{LibraryName}.dylib ");
break; break;
case CompilationTarget.StaticLibrary: case CompilationTarget.StaticLibrary:
throw new NotImplementedException ("compile to static library"); output_file = $"{LibraryName}.a";
case CompilationTarget.Framework: break;
throw new NotImplementedException ("compile to framework");
default: 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); 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}"); int exitCode;
var p = Process.Start ("xcrun", options.ToString ());
p.WaitForExit (); foreach (var build_info in build_infos) {
return p.ExitCode; 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 ");
common_options.Append ("-fobjc-arc ");
common_options.Append ("-ObjC ");
common_options.Append ($"-arch {arch} ");
common_options.Append ($"-isysroot /Applications/Xcode.app/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;
}
static 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);
}
}
static int RunProcess (string filename, string arguments)
{
Console.WriteLine ($"\t{filename} {arguments}");
using (var p = Process.Start (filename, arguments)) {
p.WaitForExit ();
return p.ExitCode;
}
}
static bool RunProcess (string filename, string arguments, out int exitCode)
{
exitCode = RunProcess (filename, arguments);
return exitCode == 0;
}
static bool Xcrun (StringBuilder options, out int exitCode)
{
return RunProcess ("xcrun", options.ToString (), out exitCode);
}
static 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);
}
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 ();
} }
} }
} }

Просмотреть файл

@ -151,15 +151,17 @@ namespace ObjC {
var native_name = GetTypeName (t); var native_name = GetTypeName (t);
headers.WriteLine (); headers.WriteLine ();
headers.WriteLine ($"// {t.AssemblyQualifiedName}"); 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 ("\tMonoEmbedObject* _object;");
}
headers.WriteLine ("}");
headers.WriteLine (); headers.WriteLine ();
implementation.WriteLine (); implementation.WriteLine ();
implementation.WriteLine ($"// {t.AssemblyQualifiedName}"); implementation.WriteLine ($"// {t.AssemblyQualifiedName}");
implementation.WriteLine ($"@implementation {native_name} {{"); implementation.WriteLine ($"@implementation {native_name} {{");
// our internal field is only needed once in the type hierarchy // 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 ("}");
implementation.WriteLine (); implementation.WriteLine ();

3
tests/objc-cli/.gitignore поставляемый
Просмотреть файл

@ -24,3 +24,6 @@ profile
*.moved-aside *.moved-aside
DerivedData DerivedData
.idea/ .idea/
i386
x86_64

Просмотреть файл

@ -1,4 +1,4 @@
all: run-test perf xctest test-leaks all: run-test perf xctest test-leaks test-static test-dynamic
OBJC_GEN_DIR=../../objcgen OBJC_GEN_DIR=../../objcgen
OBJC_GEN=$(OBJC_GEN_DIR)/bin/Debug/objcgen.exe OBJC_GEN=$(OBJC_GEN_DIR)/bin/Debug/objcgen.exe
@ -52,3 +52,13 @@ test-xctest-leaks: ../leaktest/bin/Debug/leaktest.exe $(MANAGED_DLL) libmanaged.
libLeakCheckAtExit.dylib: leak-at-exit.c libLeakCheckAtExit.dylib: leak-at-exit.c
clang -arch i386 -arch x86_64 -shared $< -o $@ 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=$*