Esempio n. 1
0
        private static string GetAssemblyDirectory(CsAssembly csAssembly, string rootDirectory, string generatedCodeFolder, bool includeAssemblyNameFolder)
        {
            var assemblyRootDirectory = includeAssemblyNameFolder ? Path.Combine(rootDirectory, csAssembly.Name) : rootDirectory;

            var generatedDirectoryForAssembly = Path.Combine(assemblyRootDirectory, generatedCodeFolder ?? "Generated");
            return generatedDirectoryForAssembly;
        }
Esempio n. 2
0
        /// <summary>
        /// Process the specified config file.
        /// </summary>
        /// <param name="file">The file.</param>
        private void ProcessCppModuleWithConfig(CppModule cppModule, ConfigFile file)
        {
            Logger.PushLocation(file.AbsoluteFilePath);
            try
            {
                CsAssembly assembly = null;

                if (!string.IsNullOrEmpty(file.Assembly))
                {
                    assembly = assemblyManager.GetOrCreateAssembly(file.Assembly);
                }

                if (assembly != null)
                {
                    Logger.Message("Process rules for assembly [{0}] and namespace [{1}]", file.Assembly, file.Namespace);
                }

                var elementFinder = new CppElementFinder(cppModule);

                // Only attach includes when there is a bind to an assembly
                if (assembly != null)
                {
                    AttachIncludes(file);

                    ProcessExtensions(elementFinder, file);
                }

                ProcessMappings(elementFinder, file);
            }
            finally
            {
                Logger.PopLocation();
            }
        }
Esempio n. 3
0
        public void Run(CsAssembly csAssembly, string generatedCodeFolder)
        {
            if (string.IsNullOrEmpty(generatedCodeFolder))
            {
                throw new ArgumentException("Value cannot be null or empty.", nameof(generatedCodeFolder));
            }

            var directoryToCreate = new HashSet <string>(StringComparer.CurrentCulture);

            // Remove the generated directory before creating it
            if (!directoryToCreate.Contains(generatedCodeFolder))
            {
                directoryToCreate.Add(generatedCodeFolder);
                if (Directory.Exists(generatedCodeFolder))
                {
                    foreach (var oldGeneratedFile in Directory.EnumerateFiles(generatedCodeFolder, "*.cs", SearchOption.AllDirectories))
                    {
                        try
                        {
                            File.Delete(oldGeneratedFile);
                        }
                        catch
                        {
                            // ignored
                        }
                    }
                }
            }

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

            logger.Message("Process Assembly => {0}", generatedCodeFolder);

            var trees = new[]
            {
                CreateTree("Enumerations", ns => ns.Enums, generators.Enum),
                CreateTree("Structures", ns => ns.Structs, generators.Struct),
                CreateTree("Functions", ns => ns.Classes, generators.Group),
                CreateTree("Interfaces", ns => ns.Interfaces, generators.Interface)
            };

            SyntaxTree CreateTree <T>(string fileName, Func <CsNamespace, IEnumerable <T> > membersFunc,
                                      IMultiCodeGenerator <T, MemberDeclarationSyntax> generator) where T : CsBase =>
            CSharpSyntaxTree.Create(
                GenerateCompilationUnit(
                    csAssembly.Namespaces.Select(
                        ns => GenerateNamespaceDeclaration(ns, membersFunc(ns), generator)
                        )
                    ),
                path : Path.Combine(generatedCodeFolder, $"{fileName}.cs")
                );

            foreach (var tree in trees)
            {
                File.WriteAllText(tree.FilePath, tree.GetCompilationUnitRoot().ToFullString());
            }
        }
Esempio n. 4
0
    public void Run(CsAssembly csAssembly, string generatedCodeFolder, Ioc ioc)
    {
        if (string.IsNullOrEmpty(generatedCodeFolder))
        {
            throw new ArgumentException("Value cannot be null or empty.", nameof(generatedCodeFolder));
        }

        var logger     = ioc.Logger;
        var generators = ioc.Generators;

        var directoryToCreate = new HashSet <string>(StringComparer.CurrentCulture);

        // Remove the generated directory before creating it
        if (!directoryToCreate.Contains(generatedCodeFolder))
        {
            directoryToCreate.Add(generatedCodeFolder);
            if (Directory.Exists(generatedCodeFolder))
            {
                foreach (var oldGeneratedFile in Directory.EnumerateFiles(generatedCodeFolder, "*.cs", SearchOption.AllDirectories))
                {
                    try
                    {
                        File.Delete(oldGeneratedFile);
                    }
                    catch
                    {
                        // ignored
                    }
                }
            }
        }

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

        logger.Message("Process Assembly => {0}", generatedCodeFolder);

        List <SyntaxTree> trees = new()
        {
            CreateTree("Enumerations", ns => ns.Enums, generators.Enum),
            CreateTree("Structures", ns => ns.Structs, generators.Struct),
            CreateTree("Functions", ns => ns.Classes, generators.Group),
            CreateTree("Interfaces", ns => ns.Interfaces, generators.Interface)
        };

        var resultConstants = csAssembly.Namespaces
                              .SelectMany(x => x.EnumerateDescendants <CsResultConstant>(withAdditionalItems: false))
                              .ToArray();

        if (resultConstants.Length > 0)
        {
            trees.Add(
                CSharpSyntaxTree.Create(
                    CompilationUnit(
Esempio n. 5
0
        /// <summary>
        /// Gets a C# assembly by its name.
        /// </summary>
        /// <param name="assemblyName">Name of the assembly.</param>
        /// <returns>A C# assembly</returns>
        public CsAssembly GetOrCreateAssembly(string assemblyName)
        {
            var selectedAssembly = Assemblies.FirstOrDefault(assembly => assembly.Name == assemblyName);

            if (selectedAssembly == null)
            {
                selectedAssembly = new CsAssembly(assemblyName);
                assemblies.Add(selectedAssembly);
            }

            return(selectedAssembly);
        }
Esempio n. 6
0
        private void GenerateCode(IDocumentationLinker docAggregator, CsAssembly asm, ExternalDocCommentsReader docCommentsReader)
        {
            var generator = new RoslynGenerator(Logger, GlobalNamespace, docAggregator, docCommentsReader, new GeneratorConfig {
                Platforms = PlatformDetectionType.Any
            });

            generator.Run(asm, _generatedPath, GeneratedCodeFolder);

            // Update check files for all assemblies
            var processTime = DateTime.Now;

            File.WriteAllText(Path.Combine(IntermediateOutputPath, asm.CheckFileName), "");
            File.SetLastWriteTime(Path.Combine(IntermediateOutputPath, asm.CheckFileName), processTime);
        }
        public override bool Execute()
        {
            var asm = CsAssembly.Read(Model.ItemSpec);

            var files = RoslynGenerator.GetFilePathsForGeneratedFiles(asm, OutputDirectory, GeneratedCodeFolder);

            GeneratedFiles = files
                             .Select(file =>
            {
                var item = new TaskItem(file);
                return(item);
            }).ToArray();
            return(true);
        }
Esempio n. 8
0
        public static IList <string> GetFilePathsForGeneratedFiles(CsAssembly assembly, string rootDirectory, string generatedCodeFolder)
        {
            var directory = GetAssemblyDirectory(rootDirectory, generatedCodeFolder);
            var results   = new List <string> {
                Path.Combine(directory, "LocalInterop.cs")
            };

            foreach (var nameSpace in assembly.Namespaces)
            {
                var namespaceDirectory = GetNamespaceDirectory(directory, nameSpace);
                results.Add(Path.Combine(namespaceDirectory, "Enumerations.cs"));
                results.Add(Path.Combine(namespaceDirectory, "Structures.cs"));
                results.Add(Path.Combine(namespaceDirectory, "Interfaces.cs"));
                results.Add(Path.Combine(namespaceDirectory, "Functions.cs"));
            }

            return(results);
        }
Esempio n. 9
0
        /// <summary>
        ///   Maps all C++ types to C#
        /// </summary>
        /// <param name="cppModule">The C++ module to parse.</param>
        /// <param name="configFile">The config file to use to transform the C++ module into C# assemblies.</param>
        /// <param name="checkFilesPath">The path for the check files.</param>
        public (CsAssembly assembly, IEnumerable <DefineExtensionRule> consumerExtensions) Transform(CppModule cppModule, ConfigFile configFile, string checkFilesPath)
        {
            Init(configFile.ConfigFilesLoaded);

            var checkFileNodes = CheckIfUpdated(configFile.ConfigFilesLoaded, checkFilesPath);

            var moduleToTransform = MapModule(cppModule, configFile.ConfigFilesLoaded.Where(cfg => cfg.ProcessMappings));


            var selectedCSharpType = new List <CsBase>();

            // Prepare transform by defining/registering all types to process
            selectedCSharpType.AddRange(PrepareTransform(moduleToTransform, EnumTransform));
            selectedCSharpType.AddRange(PrepareTransform(moduleToTransform, StructTransform));
            selectedCSharpType.AddRange(PrepareTransform(moduleToTransform, InterfaceTransform));
            selectedCSharpType.AddRange(PrepareTransform <CppFunction, CsFunction>(moduleToTransform, FunctionTransform));

            // Transform all types
            Logger.Progress(65, "Transforming enums...");
            ProcessTransform(EnumTransform, selectedCSharpType.OfType <CsEnum>());
            Logger.Progress(70, "Transforming structs...");
            ProcessTransform(StructTransform, selectedCSharpType.OfType <CsStruct>());
            Logger.Progress(75, "Transforming interfaces...");
            ProcessTransform(InterfaceTransform, selectedCSharpType.OfType <CsInterface>());
            Logger.Progress(80, "Transforming functions...");
            ProcessTransform(FunctionTransform, selectedCSharpType.OfType <CsFunction>());

            var asm = new CsAssembly
            {
                Interop = interopManager
            };

            foreach (var ns in namespaceRegistry.Namespaces)
            {
                foreach (var group in ns.Classes)
                {
                    constantManager.AttachConstants(group);
                }
                asm.Add(ns);
            }

            return(asm, configFile.ConfigFilesLoaded.SelectMany(file => file.Extension.OfType <DefineExtensionRule>()));
        }
        public void Run(CsAssembly csAssembly, string generatedCodeFolder)
        {
            if (string.IsNullOrEmpty(generatedCodeFolder))
            {
                throw new ArgumentException("Value cannot be null or empty.", nameof(generatedCodeFolder));
            }

            var directoryToCreate = new HashSet <string>(StringComparer.CurrentCulture);

            // Remove the generated directory before creating it
            if (!directoryToCreate.Contains(generatedCodeFolder))
            {
                directoryToCreate.Add(generatedCodeFolder);
                if (Directory.Exists(generatedCodeFolder))
                {
                    foreach (var oldGeneratedFile in Directory.EnumerateFiles(generatedCodeFolder, "*.cs", SearchOption.AllDirectories))
                    {
                        try
                        {
                            File.Delete(oldGeneratedFile);
                        }
                        catch
                        {
                            // ignored
                        }
                    }
                }
            }

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

            Logger.Message("Process Assembly {0} => {1}", csAssembly.Name, generatedCodeFolder);

            var trees = new[]
Esempio n. 11
0
    public static async Task ApplyDocumentation(IDocProvider?docProvider, DocItemCache cache, CsAssembly module,
                                                DocumentationContext context)
    {
        var documentationTasks = new List <Task>();

        Task DocumentSelector(CsBase cppElement) =>
        DocumentElement(docProvider, cache, cppElement, context, true, null);

        foreach (var cppInclude in module.Namespaces)
        {
            documentationTasks.AddRange(cppInclude.Enums.Select(DocumentSelector));
            documentationTasks.AddRange(cppInclude.Structs.Select(DocumentSelector));
            documentationTasks.AddRange(
                cppInclude.Interfaces
                .Select(cppInterface => DocumentInterface(docProvider, cache, cppInterface, context))
                );
            documentationTasks.AddRange(
                cppInclude.Classes
                .Select(cppFunction => DocumentGroup(docProvider, cache, cppFunction, context))
                );
        }

        await Task.WhenAll(documentationTasks);
    }
Esempio n. 12
0
        private void PrintStatistics(CsAssembly assembly)
        {
            var globalStats = new Dictionary <string, int>
            {
                ["interfaces"] = 0,
                ["methods"]    = 0,
                ["parameters"] = 0,
                ["enums"]      = 0,
                ["structs"]    = 0,
                ["fields"]     = 0,
                ["enumitems"]  = 0,
                ["functions"]  = 0
            };

            var stats = globalStats.ToDictionary(globalStat => globalStat.Key, globalStat => 0);

            foreach (var nameSpace in assembly.Items)
            {
                // Enums, Structs, Interface, FunctionGroup
                foreach (var item in nameSpace.Items)
                {
                    if (item is CsInterface)
                    {
                        stats["interfaces"]++;
                    }
                    else if (item is CsStruct)
                    {
                        stats["structs"]++;
                    }
                    else if (item is CsEnum)
                    {
                        stats["enums"]++;
                    }

                    foreach (var subitem in item.Items)
                    {
                        if (subitem is CsFunction)
                        {
                            stats["functions"]++;
                            stats["parameters"] += subitem.Items.Count;
                        }
                        else if (subitem is CsMethod)
                        {
                            stats["methods"]++;
                            stats["parameters"] += subitem.Items.Count;
                        }
                        else if (subitem is CsEnumItem)
                        {
                            stats["enumitems"]++;
                        }
                        else if (subitem is CsField)
                        {
                            stats["fields"]++;
                        }
                    }
                }

                foreach (var stat in stats)
                {
                    globalStats[stat.Key] += stat.Value;
                }
            }

            Logger.Message("Assembly [{0}] Statistics", assembly.QualifiedName);
            foreach (var stat in stats)
            {
                Logger.Message("\tNumber of {0} : {1}", stat.Key, stat.Value);
            }
            Logger.Message("\n");

            Logger.Message("Global Statistics:");
            foreach (var stat in globalStats)
            {
                Logger.Message("\tNumber of {0} : {1}", stat.Key, stat.Value);
            }
        }
Esempio n. 13
0
        /// <summary>
        /// Run CodeGenerator
        /// </summary>
        public void Run()
        {
            Logger.Progress(0, "Starting code generation...");

            try
            {
                var consumerConfig = new ConfigFile
                {
                    Id = ConsumerBindMappingConfigId
                };

                var(filesWithIncludes, filesWithExtensions) = Config.GetFilesWithIncludesAndExtensionHeaders();

                var configsWithIncludes = new HashSet <ConfigFile>();

                foreach (var config in Config.ConfigFilesLoaded)
                {
                    if (filesWithIncludes.Contains(config.Id))
                    {
                        configsWithIncludes.Add(config);
                    }
                }

                var sdkResolver = new SdkResolver(Logger);

                foreach (var config in Config.ConfigFilesLoaded)
                {
                    foreach (var sdk in config.Sdks)
                    {
                        config.IncludeDirs.AddRange(sdkResolver.ResolveIncludeDirsForSdk(sdk));
                    }
                }

                var cppHeadersUpdated = GenerateHeaders(filesWithExtensions, configsWithIncludes, consumerConfig);

                if (Logger.HasErrors)
                {
                    Logger.Fatal("Failed to generate C++ headers.");
                }

                CppModule group;
                var       groupFileName = $"{Config.Id}-out.xml";

                if (cppHeadersUpdated.Count != 0)
                {
                    var resolver = new IncludeDirectoryResolver(Logger);
                    resolver.Configure(Config);

                    var castXml = new CastXml(Logger, resolver, CastXmlExecutablePath, Array.Empty <string>())
                    {
                        OutputPath = IntermediateOutputPath,
                    };

                    group = GenerateExtensionHeaders(filesWithExtensions, cppHeadersUpdated, castXml);
                    group = ParseCpp(castXml, group);

                    if (IsGeneratingDoc)
                    {
                        ApplyDocumentation(DocumentationCache, group);
                    }
                }
                else
                {
                    Logger.Progress(10, "Config files unchanged. Read previous C++ parsing...");
                    if (File.Exists(Path.Combine(IntermediateOutputPath, groupFileName)))
                    {
                        group = CppModule.Read(Path.Combine(IntermediateOutputPath, groupFileName));
                    }
                    else
                    {
                        group = new CppModule();
                    }
                }

                // Save back the C++ parsed includes
                group.Write(Path.Combine(IntermediateOutputPath, groupFileName));

                Config.ExpandDynamicVariables(Logger, group);

                var(docAggregator, asm) = ExecuteMappings(group, consumerConfig);

                asm.Write(Path.Combine(IntermediateOutputPath, "Assembly.xml"));

                asm = CsAssembly.Read(Path.Combine(IntermediateOutputPath, "Assembly.xml"));

                GenerateConfigForConsumers(consumerConfig);

                GenerateCode(docAggregator, asm, new ExternalDocCommentsReader(ExternalDocumentation));

                if (Logger.HasErrors)
                {
                    Logger.Fatal("Code generation failed");
                }

                // Update Checkfile for assembly
                File.WriteAllText(_assemblyCheckFile, "");
                File.SetLastWriteTime(_assemblyCheckFile, _assemblyDatetime);

                // Update Checkfile for all config files
                File.WriteAllText(_allConfigCheck, "");
                File.SetLastWriteTime(_allConfigCheck, DateTime.Now);
            }
            finally
            {
                Logger.Progress(100, "Finished");
            }
        }
Esempio n. 14
0
        public override bool Execute()
        {
            var documentationFiles = new Dictionary <string, XmlDocument>();

            foreach (var file in ExternalDocumentation ?? Enumerable.Empty <ITaskItem>())
            {
                using (var stream = File.OpenRead(file.ItemSpec))
                {
                    var xml = new XmlDocument();
                    xml.Load(stream);
                    documentationFiles.Add(file.ItemSpec, xml);
                }
            }

            var globalNamespace = new GlobalNamespaceProvider();

            foreach (var nameOverride in GlobalNamespaceOverrides ?? Enumerable.Empty <ITaskItem>())
            {
                var wellKnownName = nameOverride.ItemSpec;
                var overridenName = nameOverride.GetMetadata("Override");
                if (overridenName != null && Enum.TryParse(wellKnownName, out WellKnownName name))
                {
                    globalNamespace.OverrideName(name, overridenName);
                }
            }

            PlatformDetectionType platformMask = 0;

            foreach (var platform in Platforms ?? Enumerable.Empty <ITaskItem>())
            {
                if (!Enum.TryParse <PlatformDetectionType>("Is" + platform.ItemSpec, out var parsedPlatform))
                {
                    Log.LogWarning(null, LoggingCodes.InvalidPlatformDetectionType, null, null, 0, 0, 0, 0, $"The platform type {platform} is an unknown platform to SharpGenTools. Falling back to Any platform detection.");
                    platformMask = PlatformDetectionType.Any;
                }
                else
                {
                    platformMask |= parsedPlatform;
                }
            }

            if (platformMask == 0)
            {
                platformMask = PlatformDetectionType.Any;
            }

            var config = new GeneratorConfig
            {
                Platforms = platformMask
            };

            var generator = new RoslynGenerator(
                new Logger(new MSBuildSharpGenLogger(Log), null),
                globalNamespace,
                new CachedDocumentationLinker(DocLinkCache.ItemSpec),
                new ExternalDocCommentsReader(documentationFiles),
                config);

            generator.Run(CsAssembly.Read(Model.ItemSpec), GeneratedCodeFolder);

            return(true);
        }
Esempio n. 15
0
        public void Run(CsAssembly csAssembly, string rootDirectory, string generatedCodeFolder)
        {
            var trees = new List <SyntaxTree>();

            string generatedDirectoryForAssembly = GetAssemblyDirectory(rootDirectory, generatedCodeFolder);
            var    directoryToCreate             = new HashSet <string>(StringComparer.CurrentCulture);

            // Remove the generated directory before creating it
            if (!directoryToCreate.Contains(generatedDirectoryForAssembly))
            {
                directoryToCreate.Add(generatedDirectoryForAssembly);
                if (Directory.Exists(generatedDirectoryForAssembly))
                {
                    foreach (var oldGeneratedFile in Directory.EnumerateFiles(generatedDirectoryForAssembly, "*.cs", SearchOption.AllDirectories))
                    {
                        try
                        {
                            File.Delete(oldGeneratedFile);
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }

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

            Logger.Message("Process Assembly {0} => {1}", csAssembly.Name, generatedDirectoryForAssembly);

            // LocalInterop is once generated per assembly
            trees.Add(
                CSharpSyntaxTree.Create(
                    CompilationUnit().WithMembers(
                        SingletonList <MemberDeclarationSyntax>(
                            Generators.LocalInterop.GenerateCode(csAssembly))
                        )
                    .WithLeadingTrivia(Comment("// <auto-generated/>\n"))
                    .NormalizeWhitespace(elasticTrivia: true))
                .WithFilePath(Path.Combine(generatedDirectoryForAssembly, "LocalInterop.cs")));

            foreach (var csNamespace in csAssembly.Namespaces)
            {
                var nameSpaceDirectory = GetNamespaceDirectory(generatedDirectoryForAssembly, csNamespace);
                if (!Directory.Exists(nameSpaceDirectory))
                {
                    Directory.CreateDirectory(nameSpaceDirectory);
                }

                trees.Add(
                    CSharpSyntaxTree.Create(
                        GenerateCompilationUnit(csNamespace.Name, csNamespace.Enums.OrderBy(element => element.Name), Generators.Enum))
                    .WithFilePath(Path.Combine(nameSpaceDirectory, "Enumerations.cs")));
                trees.Add(
                    CSharpSyntaxTree.Create(
                        GenerateCompilationUnit(csNamespace.Name, csNamespace.Structs.OrderBy(element => element.Name), Generators.Struct))
                    .WithFilePath(Path.Combine(nameSpaceDirectory, "Structures.cs")));
                trees.Add(
                    CSharpSyntaxTree.Create(
                        GenerateCompilationUnit(csNamespace.Name, csNamespace.Classes.OrderBy(element => element.Name), Generators.Group))
                    .WithFilePath(Path.Combine(nameSpaceDirectory, "Functions.cs")));
                trees.Add(
                    CSharpSyntaxTree.Create(
                        GenerateCompilationUnit(csNamespace.Name, csNamespace.Interfaces.OrderBy(element => element.Name), Generators.Interface))
                    .WithFilePath(Path.Combine(nameSpaceDirectory, "Interfaces.cs")));
            }

            foreach (var tree in trees)
            {
                File.WriteAllText(tree.FilePath, tree.GetCompilationUnitRoot().ToFullString());
            }
        }