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: 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: 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(); } } 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: output_file = $"lib{LibraryName}.dylib"; break; case CompilationTarget.StaticLibrary: case CompilationTarget.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); } int exitCode; /* * For static libraries we * * Compile all source files to .o files, per architecture. * * Then we archive .o files into per-sdk .a files, then we archive all .o files into a global .a file. So we end up with one .a for device, one for simulator, and one for both device and simulator. * * Then we dsym the global .a file * * For dynamic libraries we * * Compile all source files to .o files, per architecture. * * Then we link all .o files per architecture into a .dylib. * * Then we lipo .dylib files into per-sdk fat .dylib, then we lipo all .dylib into a global .dylib file. So we end up with one fat .dylib for device, one fat .dylib for simulator, and one very fat .dylib for both simulator and device. * * Finally we dsymutil the global .dylib * * For frameworks we * * First we build the source files to .o files and then archive to .a files just like for static libraries. * * Then we call mtouch to build a framework of the managed assemblies, passing the static library we just created as a gcc_flag so that it's linked in. This is done per sdk (simulator/device). * * Finally we merge the simulator framework and the device framework into a global framework, that supports both simulator and device. * * We dsymutil those frameworks. */ 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} "); switch (Platform) { case Platform.macOS: // FIXME: Pending XM support to switch include directory. //common_options.Append ("-I/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/include "); //common_options.Append ("-DXAMARIN_MAC "); common_options.Append("-I/Library/Frameworks/Mono.framework/Versions/Current/include/mono-2.0 "); break; case Platform.iOS: case Platform.tvOS: case Platform.watchOS: common_options.Append($"-I/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/SDKs/{build_info.XamariniOSSDK}/usr/include "); common_options.Append("-DXAMARIN_IOS "); 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); } // Build each source 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); } } // Link/archive object files to .a/.dylib 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 "); options.Append("-lxamarin "); } 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.Framework: 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; 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); } } // Create the per-sdk output file var sdk_output_file = Path.Combine(OutputDirectory, build_info.Sdk, output_file); if (!Lipo(lipo_files, sdk_output_file, out exitCode)) { return(exitCode); } // Call mtouch if we're building a framework if (CompilationTarget == CompilationTarget.Framework) { switch (Platform) { case Platform.macOS: throw new NotImplementedException("frameworks for macOS"); case Platform.iOS: case Platform.tvOS: case Platform.watchOS: var mtouch = new StringBuilder(); var appdir = Path.GetFullPath(Path.Combine(OutputDirectory, build_info.Sdk, "appdir")); var cachedir = Path.GetFullPath(Path.Combine(outputDirectory, build_info.Sdk, "mtouch-cache")); mtouch.Append(build_info.IsSimulator ? "--sim " : "--dev "); mtouch.Append($"{Quote (appdir)} "); mtouch.Append($"--abi={string.Join (",", build_info.Architectures)} "); mtouch.Append($"--sdkroot {XcodeApp} "); mtouch.Append($"--targetver {build_info.MinVersion} "); mtouch.Append("--dsym:false "); mtouch.Append($"--embeddinator "); foreach (var asm in Assemblies) { mtouch.Append(Quote(Path.GetFullPath(asm.Location))).Append(" "); } mtouch.Append($"-r:{GetPlatformAssembly ()} "); mtouch.Append($"--sdk {GetSdkVersion (build_info.Sdk.ToLower ())} "); mtouch.Append("--linksdkonly "); mtouch.Append("--registrar:static "); mtouch.Append($"--cache {Quote (cachedir)} "); if (Debug) { mtouch.Append("--debug "); } mtouch.Append($"--assembly-build-target=@all=framework={LibraryName}.framework "); mtouch.Append($"--target-framework {GetTargetFramework ()} "); mtouch.Append($"\"--gcc_flags=-force_load {Path.GetFullPath (sdk_output_file)}\" "); if (!RunProcess("/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/bin/mtouch", mtouch.ToString(), out exitCode)) { return(exitCode); } // Add the headers to the framework var fwdir = Path.Combine(appdir, "Frameworks", $"{LibraryName}.framework"); var headers = Path.Combine(fwdir, "Headers"); Directory.CreateDirectory(headers); File.Copy(Path.Combine(OutputDirectory, "embeddinator.h"), Path.Combine(headers, "embeddinator.h"), true); File.Copy(Path.Combine(OutputDirectory, "bindings.h"), Path.Combine(headers, $"bindings.h"), true); // Create an umbrella header for the everything in the framework. File.WriteAllText(Path.Combine(headers, LibraryName + ".h"), @" #include ""bindings.h"" #if defined(__i386__) #include ""registrar-i386.h"" #elif defined(__x86_64__) #include ""registrar-x86_64.h"" #elif defined(__arm__) #include ""registrar-arm32.h"" // this includes all 32-bit arm architectures. #elif defined(__aarch64__) #include ""registrar-arm64.h"" #else #error Unknown architecture #endif "); if (build_info.IsSimulator) { FileCopyIfExists(Path.Combine(cachedir, "32", "registrar.h"), Path.Combine(headers, "registrar-i386.h")); FileCopyIfExists(Path.Combine(cachedir, "64", "registrar.h"), Path.Combine(headers, "registrar-x86_64.h")); } else { FileCopyIfExists(Path.Combine(cachedir, "32", "registrar.h"), Path.Combine(headers, "registrar-arm32.h")); FileCopyIfExists(Path.Combine(cachedir, "64", "registrar.h"), Path.Combine(headers, "registrar-arm64.h")); } // Move the framework to the output directory var fwpath = Path.Combine(OutputDirectory, build_info.Sdk, $"{LibraryName}.framework"); if (Directory.Exists(fwpath)) { Directory.Delete(fwpath, true); } Directory.Move(fwdir, fwpath); Directory.Delete(appdir, true); // We don't need this anymore. 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); } } } 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); } if (build_infos.Length == 2 && CompilationTarget == CompilationTarget.Framework) { var dev_fwpath = Path.Combine(OutputDirectory, build_infos [0].Sdk, $"{LibraryName}.framework"); var sim_fwpath = Path.Combine(OutputDirectory, build_infos [1].Sdk, $"{LibraryName}.framework"); if (!MergeFrameworks(Path.Combine(OutputDirectory, LibraryName + ".framework"), dev_fwpath, sim_fwpath, out exitCode)) { return(exitCode); } } return(0); }
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: 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: 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: 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); } 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: output_file = $"lib{LibraryName}.dylib"; break; case CompilationTarget.StaticLibrary: 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); } 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); }