SharpGen/Parser/CppHeaderGenerator.cs (106 lines of code) (raw):

using SharpGen.Config; using SharpGen.CppModel; using SharpGen.Logging; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace SharpGen.Parser { public sealed class CppHeaderGenerator { private readonly Ioc ioc; private const string Version = "1.1"; private Logger Logger => ioc.Logger; private string OutputPath { get; } public CppHeaderGenerator(string outputPath, Ioc ioc) { OutputPath = outputPath; this.ioc = ioc ?? throw new ArgumentNullException(nameof(ioc)); } public readonly struct Result { public HashSet<ConfigFile> UpdatedConfigs { get; } public string Prologue { get; } public Result(HashSet<ConfigFile> updatedConfigs, string prolog) { UpdatedConfigs = updatedConfigs ?? throw new ArgumentNullException(nameof(updatedConfigs)); Prologue = prolog ?? throw new ArgumentNullException(nameof(prolog)); } } public Result GenerateCppHeaders(ConfigFile configRoot, IReadOnlyCollection<ConfigFile> configsWithIncludes, ISet<ConfigFile> configsWithExtensionHeaders) { var updatedConfigs = new HashSet<ConfigFile>(ConfigFile.IdComparer); var prologue = GeneratePrologue(configRoot); // Dump includes foreach (var configFile in configsWithIncludes) { var outputConfigStr = GenerateIncludeConfigContents( configRoot, configFile, configsWithIncludes, configsWithExtensionHeaders, prologue ); var fileName = Path.Combine(OutputPath, configFile.HeaderFileName); // Test if Last config file was generated. If not, then we need to generate it // If it exists, then we need to test if it is the same than previous run bool isConfigUpdated; if (File.Exists(fileName)) isConfigUpdated = outputConfigStr != File.ReadAllText(fileName); else isConfigUpdated = true; // Small optim: just write the header file when the file is updated or new if (!isConfigUpdated) continue; Logger.Message("Config file changed for C++ headers [{0}]/[{1}]", configFile.Id, configFile.FilePath); updatedConfigs.Add(configFile); File.WriteAllText(fileName, outputConfigStr, Encoding.UTF8); } return new Result(updatedConfigs, prologue); } private static string GeneratePrologue(ConfigFile configRoot) { var prolog = new StringBuilder(); foreach (var prologItem in configRoot.ConfigFilesLoaded.SelectMany(file => file.IncludeProlog)) { prolog.Append(prologItem); } prolog.AppendLine(); return prolog.ToString(); } private static string GenerateIncludeConfigContents(ConfigFile configRoot, ConfigFile configFile, IReadOnlyCollection<ConfigFile> configsWithIncludes, ISet<ConfigFile> configsWithExtensionHeaders, string prolog) { using var outputConfig = new StringWriter(); outputConfig.WriteLine("// SharpGen include config [{0}] - Version {1}", configFile.Id, Version); if (configRoot.Id == configFile.Id) outputConfig.Write(prolog); // Write includes foreach (var includeRule in configFile.Includes) { if (!string.IsNullOrEmpty(includeRule.Pre)) { outputConfig.WriteLine(includeRule.Pre); } outputConfig.WriteLine("#include \"{0}\"", includeRule.File); if (!string.IsNullOrEmpty(includeRule.Post)) { outputConfig.WriteLine(includeRule.Post); } } // Write includes to references foreach (var reference in configFile.References) { if (configsWithIncludes.Contains(reference)) outputConfig.WriteLine("#include \"{0}\"", reference.HeaderFileName); } // Dump Create from macros if (configsWithExtensionHeaders.Contains(configFile)) { foreach (var typeBaseRule in configFile.Extension) { if (typeBaseRule.GeneratesExtensionHeader()) outputConfig.WriteLine("// {0}", typeBaseRule); } // Include extension header if it exists // so we can generate extension headers without needing them to already exist. outputConfig.WriteLine("#if __has_include(\"{0}\")", configFile.ExtensionFileName); outputConfig.WriteLine("#include \"{0}\"", configFile.ExtensionFileName); outputConfig.WriteLine("#endif"); } return outputConfig.ToString(); } } }