Пример #1
0
        public override void Run()
        {
            this.Init();

            Dictionary <string, ClrApi> platformApis = this.LoadApis();
            ClrApi mergedApi = this.MergeApis(platformApis);

            platformApis.Add(PluginInfo.PortablePlatformName, mergedApi);
            Assembly mergedAssembly = mergedApi.Assemblies.Single();

            string xamarinPlatformId  = XamarinPlatformIds.Get(PluginInfo.PortablePlatformName);
            string targetRelativePath =
                Path.Combine("build", xamarinPlatformId, mergedAssembly.Name + ".dll");

            Log.Message("    " + targetRelativePath);
            Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(this.TargetOutputPath, targetRelativePath)));
            PortableAssemblyEmitter assemblyEmitter = new PortableAssemblyEmitter
            {
                Assembly         = mergedAssembly,
                AssemblyFilePath = Path.Combine(this.TargetOutputPath, targetRelativePath),
            };

            assemblyEmitter.Run();

            Dictionary <string, Assembly> platformAssemblies =
                platformApis.ToDictionary(pa => pa.Key, pa => pa.Value.Assemblies.Single());

            this.CopyAssemblies(platformAssemblies);
            this.GenerateNuspec(mergedAssembly, platformAssemblies);
            this.PackNugetPackage();
        }
Пример #2
0
        void ExportApi(ICollection <string> referencePaths)
        {
            string[] includeNamespaces = this.PluginInfo.AndroidPlatform.NamespaceMappings.Select(m => m.Namespace)
                                         .Concat(new[] { typeof(AndroidApiCompiler).Namespace }).ToArray();
            Assembly apiAssembly = ClrApi.LoadFrom(
                this.BuiltBindingsAssemblyPath,
                referencePaths,
                includeNamespaces);

            ClrApi androidApi = new ClrApi
            {
                Platform   = PluginInfo.AndroidPlatformName,
                Assemblies = new List <Assembly>(new[] { apiAssembly }),
            };

            string apiXmlPath = Path.GetFullPath(Path.Combine(
                                                     this.PlatformIntermediatePath, this.PlatformName + "-api.xml"));

            using (StreamWriter writer = File.CreateText(apiXmlPath))
            {
                androidApi.ToXml(writer);
            }

            Log.Important($"Exported {this.PlatformName} API metadata to {apiXmlPath}");
        }
Пример #3
0
        protected Dictionary <string, ClrApi> LoadApis()
        {
            Dictionary <string, ClrApi> platformApis = new Dictionary <string, ClrApi>();

            foreach (string platform in PluginLinker.SupportedPlatforms.Where(p => !platformApis.ContainsKey(p)))
            {
                string platformApiFile = this.FindIntermediateFile(Path.Combine(platform, platform + "-api.xml"));
                if (platformApiFile != null)
                {
                    using (StreamReader reader = File.OpenText(platformApiFile))
                    {
                        ClrApi platformApi = ClrApi.FromXml(reader);
                        platformApis.Add(platform, platformApi);

                        if (platform == PluginInfo.WindowsPlatformName)
                        {
                            this.windowsLanguage = platformApi.Language;
                        }
                    }
                }
            }

            if (platformApis.Count == 0)
            {
                throw new InvalidOperationException("No plugin APIs were found. Run the compile step first.");
            }

            return(platformApis);
        }
Пример #4
0
 void RemovePlatformIfNotAvailable(ClrApi api, string platform)
 {
     PluginInfo.PlatformInfo platformInfo = this.PluginInfo.Platforms.FirstOrDefault(p => p.Name == platform);
     if (!api.Platforms.Contains(platform) && platformInfo != null)
     {
         this.PluginInfo.Platforms.Remove(platformInfo);
     }
 }
Пример #5
0
        void GenerateWindowsAdapterProject()
        {
            Log.Important("Generating Windows C# bindings project at\n" + this.bindingsProject);

            string project = Utils.ExtractResourceText(ProjectFiles.Project);

            project = project.Replace($"$({MsbuildProperties.AssemblyName})", this.PluginInfo.Assembly.Name);
            project = project.Replace(
                $"$({MsbuildProperties.PluginAssemblyName})",
                Path.GetFileNameWithoutExtension(this.pluginMetadataFilePath));
            project = project.Replace(
                $"$({MsbuildProperties.PluginAssemblyPath})",
                this.pluginMetadataFilePath.Replace("x86", "$(Platform)"));

            File.WriteAllText(this.bindingsProject, project);

            Utils.ExtractResource(ProjectFiles.ProjectJson, this.PlatformIntermediatePath);

            string propertiesPath = Path.Combine(this.PlatformIntermediatePath, "Properties");

            if (!Directory.Exists(propertiesPath))
            {
                Directory.CreateDirectory(propertiesPath);
            }

            this.GenerateAssemblyInfo(Path.Combine(propertiesPath, ProjectFiles.AssemblyInfo));

            string adaptersPath = Path.Combine(this.PlatformIntermediatePath, "Adapters");

            if (!Directory.Exists(adaptersPath))
            {
                Directory.CreateDirectory(adaptersPath);
            }

            string refPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86) +
                             @"\Reference Assemblies\Microsoft\Framework\";

            string[] referenceAssemblyPaths = new string[]
            {
                refPath + @".NETFramework\v4.5\System.dll",
                refPath + @".NETFramework\v4.5\Facades\System.Runtime.dll",
                refPath + @".NETFramework\v4.6.1\Facades\System.Diagnostics.Debug.dll",
                refPath + @".NETFramework\v4.6.1\Facades\System.Runtime.dll",
                refPath + @".NETFramework\v4.6.1\Facades\System.Threading.Tasks.dll",
                refPath + @".NETCore\v4.5\System.Runtime.WindowsRuntime.dll",
                refPath + @".NETCore\v4.5\System.Runtime.WindowsRuntime.UI.Xaml.dll",
                refPath + @".NETCore\v4.5.1\System.Runtime.InteropServices.WindowsRuntime.dll",
            };

            Assembly          androidApi = ClrApi.LoadFrom(this.pluginMetadataFilePath, referenceAssemblyPaths);
            WindowsApiAdapter adapter    = new WindowsApiAdapter();

            adapter.PluginInfo          = this.PluginInfo;
            adapter.OutputDirectoryPath = adaptersPath;
            adapter.GenerateAdapterCodeForApi(androidApi);
        }
Пример #6
0
        public static ClrApi FromXml(TextReader textReader)
        {
            XmlReader reader = XmlReader.Create(
                textReader,
                new XmlReaderSettings {
                ConformanceLevel = ConformanceLevel.Document
            });
            ClrApi clrApi = (ClrApi) new XmlSerializer(typeof(ClrApi)).Deserialize(reader);

            return(clrApi);
        }
Пример #7
0
        void GenerateIOSCSharpBindingsProject(bool includeAdapters, ICollection <string> referencePaths = null)
        {
            Log.Important("Generating iOS C# bindings project at\n" + this.bindingsProjectFilePath);

            string project          = Utils.ExtractResourceText(Resources.Project);
            string assemblyName     = this.PluginInfo.Assembly.Name;
            string tempAssemblyName = "temp";

            project = project.Replace(
                $"$({MsbuildProperties.AssemblyName})", (includeAdapters ? assemblyName : tempAssemblyName));
            project = project.Replace($"$({MsbuildProperties.NativeLibFileName})", this.nativeLibFileName);

            File.WriteAllText(this.bindingsProjectFilePath, project);

            string propertiesPath = Path.Combine(this.PlatformIntermediatePath, "Properties");

            if (!Directory.Exists(propertiesPath))
            {
                Directory.CreateDirectory(propertiesPath);
            }

            this.GenerateAssemblyInfo(
                Path.Combine(propertiesPath, ProjectFiles.AssemblyInfo),
                new string[]
            {
                "[assembly: ObjCRuntime.LinkWith(" +
                $"\"{this.nativeLibFileName}\", SmartLink = true, ForceLoad = true)]",
            });

            string adaptersPath = Path.Combine(this.PlatformIntermediatePath, "Adapters");

            if (!Directory.Exists(adaptersPath))
            {
                Directory.CreateDirectory(adaptersPath);
            }

            if (includeAdapters)
            {
                string tempPath = Path.Combine(
                    Path.GetDirectoryName(this.BuiltBindingsAssemblyPath), tempAssemblyName + ".dll");
                Assembly      iosApi  = ClrApi.LoadFrom(tempPath, referencePaths);
                IOSApiAdapter adapter = new IOSApiAdapter();
                adapter.PluginInfo          = this.PluginInfo;
                adapter.OutputDirectoryPath = adaptersPath;
                adapter.GenerateAdapterCodeForApi(iosApi);
            }
        }
Пример #8
0
 protected void CreateTypeScriptBindings(
     ClrApi pluginApi, bool forceAsync, bool es6, string bridgeModuleName, string scriptOutputPath)
 {
     foreach (Assembly apiAssembly in pluginApi.Assemblies)
     {
         TypeScriptEmitter tsEmitter = new TypeScriptEmitter
         {
             Assembly         = apiAssembly,
             OutputPath       = scriptOutputPath,
             ForceAsyncAPIs   = forceAsync,
             ES6              = es6,
             BridgeModuleName = bridgeModuleName,
             PluginInfo       = this.PluginInfo,
         };
         tsEmitter.Run();
     }
 }
Пример #9
0
        public override void Run()
        {
            this.Init();
            ClrApi pluginApi = this.MergeApis(this.LoadApis());

            PluginLinker.CopyNativeProjects(this.SourcePath, null, this.TargetOutputPath);

            string scriptOutputPath = Path.Combine(this.TargetOutputPath, "js");

            if (!Directory.Exists(scriptOutputPath))
            {
                Directory.CreateDirectory(scriptOutputPath);
            }

            this.CreateTypeScriptBindings(
                pluginApi, true, true, c3pReactNativePackageName, scriptOutputPath);

            string modulesDirectoryPath = Path.Combine(scriptOutputPath, "node_modules");

            Directory.CreateDirectory(modulesDirectoryPath);
            this.CreateBridgeModuleTypeScriptDefinition(modulesDirectoryPath);
            Utils.ExtractResource("NativeObject.ts", modulesDirectoryPath);
            Utils.ExtractResource("NativeBridge.ts", modulesDirectoryPath);
            Utils.ExtractResource("react-native.d.ts", modulesDirectoryPath);
            Utils.ExtractResource("tsconfig.json", scriptOutputPath);

            this.GeneratePluginModule(pluginApi, scriptOutputPath);

            this.RemoveES6PromiseImports(scriptOutputPath);

            Utils.CompileTypeScript(scriptOutputPath);

            Log.Message(Utils.EnsureTrailingSlash(Path.GetFullPath(this.TargetOutputPath)));
            this.GeneratePackageJson(c3pReactNativePackageName, new[] { "    \"main\": \"js/plugin.js\"" });
            Utils.PackNpmPackage(this.TargetOutputPath);
        }
Пример #10
0
        void ExportApi()
        {
            string refPath = @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework";

            string[] referenceAssemblyPaths = new string[]
            {
                refPath + @"\v4.5\System.dll",
                refPath + @"\v4.5\Facades\System.Runtime.dll",
                refPath + @"\v4.6.1\Facades\System.Diagnostics.Debug.dll",
                refPath + @"\v4.6.1\Facades\System.Runtime.dll",
                refPath + @"\v4.6.1\Facades\System.Threading.Tasks.dll",
                this.pluginMetadataFilePath,
            };
            string[] includeNamespaces = this.PluginInfo.WindowsPlatform.IncludeNamespaces?.Select(m => m.Namespace)
                                         .Concat(new[] { typeof(WindowsApiCompiler).Namespace }).ToArray();

            Assembly apiAssembly = ClrApi.LoadFrom(
                this.BuiltBindingsAssemblyPath, referenceAssemblyPaths, includeNamespaces);

            ClrApi androidApi = new ClrApi
            {
                Platform   = PluginInfo.WindowsPlatformName,
                Assemblies = new List <Assembly>(new[] { apiAssembly }),
                Language   = this.Language,
            };

            string apiXmlPath = Path.GetFullPath(Path.Combine(
                                                     this.PlatformIntermediatePath, this.PlatformName + "-api.xml"));

            using (StreamWriter writer = File.CreateText(apiXmlPath))
            {
                androidApi.ToXml(writer);
            }

            Log.Important($"Exported {this.PlatformName} API metadata to {apiXmlPath}");
        }
Пример #11
0
        public override void Run()
        {
            this.Init();
            ClrApi pluginApi = this.MergeApis(this.LoadApis());

            PluginLinker.CopyNativeCode(this.SourcePath, null, this.TargetOutputPath);

            if (this.PluginInfo.WindowsPlatform != null)
            {
                string windowsSourcePath = Path.Combine(
                    this.SourcePath,
                    "windows" + (this.windowsLanguage != null ? "-" + this.windowsLanguage : null));
                if (Directory.Exists(windowsSourcePath))
                {
                    string windowsOutputPath = Path.Combine(this.TargetOutputPath, "windows");
                    if (!Directory.Exists(windowsOutputPath))
                    {
                        Directory.CreateDirectory(windowsOutputPath);
                    }

                    PluginLinker.CopyProjectDirectory(windowsSourcePath, windowsOutputPath);
                }
            }

            CordovaPluginLinker.CollectSourceFilesInfo(this.TargetOutputPath, this.PluginInfo);
            this.CreateAndroidConfigFileInfo();
            this.CreateIOSConfigFileInfo();
            this.CreateWindowsConfigFileInfo();

            this.AddProjectReferences();

            string scriptOutputPath = Path.Combine(this.TargetOutputPath, "www");

            if (Directory.Exists(scriptOutputPath))
            {
                Directory.Delete(scriptOutputPath, true);
            }

            Directory.CreateDirectory(scriptOutputPath);
            this.CreateTypeScriptBindings(
                pluginApi, true, false, c3pPluginId + "." + c3pCordovaModuleName, scriptOutputPath);

            string modulesDirectoryPath = Path.Combine(scriptOutputPath, "node_modules");

            Directory.CreateDirectory(modulesDirectoryPath);

            // This is messy, because Cordova's implementation of module resolution
            // doesn't respect the 'main' property from the package.json.
            this.CreateBridgeModuleTypeScriptDefinition(Path.Combine(
                                                            modulesDirectoryPath, c3pPluginId + "." + c3pCordovaModuleName + ".d.ts"));
            Utils.ExtractResource(
                "es6-promise.d.ts",
                modulesDirectoryPath,
                c3pPluginId + "." + "es6-promise.d.ts");

            foreach (string tsFile in new[] { "NativeObject.ts", "NativeBridge.ts" })
            {
                Utils.ExtractResource(tsFile, modulesDirectoryPath, c3pPluginId + "." + tsFile);
                string fixTsCode = File.ReadAllText(Path.Combine(
                                                        modulesDirectoryPath, c3pPluginId + "." + tsFile));
                fixTsCode = fixTsCode.Replace("\"es6-promise\"", "\"" + c3pPluginId + ".es6-promise\"")
                            .Replace("\"./NativeObject\"", "\"" + c3pPluginId + ".NativeObject\"");
                File.WriteAllText(
                    Path.Combine(modulesDirectoryPath, c3pPluginId + "." + tsFile), fixTsCode);
            }

            Utils.ExtractResource("tsconfig.json", scriptOutputPath);
            Utils.CompileTypeScript(scriptOutputPath);

            Log.Message(Utils.EnsureTrailingSlash(Path.GetFullPath(this.TargetOutputPath)));
            this.GeneratePluginXml(scriptOutputPath);
            this.GeneratePackageJson(c3pCordovaPackageName);
            Utils.PackNpmPackage(this.TargetOutputPath);
        }
Пример #12
0
        public void Run()
        {
            if (this.PluginInfo == null)
            {
                throw new ArgumentNullException(nameof(PluginInfo));
            }

            if (this.PlatformApis == null || this.PlatformApis.Count == 0 ||
                this.PlatformApis.Values.Any(a => a == null || a.Assemblies == null || a.Assemblies.Count == 0))
            {
                throw new ArgumentNullException(nameof(PlatformApis));
            }

            if (this.PlatformApis.Values.Any(a => a.Assemblies.Count > 1))
            {
                throw new NotSupportedException("Merging does not support multiple assemblies.");
            }

            this.platformAssemblies =
                this.PlatformApis.ToDictionary(pa => pa.Key, pa => pa.Value.Assemblies.Single());

            this.platformTypesIndex   = IndexTypes(this.platformAssemblies);
            this.platformMembersIndex = IndexMembers(this.platformTypesIndex);

            this.ValidateAssemblies();

            if (this.validationWarnings.Count > 0)
            {
                Log.Warning("");
                foreach (string message in this.validationWarnings)
                {
                    Log.Warning("{0}", message);
                }
            }

            if (this.validationErrors.Count > 0)
            {
                Log.Error("");
                foreach (string message in this.validationErrors)
                {
                    Log.Error("{0}", message);
                }

                Log.Error("");
                throw new InvalidOperationException("Cannot merge APIs due to validation errors.");
            }

            Assembly mergedAssembly = this.MergeAssemblies(this.platformAssemblies.Values);

            this.MergedApi = new ClrApi
            {
                Platform   = String.Join(",", this.PlatformApis.Keys),
                Assemblies = new List <Assembly>(new[] { mergedAssembly }),
            };

            // Event classes are automatically marshalled by value.
            foreach (Class eventClass in mergedAssembly.Types.OfType <Class>().Where(
                         c => c.ExtendsType == "System.EventArgs"))
            {
                PluginInfo.AssemblyClassInfo classInfo = this.PluginInfo.Assembly.Classes
                                                         .FirstOrDefault(c => c.Name == eventClass.Name || c.Name == eventClass.FullName);
                if (classInfo != null)
                {
                    classInfo.MarshalByValue = "true";
                }
                else
                {
                    this.PluginInfo.Assembly.Classes.Add(new PluginInfo.AssemblyClassInfo
                    {
                        Name           = eventClass.Name,
                        MarshalByValue = "true",
                    });
                }
            }
        }
Пример #13
0
        void GeneratePluginModule(ClrApi pluginApi, string scriptOutputPath)
        {
            using (CodeWriter code = new CodeWriter(Path.Combine(scriptOutputPath, "plugin.ts")))
            {
                code.Code("import { Platform, NativeModules } from \"react-native\";");
                code.Code();

                HashSet <string> allNamespaces = new HashSet <string>();

                if (this.PluginInfo.AndroidPlatform != null)
                {
                    code.Code("if (Platform.OS === \"android\") {");

                    foreach (var namespaceMapping in this.PluginInfo.AndroidPlatform.NamespaceMappings)
                    {
                        code.Code("\tNativeModules.C3P.registerNamespaceMapping(" +
                                  $"\"{namespaceMapping.Namespace}\", \"{namespaceMapping.Package}\");");
                        allNamespaces.Add(namespaceMapping.Namespace);
                    }

                    code.Code("}");
                }

                if (this.PluginInfo.IOSPlatform != null)
                {
                    code.Code("if (Platform.OS === \"ios\") {");

                    foreach (var namespaceMapping in this.PluginInfo.IOSPlatform.NamespaceMappings)
                    {
                        code.Code("\tNativeModules.C3P.registerNamespaceMapping(" +
                                  $"\"{namespaceMapping.Namespace}\", \"{namespaceMapping.Prefix}\");");
                        allNamespaces.Add(namespaceMapping.Namespace);
                    }

                    code.Code("}");
                }

                if (this.PluginInfo.WindowsPlatform != null)
                {
                    code.Code("if (Platform.OS === \"windows\") {");

                    foreach (var namespaceInfo in this.PluginInfo.WindowsPlatform.IncludeNamespaces)
                    {
                        code.Code("\tNativeModules.C3P.registerNamespaceMapping(" +
                                  $"\"{namespaceInfo.Namespace}\");");
                        allNamespaces.Add(namespaceInfo.Namespace);
                    }

                    code.Code("}");
                }

                code.Code();

                foreach (var classInfo in this.PluginInfo.Assembly.Classes.Where(c => c.MarshalByValue == "true"))
                {
                    code.Code($"NativeModules.C3P.registerMarshalByValueClass(\"{classInfo.Name}\");");
                }

                string[] includeNamespaces = allNamespaces.ToArray();

                code.Code();
                foreach (Type pluginType in pluginApi.Assemblies.Single().Types
                         .Where(t => includeNamespaces.Contains(t.Namespace)))
                {
                    code.Code($"import {pluginType.Name} = require(\"./{pluginType.Name}\");");
                }

                code.Code();
                code.Code("export = {");

                foreach (Type pluginType in pluginApi.Assemblies.Single().Types
                         .Where(t => includeNamespaces.Contains(t.Namespace)))
                {
                    code.Code($"\t{pluginType.Name}: {pluginType.Name},");
                }

                code.Code("}");
            }
        }
Пример #14
0
        void GenerateAndroidCSharpBindingsProject(bool includeAdapters, ICollection <string> referencePaths = null)
        {
            Log.Important("Generating Android C# bindings project at\n" + this.bindingsProject);

            string project          = Utils.ExtractResourceText(ProjectFiles.Project);
            string assemblyName     = this.PluginInfo.Assembly.Name;
            string tempAssemblyName = "temp";

            project = project.Replace($"$({MsbuildProperties.JavadocPath})", Path.GetFullPath(this.javadocPath));
            project = project.Replace(
                $"$({MsbuildProperties.AssemblyName})", (includeAdapters ? assemblyName : tempAssemblyName));
            project = project.Replace(
                "<ItemGroup Label=\"Jars\">", "<ItemGroup Label=\"Jars\">\n" + this.GetMsbuildJarItems());
            project = project.Replace(
                "<ItemGroup Label=\"Libs\">", "<ItemGroup Label=\"Libs\">\n" + this.GetMsbuildLibItems());

            File.WriteAllText(this.bindingsProject, project);

            string propertiesPath = Path.Combine(this.PlatformIntermediatePath, "Properties");

            if (!Directory.Exists(propertiesPath))
            {
                Directory.CreateDirectory(propertiesPath);
            }

            this.GenerateAssemblyInfo(Path.Combine(propertiesPath, ProjectFiles.AssemblyInfo));

            string adaptersPath = Path.Combine(this.PlatformIntermediatePath, "Adapters");

            if (!Directory.Exists(adaptersPath))
            {
                Directory.CreateDirectory(adaptersPath);
            }

            string transformsPath = Path.Combine(this.PlatformIntermediatePath, "Transforms");

            if (!Directory.Exists(transformsPath))
            {
                Directory.CreateDirectory(transformsPath);
            }

            if (includeAdapters)
            {
                string apiXmlPath = Path.Combine(
                    this.PlatformIntermediatePath, "obj", (this.DebugConfiguration ? "Debug" : "Release"), "api.xml");
                if (!File.Exists(apiXmlPath))
                {
                    throw new FileNotFoundException("Exported Java API XML file not found at " + apiXmlPath);
                }

                JavaApi javaApi;
                using (StreamReader reader = File.OpenText(apiXmlPath))
                {
                    javaApi = JavaApi.FromXml(reader);
                }

                string tempPath = Path.Combine(
                    Path.GetDirectoryName(this.BuiltBindingsAssemblyPath), tempAssemblyName + ".dll");
                Assembly androidApi = ClrApi.LoadFrom(
                    tempPath,
                    referencePaths);
                AndroidApiAdapter adapter = new AndroidApiAdapter();
                adapter.PluginInfo          = this.PluginInfo;
                adapter.JavaApi             = javaApi;
                adapter.OutputDirectoryPath = adaptersPath;
                adapter.GenerateAdapterCodeForApi(androidApi);
            }

            this.GenerateBindingMetadata(transformsPath);

            string enumFields = "<enum-field-mappings></enum-field-mappings>";

            File.WriteAllText(Path.Combine(transformsPath, ProjectFiles.EnumFields), enumFields);

            string enumMethods = "<enum-method-mappings></enum-method-mappings>";

            File.WriteAllText(Path.Combine(transformsPath, ProjectFiles.EnumMethods), enumMethods);
        }