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);
        }
Beispiel #2
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);
        }