public override async Task FinalizeGeneration(ProtocolGeneration proto) { await base.FinalizeGeneration(proto); if (proto.Protocol.Namespace != "Bethesda") { return; } FileGeneration fg = new FileGeneration(); fg.AppendLine("using System;"); fg.AppendLine(); using (new NamespaceWrapper(fg, "Mutagen.Bethesda")) { using (var cl = new ClassWrapper(fg, "GameCategoryHelper")) { cl.Partial = true; cl.Static = true; } using (new BraceWrapper(fg)) { using (var args = new FunctionWrapper(fg, $"public static {nameof(GameCategory)} FromModType<TMod>")) { args.Wheres.Add($"where TMod : {nameof(IModGetter)}"); } using (new BraceWrapper(fg)) { fg.AppendLine("switch (typeof(TMod).Name)"); using (new BraceWrapper(fg)) { foreach (var cat in EnumExt.GetValues <GameCategory>()) { fg.AppendLine($"case \"I{cat}Mod\":"); fg.AppendLine($"case \"I{cat}ModGetter\":"); using (new DepthWrapper(fg)) { fg.AppendLine($"return {nameof(GameCategory)}.{cat};"); } } fg.AppendLine("default:"); using (new BraceWrapper(fg)) { fg.AppendLine("throw new ArgumentException($\"Unknown game type for: {typeof(TMod).Name}\");"); } } } } } var path = Path.Combine(proto.DefFileLocation.FullName, "../Extensions", $"GameCategoryHelper{Loqui.Generation.Constants.AutogeneratedMarkerString}.cs"); fg.Generate(path); proto.GeneratedFiles.Add(path, ProjItemType.Compile); }
public string[] Create(FilePath solutionPath) { var slnDir = Path.GetDirectoryName(solutionPath) !; _FileSystem.Directory.CreateDirectory(solutionPath.Directory); // Create solution FileGeneration fg = new(); fg.AppendLine($"Microsoft Visual Studio Solution File, Format Version 12.00"); fg.AppendLine($"# Visual Studio Version 16"); fg.AppendLine($"VisualStudioVersion = 16.0.30330.147"); fg.AppendLine($"MinimumVisualStudioVersion = 10.0.40219.1"); fg.Generate(solutionPath); // Create editorconfig fg = new FileGeneration(); fg.AppendLine("[*]"); fg.AppendLine("charset = utf-8"); fg.AppendLine("end_of_line = crlf"); fg.AppendLine(); fg.AppendLine("[*.cs]"); fg.AppendLine(); fg.AppendLine("# CS4014: Task not awaited"); fg.AppendLine("dotnet_diagnostic.CS4014.severity = error"); fg.AppendLine(); fg.AppendLine("# CS1998: Async function does not contain await"); fg.AppendLine("dotnet_diagnostic.CS1998.severity = silent"); fg.Generate(Path.Combine(slnDir, ".editorconfig")); // Add nullability errors fg = new FileGeneration(); fg.AppendLine("<Project>"); using (new DepthWrapper(fg)) { fg.AppendLine("<PropertyGroup>"); using (new DepthWrapper(fg)) { fg.AppendLine("<Nullable>enable</Nullable>"); fg.AppendLine("<WarningsAsErrors>nullable</WarningsAsErrors>"); } fg.AppendLine("</PropertyGroup>"); } fg.AppendLine("</Project>"); fg.Generate(Path.Combine(slnDir, "Directory.Build.props"), fileSystem: _FileSystem); return(new string[] { solutionPath, Path.Combine(slnDir, ".editorconfig"), Path.Combine(slnDir, "Directory.Build.props"), }); }
public override async Task FinalizeGeneration(ProtocolGeneration proto) { if (proto.Protocol.Namespace.Equals("Bethesda")) { return; } bool generate = false; FileGeneration fg = new FileGeneration(); var modObj = proto.ObjectGenerationsByID.Values.FirstOrDefault(o => o.GetObjectType() == ObjectType.Mod); fg.AppendLine("using System.Collections.Generic;"); fg.AppendLine("using Mutagen.Bethesda.Plugins.Cache;"); fg.AppendLine("using Mutagen.Bethesda.Plugins.Order;"); fg.AppendLine("using Mutagen.Bethesda.Plugins.Order.Internals;"); fg.AppendLine(); using (var n = new NamespaceWrapper(fg, proto.DefaultNamespace)) { using (var c = new ClassWrapper(fg, "TypeOptionSolidifierMixIns")) { c.Static = true; } using (new BraceWrapper(fg)) { using (new RegionWrapper(fg, "Normal")) { foreach (var obj in proto.ObjectGenerationsByName .OrderBy(x => x.Key) .Select(x => x.Value)) { if (!await obj.IsMajorRecord()) { continue; } var topLevel = modObj.Fields.Any(x => { if (x is not GroupType grup) { return(false); } var grupTarget = grup.GetGroupTarget(); if (grupTarget == obj) { return(true); } return(obj.BaseClassTrail().Any(b => b == grupTarget)); }); var topLevelStr = topLevel ? "TopLevel" : string.Empty; using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Scope a load order query to {obj.Name}"); comment.Parameters.GetOrAdd("listings").AppendLine("ModListings to query"); comment.Return.AppendLine($"A typed object to do further queries on {obj.Name}"); } using (var args = new FunctionWrapper(fg, $"public static {topLevelStr}TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {obj.Interface(getter: false)}, {obj.Interface(getter: true)}> {obj.Name}")) { args.Add($"this IEnumerable<IModListingGetter<I{proto.Protocol.Namespace}ModGetter>> listings"); } using (new BraceWrapper(fg)) { using (var args = new ArgsWrapper(fg, $"return new {topLevelStr}TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {obj.Interface(getter: false)}, {obj.Interface(getter: true)}>")) { args.Add($"(bool includeDeletedRecords) => listings.WinningOverrides<{obj.Interface(getter: true)}>(includeDeletedRecords: includeDeletedRecords)"); args.Add($"({nameof(ILinkCache)} linkCache, bool includeDeletedRecords) => listings.WinningOverrideContexts<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {obj.Interface(getter: false)}, {obj.Interface(getter: true)}>(linkCache, includeDeletedRecords: includeDeletedRecords)"); } } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Scope a load order query to {obj.Name}"); comment.Parameters.GetOrAdd("mods").AppendLine("Mods to query"); comment.Return.AppendLine($"A typed object to do further queries on {obj.Name}"); } using (var args = new FunctionWrapper(fg, $"public static {topLevelStr}TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {obj.Interface(getter: false)}, {obj.Interface(getter: true)}> {obj.Name}")) { args.Add($"this IEnumerable<I{proto.Protocol.Namespace}ModGetter> mods"); } using (new BraceWrapper(fg)) { using (var args = new ArgsWrapper(fg, $"return new {topLevelStr}TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {obj.Interface(getter: false)}, {obj.Interface(getter: true)}>")) { args.Add($"(bool includeDeletedRecords) => mods.WinningOverrides<{obj.Interface(getter: true)}>(includeDeletedRecords: includeDeletedRecords)"); args.Add($"({nameof(ILinkCache)} linkCache, bool includeDeletedRecords) => mods.WinningOverrideContexts<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {obj.Interface(getter: false)}, {obj.Interface(getter: true)}>(linkCache, includeDeletedRecords: includeDeletedRecords)"); } } fg.AppendLine(); generate = true; } } using (new RegionWrapper(fg, "Link Interfaces")) { if (LinkInterfaceModule.ObjectMappings.TryGetValue(proto.Protocol, out var interfs)) { foreach (var interf in interfs) { var getter = $"{interf.Key}Getter"; using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Scope a load order query to {interf.Key}"); comment.Parameters.GetOrAdd("listings").AppendLine("ModListings to query"); comment.Return.AppendLine($"A typed object to do further queries on {interf.Key}"); } using (var args = new FunctionWrapper(fg, $"public static TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {interf.Key}, {getter}> {interf.Key}")) { args.Add($"this IEnumerable<IModListingGetter<I{proto.Protocol.Namespace}ModGetter>> listings"); } using (new BraceWrapper(fg)) { using (var args = new ArgsWrapper(fg, $"return new TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {interf.Key}, {getter}>")) { args.Add($"(bool includeDeletedRecords) => listings.WinningOverrides<{getter}>(includeDeletedRecords: includeDeletedRecords)"); args.Add($"({nameof(ILinkCache)} linkCache, bool includeDeletedRecords) => listings.WinningOverrideContexts<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {interf.Key}, {getter}>(linkCache, includeDeletedRecords: includeDeletedRecords)"); } } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Scope a load order query to {interf.Key}"); comment.Parameters.GetOrAdd("mods").AppendLine("Mods to query"); comment.Return.AppendLine($"A typed object to do further queries on {interf.Key}"); } using (var args = new FunctionWrapper(fg, $"public static TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {interf.Key}, {getter}> {interf.Key}")) { args.Add($"this IEnumerable<I{proto.Protocol.Namespace}ModGetter> mods"); } using (new BraceWrapper(fg)) { using (var args = new ArgsWrapper(fg, $"return new TypedLoadOrderAccess<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {interf.Key}, {getter}>")) { args.Add($"(bool includeDeletedRecords) => mods.WinningOverrides<{getter}>(includeDeletedRecords: includeDeletedRecords)"); args.Add($"({nameof(ILinkCache)} linkCache, bool includeDeletedRecords) => mods.WinningOverrideContexts<I{proto.Protocol.Namespace}Mod, I{proto.Protocol.Namespace}ModGetter, {interf.Key}, {getter}>(linkCache, includeDeletedRecords: includeDeletedRecords)"); } } fg.AppendLine(); } } } } } if (!generate) { return; } var path = Path.Combine(proto.DefFileLocation.FullName, $"TypeSolidifier{Loqui.Generation.Constants.AutogeneratedMarkerString}.cs"); fg.Generate(path); proto.GeneratedFiles.Add(path, ProjItemType.Compile); }
public override async Task FinalizeGeneration(ProtocolGeneration proto) { await base.FinalizeGeneration(proto); if (proto.Protocol.Namespace == "Bethesda") { return; } bool all = proto.Protocol.Namespace == "All"; FileGeneration fg = new FileGeneration(); ObjectGeneration.AddAutogenerationComment(fg); fg.AppendLine("using Loqui;"); fg.AppendLine("using Mutagen.Bethesda.Plugins.Records.Internals;"); fg.AppendLine(); using (new NamespaceWrapper(fg, proto.DefaultNamespace, fileScoped: false)) { using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine("A static class to house initialization warmup logic"); } using (var c = new ClassWrapper(fg, $"Warmup{proto.Protocol.Namespace}")) { c.Static = true; c.Partial = true; } using (new BraceWrapper(fg)) { using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine("Will initialize internals in a more efficient way that avoids reflection."); comment.Summary.AppendLine("Not required to call, but can be used to warm up ahead of time."); if (!all) { comment.Summary.AppendLine("<br/><br/>NOTE: Calling this warmup which is for a single game, will require you warm up"); comment.Summary.AppendLine("other games in the same fashion. Use WarmupAll if you want all games to be warmed."); } } using (var args = new FunctionWrapper(fg, "public static void Init")) { } using (new BraceWrapper(fg)) { if (all) { using (var args = new ArgsWrapper(fg, $"Loqui.Initialization.SpinUp")) { args.Add($"new ProtocolDefinition_Bethesda()"); } foreach (var otherProto in proto.Gen.Protocols.Values .Where(p => p.DefaultNamespace.Contains("Mutagen.Bethesda")) .Where(p => !p.DefaultNamespace.Contains("Mutagen.Bethesda.Plugins.Records")) .Where(p => !object.ReferenceEquals(p, proto))) { fg.AppendLine($"{otherProto.DefaultNamespace}.Warmup{otherProto.Protocol.Namespace}.Init();"); } } else { using (var args = new ArgsWrapper(fg, $"Loqui.Initialization.SpinUp")) { args.Add($"new ProtocolDefinition_Bethesda()"); args.Add($"new ProtocolDefinition_{proto.Protocol.Namespace}()"); } fg.AppendLine($"LinkInterfaceMapping.AutomaticRegistration = false;"); using (var args = new ArgsWrapper(fg, $"LinkInterfaceMapping.Register")) { args.Add($"new {proto.DefaultNamespace}.Internals.LinkInterfaceMapping()"); } fg.AppendLine("InitCustom();"); } } fg.AppendLine("static partial void InitCustom();"); } } var path = Path.Combine(proto.DefFileLocation.FullName, $"../Warmup{proto.Protocol.Namespace}{Loqui.Generation.Constants.AutogeneratedMarkerString}.cs"); fg.Generate(path); proto.GeneratedFiles.Add(path, ProjItemType.Compile); }
public override async Task PrepareGeneration(ProtocolGeneration proto) { await base.PrepareGeneration(proto); // Compile interfaces implementing interfaces mapping data var interfaceInheritenceMappings = new Dictionary <string, HashSet <string> >(); foreach (var obj in proto.ObjectGenerationsByID.Values) { foreach (var item in obj.Node.Elements(XName.Get("LinkInterface", LoquiGenerator.Namespace))) { ObjectMappings.GetOrAdd(proto.Protocol).GetOrAdd(item.Value).Add(obj); } } // Generate interface files themselves if (!ObjectMappings.TryGetValue(proto.Protocol, out var mappings)) { return; } foreach (var interf in mappings) { FileGeneration fg = new FileGeneration(); ObjectGeneration.AddAutogenerationComment(fg); fg.AppendLine("using Mutagen.Bethesda;"); fg.AppendLine(); var implementedObjs = new HashSet <ObjectGeneration>(); void AddObjs(string interfKey) { implementedObjs.Add(ObjectMappings[proto.Protocol][interfKey]); if (interfaceInheritenceMappings.TryGetValue(interfKey, out var parents)) { foreach (var parent in parents) { AddObjs(parent); } } } AddObjs(interf.Key); using (new NamespaceWrapper(fg, proto.DefaultNamespace, fileScoped: false)) { fg.AppendLine("/// <summary>"); fg.AppendLine($"/// Implemented by: [{string.Join(", ", implementedObjs.Select(o => o.ObjectName))}]"); fg.AppendLine("/// </summary>"); using (var c = new ClassWrapper(fg, interf.Key)) { c.Type = ClassWrapper.ObjectType.@interface; c.Interfaces.Add($"I{proto.Protocol.Namespace}MajorRecordInternal"); c.Interfaces.Add($"{interf.Key}Getter"); if (interfaceInheritenceMappings.TryGetValue(interf.Key, out var impls)) { c.Interfaces.Add(impls); } c.Partial = true; } using (new BraceWrapper(fg)) { } fg.AppendLine(); fg.AppendLine("/// <summary>"); fg.AppendLine($"/// Implemented by: [{string.Join(", ", implementedObjs.Select(o => o.ObjectName))}]"); fg.AppendLine("/// </summary>"); using (var c = new ClassWrapper(fg, $"{interf.Key}Getter")) { c.Type = ClassWrapper.ObjectType.@interface; c.Interfaces.Add($"I{proto.Protocol.Namespace}MajorRecordGetter"); if (interfaceInheritenceMappings.TryGetValue(interf.Key, out var impls)) { c.Interfaces.Add(impls.Select(i => $"{i}Getter")); } c.Partial = true; } using (new BraceWrapper(fg)) { } } var path = Path.Combine(proto.DefFileLocation.FullName, $"../Interfaces/{interf.Key}{Loqui.Generation.Constants.AutogeneratedMarkerString}.cs"); fg.Generate(path); proto.GeneratedFiles.Add(path, ProjItemType.Compile); } // Generate interface to major record mapping registry FileGeneration mappingGen = new FileGeneration(); ObjectGeneration.AddAutogenerationComment(mappingGen); mappingGen.AppendLine($"using System;"); mappingGen.AppendLine($"using System.Collections.Generic;"); mappingGen.AppendLine($"using Mutagen.Bethesda.Plugins.Records.Internals;"); mappingGen.AppendLine(); using (new NamespaceWrapper(mappingGen, $"{proto.DefaultNamespace}.Internals", fileScoped: false)) { using (var c = new ClassWrapper(mappingGen, "LinkInterfaceMapping")) { c.Interfaces.Add(nameof(ILinkInterfaceMapping)); } using (new BraceWrapper(mappingGen)) { mappingGen.AppendLine($"public IReadOnlyDictionary<Type, Type[]> InterfaceToObjectTypes {{ get; }}"); mappingGen.AppendLine(); mappingGen.AppendLine($"public {nameof(GameCategory)} GameCategory => {nameof(GameCategory)}.{proto.Protocol.Namespace};"); mappingGen.AppendLine(); mappingGen.AppendLine("public LinkInterfaceMapping()"); using (new BraceWrapper(mappingGen)) { mappingGen.AppendLine($"var dict = new Dictionary<Type, Type[]>();"); foreach (var interf in mappings) { mappingGen.AppendLine($"dict[typeof({interf.Key})] = new Type[]"); using (new BraceWrapper(mappingGen) { AppendSemicolon = true }) { foreach (var obj in interf.Value) { mappingGen.AppendLine($"typeof({obj.ObjectName}),"); } } mappingGen.AppendLine($"dict[typeof({interf.Key}Getter)] = dict[typeof({interf.Key})];"); } mappingGen.AppendLine($"InterfaceToObjectTypes = dict;"); } } } mappingGen.AppendLine(); var mappingPath = Path.Combine(proto.DefFileLocation.FullName, $"../Interfaces/LinkInterfaceMapping{Loqui.Generation.Constants.AutogeneratedMarkerString}.cs"); mappingGen.Generate(mappingPath); proto.GeneratedFiles.Add(mappingPath, ProjItemType.Compile); }
public override async Task FinalizeGeneration(ProtocolGeneration proto) { if (proto.Protocol.Namespace.Equals("Bethesda")) { return; } var objData = proto.ObjectGenerationsByID.Values.FirstOrDefault()?.GetObjectData(); if (objData == null) { return; } var relString = objData.HasMultipleReleases ? "release.ToGameRelease()" : $"{nameof(GameRelease)}.{proto.Protocol.Namespace}"; FileGeneration fg = new FileGeneration(); fg.AppendLine("using System.Collections.Generic;"); fg.AppendLine($"using Mutagen.Bethesda.Plugins;"); fg.AppendLine($"using Mutagen.Bethesda.Plugins.Implicit;"); fg.AppendLine($"using Mutagen.Bethesda.{proto.Protocol.Namespace};"); fg.AppendLine(); using (var n = new NamespaceWrapper(fg, "Mutagen.Bethesda")) { using (var c = new ClassWrapper(fg, "ImplicitsMixIn")) { c.Static = true; } using (new BraceWrapper(fg)) { using (var args = new FunctionWrapper(fg, $"public static IReadOnlyCollection<ModKey> {proto.Protocol.Namespace}")) { args.Add($"this ImplicitBaseMasters _"); if (objData.HasMultipleReleases) { args.Add($"{objData.GameCategory}Release release"); } } using (new BraceWrapper(fg)) { fg.AppendLine($"return Implicits.Get({relString}).BaseMasters;"); } fg.AppendLine(); using (var args = new FunctionWrapper(fg, $"public static IReadOnlyCollection<ModKey> {proto.Protocol.Namespace}")) { args.Add($"this ImplicitListings _"); if (objData.HasMultipleReleases) { args.Add($"{objData.GameCategory}Release release"); } } using (new BraceWrapper(fg)) { fg.AppendLine($"return Implicits.Get({relString}).Listings;"); } fg.AppendLine(); using (var args = new FunctionWrapper(fg, $"public static IReadOnlyCollection<FormKey> {proto.Protocol.Namespace}")) { args.Add($"this ImplicitRecordFormKeys _"); if (objData.HasMultipleReleases) { args.Add($"{objData.GameCategory}Release release"); } } using (new BraceWrapper(fg)) { fg.AppendLine($"return Implicits.Get({relString}).RecordFormKeys;"); } } } var path = Path.Combine(proto.DefFileLocation.FullName, $"../ImplicitsMixIn{Loqui.Generation.Constants.AutogeneratedMarkerString}.cs"); fg.Generate(path); proto.GeneratedFiles.Add(path, ProjItemType.Compile); }
public override async Task FinalizeGeneration(ProtocolGeneration proto) { if (proto.Protocol.Namespace.Equals("All") || proto.Protocol.Namespace.Equals("Bethesda")) { return; } FileGeneration fg = new FileGeneration(); fg.AppendLine("using System.Collections.Generic;"); fg.AppendLine("using Mutagen.Bethesda.Plugins.Order;"); fg.AppendLine("using Mutagen.Bethesda.Cache.Implementations;"); fg.AppendLine(); using (var n = new NamespaceWrapper(fg, proto.DefaultNamespace)) { var setterName = $"I{proto.Protocol.Namespace}Mod"; var getterName = $"I{proto.Protocol.Namespace}ModGetter"; var generic = $"<{setterName}, {getterName}>"; using (var c = new ClassWrapper(fg, "LinkCacheMixIns")) { c.Static = true; } using (new BraceWrapper(fg)) { using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a Link Cache using a single mod as its link target. <br/>"); comment.Summary.AppendLine($"Modification of the target Mod is not safe. Internal caches can become incorrect if "); comment.Summary.AppendLine($"modifications occur on content already cached."); comment.Parameters.GetOrAdd("mod").AppendLine("Mod to construct the package relative to"); comment.Return.AppendLine($"LinkPackage attached to given mod"); } using (var args = new FunctionWrapper(fg, $"public static ImmutableModLinkCache{generic} ToImmutableLinkCache")) { args.Add($"this {getterName} mod"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return mod.ToImmutableLinkCache{generic}();"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a Link Cache using a single mod as its link target. Mod is allowed to be modified afterwards, but"); comment.Summary.AppendLine($"this comes at a performance cost of not allowing much caching to be done. If the mod is not expected to"); comment.Summary.AppendLine($"be modified afterwards, use ImmutableModLinkCache instead.<br/>"); comment.Parameters.GetOrAdd("mod").AppendLine("Mod to construct the package relative to"); comment.Return.AppendLine($"LinkPackage attached to given mod"); } using (var args = new FunctionWrapper(fg, $"public static MutableModLinkCache{generic} ToMutableLinkCache")) { args.Add($"this {getterName} mod"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return mod.ToMutableLinkCache{generic}();"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a new linking package relative to a load order.<br/>"); comment.Summary.AppendLine($"Will resolve links to the highest overriding mod containing the record being sought. <br/>"); comment.Summary.AppendLine($"Modification of the target LoadOrder, or Mods on the LoadOrder is not safe. Internal caches can become"); comment.Summary.AppendLine($"incorrect if modifications occur on content already cached."); comment.Parameters.GetOrAdd("loadOrder").AppendLine("LoadOrder to construct the package relative to"); comment.Return.AppendLine($"LinkPackage attached to given LoadOrder"); } using (var args = new FunctionWrapper(fg, $"public static ImmutableLoadOrderLinkCache{generic} ToImmutableLinkCache")) { args.Add($"this ILoadOrderGetter<{getterName}> loadOrder"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return loadOrder.ToImmutableLinkCache{generic}();"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a new linking package relative to a load order.<br/>"); comment.Summary.AppendLine($"Will resolve links to the highest overriding mod containing the record being sought. <br/>"); comment.Summary.AppendLine($"Modification of the target LoadOrder, or Mods on the LoadOrder is not safe. Internal caches can become"); comment.Summary.AppendLine($"incorrect if modifications occur on content already cached."); comment.Parameters.GetOrAdd("loadOrder").AppendLine("LoadOrder to construct the package relative to"); comment.Return.AppendLine($"LinkPackage attached to given LoadOrder"); } using (var args = new FunctionWrapper(fg, $"public static ImmutableLoadOrderLinkCache{generic} ToImmutableLinkCache")) { args.Add($"this ILoadOrderGetter<IModListingGetter<{getterName}>> loadOrder"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return loadOrder.ToImmutableLinkCache{generic}();"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a new linking package relative to a load order.<br/>"); comment.Summary.AppendLine($"Will resolve links to the highest overriding mod containing the record being sought. <br/>"); comment.Summary.AppendLine($"Modification of the target LoadOrder, or Mods on the LoadOrder is not safe. Internal caches can become"); comment.Summary.AppendLine($"incorrect if modifications occur on content already cached."); comment.Parameters.GetOrAdd("loadOrder").AppendLine("LoadOrder to construct the package relative to"); comment.Return.AppendLine($"LinkPackage attached to given LoadOrder"); } using (var args = new FunctionWrapper(fg, $"public static ImmutableLoadOrderLinkCache{generic} ToImmutableLinkCache")) { args.Add($"this IEnumerable<IModListingGetter<{getterName}>> loadOrder"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return loadOrder.ToImmutableLinkCache{generic}();"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a new linking package relative to a load order.<br/>"); comment.Summary.AppendLine($"Will resolve links to the highest overriding mod containing the record being sought. <br/>"); comment.Summary.AppendLine($"Modification of the target LoadOrder, or Mods on the LoadOrder is not safe. Internal caches can become"); comment.Summary.AppendLine($"incorrect if modifications occur on content already cached."); comment.Parameters.GetOrAdd("loadOrder").AppendLine("LoadOrder to construct the package relative to"); comment.Return.AppendLine($"LinkPackage attached to given LoadOrder"); } using (var args = new FunctionWrapper(fg, $"public static ImmutableLoadOrderLinkCache{generic} ToImmutableLinkCache")) { args.Add($"this IEnumerable<{getterName}> loadOrder"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return loadOrder.ToImmutableLinkCache{generic}();"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a mutable load order link cache by combining an existing immutable load order cache,"); comment.Summary.AppendLine($"plus a set of mods to be put at the end of the load order and allow to be mutable."); comment.Parameters.GetOrAdd("immutableBaseCache").AppendLine("LoadOrderCache to use as the immutable base"); comment.Parameters.GetOrAdd("mutableMods").AppendLine("Set of mods to place at the end of the load order, which are allowed to be modified afterwards"); comment.Return.AppendLine($"LinkPackage attached to given LoadOrder"); } using (var args = new FunctionWrapper(fg, $"public static MutableLoadOrderLinkCache{generic} ToMutableLinkCache")) { args.Add($"this ILoadOrderGetter<{getterName}> immutableBaseCache"); args.Add($"params {setterName}[] mutableMods"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return immutableBaseCache.ToMutableLinkCache{generic}(mutableMods);"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a mutable load order link cache by combining an existing immutable load order cache,"); comment.Summary.AppendLine($"plus a set of mods to be put at the end of the load order and allow to be mutable."); comment.Parameters.GetOrAdd("immutableBaseCache").AppendLine("LoadOrderCache to use as the immutable base"); comment.Parameters.GetOrAdd("mutableMods").AppendLine("Set of mods to place at the end of the load order, which are allowed to be modified afterwards"); comment.Return.AppendLine($"LinkPackage attached to given LoadOrder"); } using (var args = new FunctionWrapper(fg, $"public static MutableLoadOrderLinkCache{generic} ToMutableLinkCache")) { args.Add($"this ILoadOrderGetter<IModListingGetter<{getterName}>> immutableBaseCache"); args.Add($"params {setterName}[] mutableMods"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return immutableBaseCache.ToMutableLinkCache{generic}(mutableMods);"); } fg.AppendLine(); using (var comment = new CommentWrapper(fg)) { comment.Summary.AppendLine($"Creates a mutable load order link cache by combining an existing immutable load order cache,"); comment.Summary.AppendLine($"plus a set of mods to be put at the end of the load order and allow to be mutable."); comment.Parameters.GetOrAdd("immutableBaseCache").AppendLine("LoadOrderCache to use as the immutable base"); comment.Parameters.GetOrAdd("mutableMods").AppendLine("Set of mods to place at the end of the load order, which are allowed to be modified afterwards"); comment.Return.AppendLine($"LinkPackage attached to given LoadOrder"); } using (var args = new FunctionWrapper(fg, $"public static MutableLoadOrderLinkCache{generic} ToMutableLinkCache")) { args.Add($"this IEnumerable<{getterName}> immutableBaseCache"); args.Add($"params {setterName}[] mutableMods"); } using (new BraceWrapper(fg)) { fg.AppendLine($"return immutableBaseCache.ToMutableLinkCache{generic}(mutableMods);"); } fg.AppendLine(); } } var path = Path.Combine(proto.DefFileLocation.FullName, $"LinkCacheMixIns{Loqui.Generation.Constants.AutogeneratedMarkerString}.cs"); fg.Generate(path); proto.GeneratedFiles.Add(path, ProjItemType.Compile); }
public static string[] CreateProject(string projPath, GameCategory category) { Directory.CreateDirectory(Path.GetDirectoryName(projPath)); var projName = Path.GetFileNameWithoutExtension(projPath); // Generate Project File FileGeneration fg = new FileGeneration(); fg.AppendLine("<Project Sdk=\"Microsoft.NET.Sdk\">"); using (new DepthWrapper(fg)) { fg.AppendLine($"<PropertyGroup>"); using (new DepthWrapper(fg)) { fg.AppendLine($"<OutputType>Exe</OutputType>"); fg.AppendLine($"<TargetFramework>netcoreapp3.1</TargetFramework>"); } fg.AppendLine($"</PropertyGroup>"); fg.AppendLine(); fg.AppendLine($"<ItemGroup>"); using (new DepthWrapper(fg)) { fg.AppendLine($"<PackageReference Include=\"Mutagen.Bethesda\" Version=\"{Versions.MutagenVersion}\" />"); fg.AppendLine($"<PackageReference Include=\"Mutagen.Bethesda.Synthesis\" Version=\"{Versions.SynthesisVersion}\" />"); fg.AppendLine($"<PackageReference Include=\"GitInfo\" Version=\"*\">"); using (new DepthWrapper(fg)) { fg.AppendLine($"<PrivateAssets>all</PrivateAssets>"); fg.AppendLine($"<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>"); } fg.AppendLine($"</PackageReference>"); } fg.AppendLine($"</ItemGroup>"); fg.AppendLine(); } fg.AppendLine("</Project>"); fg.Generate(projPath); // Generate Program.cs fg = new FileGeneration(); fg.AppendLine("using System;"); fg.AppendLine("using System.Collections.Generic;"); fg.AppendLine("using System.Linq;"); fg.AppendLine("using Mutagen.Bethesda;"); fg.AppendLine("using Mutagen.Bethesda.Synthesis;"); fg.AppendLine($"using Mutagen.Bethesda.{category};"); fg.AppendLine(); fg.AppendLine($"namespace {projName}"); using (new BraceWrapper(fg)) { fg.AppendLine($"public class Program"); using (new BraceWrapper(fg)) { fg.AppendLine("public static int Main(string[] args)"); using (new BraceWrapper(fg)) { fg.AppendLine($"return SynthesisPipeline.Instance.Patch<I{category}Mod, I{category}ModGetter>("); using (new DepthWrapper(fg)) { fg.AppendLine("args: args,"); fg.AppendLine("patcher: RunPatch,"); fg.AppendLine($"userPreferences: new {nameof(UserPreferences)}()"); using (new BraceWrapper(fg) { AppendParenthesis = true, AppendSemicolon = true }) { fg.AppendLine($"{nameof(UserPreferences.ActionsForEmptyArgs)} = new {nameof(RunDefaultPatcher)}()"); using (new BraceWrapper(fg)) { fg.AppendLine($"{nameof(RunDefaultPatcher.IdentifyingModKey)} = \"YourPatcher.esp\","); fg.AppendLine($"{nameof(RunDefaultPatcher.TargetRelease)} = {nameof(GameRelease)}.{category.DefaultRelease()},"); fg.AppendLine($"{nameof(RunDefaultPatcher.BlockAutomaticExit)} = true,"); } } } } fg.AppendLine(); fg.AppendLine($"public static void RunPatch(SynthesisState<I{category}Mod, I{category}ModGetter> state)"); using (new BraceWrapper(fg)) { fg.AppendLine($"//Your code here!"); } } } fg.Generate(Path.Combine(Path.GetDirectoryName(projPath) !, "Program.cs")); return(new string[] { projPath, Path.Combine(Path.GetDirectoryName(projPath) !, "Program.cs") });
public override async Task FinalizeGeneration(ProtocolGeneration proto) { FileGeneration fg = new FileGeneration(); if (LinkInterfaceModule.ObjectMappings.TryGetValue(proto.Protocol, out var linkInterfaces)) { Dictionary <string, List <string> > reverse = new Dictionary <string, List <string> >(); fg.AppendLine("# Link Interfaces"); fg.AppendLine("Link Interfaces are used by FormLinks to point to several record types at once. For example, a Container record might be able to contain Armors, Weapons, Ingredients, etc."); fg.AppendLine(); fg.AppendLine("An interface would be defined such as 'IItem', which all Armor, Weapon, Ingredients would all implement."); fg.AppendLine(); fg.AppendLine("A `FormLink<IItem>` could then point to all those record types by pointing to the interface instead."); fg.AppendLine($"## Interfaces to Concrete Classes"); foreach (var interf in linkInterfaces.OrderBy(x => x.Key)) { fg.AppendLine($"### {interf.Key}"); foreach (var obj in interf.Value.OrderBy(o => o.Name)) { fg.AppendLine($"- {obj.Name}"); reverse.GetOrAdd(obj.Name).Add(interf.Key); } } fg.AppendLine($"## Concrete Classes to Interfaces"); foreach (var obj in reverse.OrderBy(x => x.Key)) { fg.AppendLine($"### {obj.Key}"); foreach (var interf in obj.Value.OrderBy(x => x)) { fg.AppendLine($"- {interf}"); } } } var path = Path.Combine(proto.DefFileLocation.FullName, $"../Documentation/LinkInterfaceDocumentation{Loqui.Generation.Constants.AutogeneratedMarkerString}.md"); if (fg.Count > 0) { fg.Generate(path); } fg = new FileGeneration(); if (AspectInterfaceModule.ObjectMappings.TryGetValue(proto.Protocol, out var aspectInterfaces)) { Dictionary <string, List <string> > reverse = new Dictionary <string, List <string> >(); fg.AppendLine("# Aspect Interfaces"); fg.AppendLine("Aspect Interfaces expose common aspects of records. For example, `INamed` are implemented by all records that have a `Name`."); fg.AppendLine(); fg.AppendLine("Functions can then be written that take in `INamed`, allowing any record that has a name to be passed in."); fg.AppendLine($"## Interfaces to Concrete Classes"); foreach (var interf in aspectInterfaces.OrderBy(x => x.Key.Name)) { fg.AppendLine($"### {interf.Key.Name}"); foreach (var obj in interf.Value.OrderBy(x => x.Name)) { fg.AppendLine($"- {obj.Name}"); reverse.GetOrAdd(obj.Name).Add(interf.Key.Name); } } fg.AppendLine($"## Concrete Classes to Interfaces"); foreach (var obj in reverse.OrderBy(x => x.Key)) { fg.AppendLine($"### {obj.Key}"); foreach (var interf in obj.Value.OrderBy(x => x)) { fg.AppendLine($"- {interf}"); } } } path = Path.Combine(proto.DefFileLocation.FullName, $"../Documentation/AspectInterfaceDocumentation{Loqui.Generation.Constants.AutogeneratedMarkerString}.md"); if (fg.Count > 0) { fg.Generate(path); } }
public static string[] CreateProject(string projPath, GameCategory category, bool insertOldVersion = false) { Directory.CreateDirectory(Path.GetDirectoryName(projPath) !); var projName = Path.GetFileNameWithoutExtension(projPath); // Generate Project File FileGeneration fg = new FileGeneration(); fg.AppendLine("<Project Sdk=\"Microsoft.NET.Sdk\">"); using (new DepthWrapper(fg)) { fg.AppendLine($"<PropertyGroup>"); using (new DepthWrapper(fg)) { fg.AppendLine($"<OutputType>Exe</OutputType>"); fg.AppendLine($"<TargetFramework>netcoreapp3.1</TargetFramework>"); } fg.AppendLine($"</PropertyGroup>"); fg.AppendLine(); fg.AppendLine($"<ItemGroup>"); using (new DepthWrapper(fg)) { fg.AppendLine($"<PackageReference Include=\"Mutagen.Bethesda\" Version=\"{(insertOldVersion ? Versions.OldMutagenVersion : Versions.MutagenVersion)}\" />"); fg.AppendLine($"<PackageReference Include=\"Mutagen.Bethesda.Synthesis\" Version=\"{(insertOldVersion ? Versions.OldSynthesisVersion : Versions.SynthesisVersion)}\" />"); } fg.AppendLine($"</ItemGroup>"); fg.AppendLine(); } fg.AppendLine("</Project>"); fg.Generate(projPath); // Generate Program.cs fg = new FileGeneration(); fg.AppendLine("using System;"); fg.AppendLine("using System.Collections.Generic;"); fg.AppendLine("using System.Linq;"); fg.AppendLine("using Mutagen.Bethesda;"); fg.AppendLine("using Mutagen.Bethesda.Synthesis;"); fg.AppendLine($"using Mutagen.Bethesda.{category};"); fg.AppendLine("using System.Threading.Tasks;"); fg.AppendLine(); fg.AppendLine($"namespace {projName}"); using (new BraceWrapper(fg)) { fg.AppendLine($"public class Program"); using (new BraceWrapper(fg)) { fg.AppendLine("public static async Task<int> Main(string[] args)"); using (new BraceWrapper(fg)) { fg.AppendLine($"return await SynthesisPipeline.Instance"); using (new DepthWrapper(fg)) { fg.AppendLine($".AddPatch<I{category}Mod, I{category}ModGetter>(RunPatch)"); fg.AppendLine($".Run(args, new {nameof(RunPreferences)}()"); using (new BraceWrapper(fg) { AppendParenthesis = true, AppendSemicolon = true }) { fg.AppendLine($"{nameof(UserPreferences.ActionsForEmptyArgs)} = new {nameof(RunDefaultPatcher)}()"); using (new BraceWrapper(fg)) { fg.AppendLine($"{nameof(RunDefaultPatcher.IdentifyingModKey)} = \"YourPatcher.esp\","); fg.AppendLine($"{nameof(RunDefaultPatcher.TargetRelease)} = {nameof(GameRelease)}.{category.DefaultRelease()},"); } } } } fg.AppendLine(); fg.AppendLine($"public static void RunPatch({nameof(IPatcherState)}<I{category}Mod, I{category}ModGetter> state)"); using (new BraceWrapper(fg)) { fg.AppendLine($"//Your code here!"); } } } fg.Generate(Path.Combine(Path.GetDirectoryName(projPath) !, "Program.cs")); return(new string[] { projPath, Path.Combine(Path.GetDirectoryName(projPath) !, "Program.cs") });
public static void Generate(GenerateFromMod gen) { var mod = ModInstantiator.Importer(gen.Path, gen.Release); var list = new List <(FormKey FormKey, string Edid, ILoquiRegistration Regis)>(); foreach (var rec in mod.EnumerateMajorRecords()) { if (!rec.EditorID.TryGet(out var edid)) { continue; } var formKey = rec.FormKey; // Only register FormKeys originating from the mod itself if (formKey.ModKey != mod.ModKey) { continue; } var regis = GetMajorRecord(rec.Registration); list.Add((formKey, edid, regis)); } var namespaceStr = $"Mutagen.Bethesda.FormKeys.{gen.Release}"; var importStr = $"Mutagen.Bethesda.{gen.Release.ToCategory()}"; var modName = mod.ModKey.Name.TrimStart("DLC"); foreach (var regis in list.GroupBy(x => x.Regis)) { FileGeneration fg = new FileGeneration(); fg.AppendLine($"using {importStr};"); fg.AppendLine(); using (new NamespaceWrapper(fg, namespaceStr)) { using (var c = new ClassWrapper(fg, modName)) { c.Static = true; c.Partial = true; } using (new BraceWrapper(fg)) { using (var c = new ClassWrapper(fg, regis.Key.Name)) { c.Static = true; } using (new BraceWrapper(fg)) { fg.AppendLine($"private readonly static ModKey ModKey = ModKey.{nameof(ModKey.FromNameAndExtension)}(\"{mod.ModKey}\");"); fg.AppendLine($"private static FormLink<{regis.Key.GetterType.Name}> Construct(uint id) => new FormLink<{regis.Key.GetterType.Name}>(ModKey.{nameof(ModKeyExt.MakeFormKey)}(id));"); foreach (var rec in regis) { fg.AppendLine($"public static FormLink<{regis.Key.GetterType.Name}> {CleanName(rec.Edid, regis.Key.Name)} => Construct(0x{rec.FormKey.ID:x});"); } } } } var path = Path.Combine("Output", gen.Release.ToString(), modName, $"{regis.Key.Name}.cs"); fg.Generate(path); System.Console.WriteLine($"Exported: {path}"); } }