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; }
/// <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(); } }
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()); } }
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(
/// <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); }
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); }
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); }
/// <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[]
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); }
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); } }
/// <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"); } }
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); }
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()); } }