diff --git a/Wizard/Executor.cs b/Wizard/Executor.cs index 019534c..f3edab6 100644 --- a/Wizard/Executor.cs +++ b/Wizard/Executor.cs @@ -31,6 +31,7 @@ using net.r_eg.DllExport.Wizard.Extensions; using net.r_eg.MvsSln; using net.r_eg.MvsSln.Core; +using net.r_eg.MvsSln.Extensions; using net.r_eg.MvsSln.Log; namespace net.r_eg.DllExport.Wizard diff --git a/Wizard/Extensions/CollectionExtension.cs b/Wizard/Extensions/CollectionExtension.cs index 6bd3aae..c20a02b 100644 --- a/Wizard/Extensions/CollectionExtension.cs +++ b/Wizard/Extensions/CollectionExtension.cs @@ -24,10 +24,11 @@ using System; using System.Collections.Generic; +using net.r_eg.MvsSln.Extensions; namespace net.r_eg.DllExport.Wizard.Extensions { - public static class CollectionExtension + internal static class CollectionExtension { /// /// Foreach in Linq manner. @@ -35,27 +36,9 @@ public static class CollectionExtension /// /// /// The action that should be executed for each item. - public static IEnumerable ForEach(this IEnumerable items, Action act) + public static IEnumerable Each(this IEnumerable items, Action act) { - return items?.ForEach((x, i) => act(x)); - } - - /// - /// Foreach in Linq manner. - /// - /// - /// - /// The action that should be executed for each item. - public static IEnumerable ForEach(this IEnumerable items, Action act) - { - if(items == null) { - return null; - } - - long n = 0; - foreach(var item in items) { - act(item, n++); - } + items?.ForEach((x, i) => act(x)); return items; } @@ -69,7 +52,7 @@ public static IEnumerable ForEach(this IEnumerable items, Action public static IEnumerable Combine(this IEnumerable items, string elem, bool before = false) { - if(before && !String.IsNullOrEmpty(elem)) { + if(before && !string.IsNullOrEmpty(elem)) { yield return elem; } @@ -77,7 +60,7 @@ public static IEnumerable Combine(this IEnumerable items, string yield return item; } - if(!before && !String.IsNullOrEmpty(elem)) { + if(!before && !string.IsNullOrEmpty(elem)) { yield return elem; } } diff --git a/Wizard/Extensions/StringExtension.cs b/Wizard/Extensions/StringExtension.cs index 43d414e..ea4e544 100644 --- a/Wizard/Extensions/StringExtension.cs +++ b/Wizard/Extensions/StringExtension.cs @@ -24,8 +24,6 @@ using System; using System.IO; -using System.Security.Cryptography; -using System.Text; namespace net.r_eg.DllExport.Wizard.Extensions { @@ -38,7 +36,7 @@ public static class StringExtension /// public static bool ToBoolean(this string value) { - if(String.IsNullOrWhiteSpace(value)) { + if(string.IsNullOrWhiteSpace(value)) { return false; } @@ -67,7 +65,7 @@ public static bool ToBoolean(this string value) /// public static int ToInteger(this string value) { - if(String.IsNullOrWhiteSpace(value)) { + if(string.IsNullOrWhiteSpace(value)) { return 0; } @@ -81,7 +79,7 @@ public static int ToInteger(this string value) /// public static long ToLongInteger(this string value) { - if(String.IsNullOrWhiteSpace(value)) { + if(string.IsNullOrWhiteSpace(value)) { return 0; } @@ -94,44 +92,17 @@ public static long ToLongInteger(this string value) /// public static void OpenUrl(this string url) { - if(!String.IsNullOrWhiteSpace(url)) { + if(!string.IsNullOrWhiteSpace(url)) { System.Diagnostics.Process.Start(url); } } - /// - /// Calculate SHA-1 hash from file. - /// - /// Path to file. - /// SHA-1 Hash code. - public static string SHA1HashFromFile(this string file) - { - using(var fs = File.OpenRead(file)) - { - using(SHA1 sha1 = SHA1.Create()) { - return BytesToHexView(sha1.ComputeHash(fs)); - } - } - } - - /// - /// To add postfix to filename. - /// - /// Filename or path to file with extension or without. - /// - /// - public static string AddFileNamePostfix(this string fname, string postfix) + internal static bool CmpPublicKeyTokenWith(this string pkToken, string pkTokenAsm) { - if(String.IsNullOrWhiteSpace(fname) || String.IsNullOrWhiteSpace(postfix)) { - return fname; - } - - int idx = fname.LastIndexOf('.'); - if(idx == -1) { - return fname + postfix; + if(pkTokenAsm == null || string.IsNullOrWhiteSpace(pkToken)) { + return false; } - - return fname.Insert(idx, postfix); + return pkToken.Equals(pkTokenAsm, StringComparison.InvariantCultureIgnoreCase); } /// @@ -141,7 +112,7 @@ public static string AddFileNamePostfix(this string fname, string postfix) /// public static string OpenDoubleQuotes(this string value) { - if(String.IsNullOrWhiteSpace(value)) { + if(string.IsNullOrWhiteSpace(value)) { return value; } @@ -183,31 +154,17 @@ public static string FilePathFormat(this string path, string root = null) /// /// /// - public static string CombineRootPath(string path, string root) + private static string CombineRootPath(string path, string root) { - if(String.IsNullOrWhiteSpace(path) || Path.IsPathRooted(path)) { + if(string.IsNullOrWhiteSpace(path) || Path.IsPathRooted(path)) { return path; } - if(String.IsNullOrWhiteSpace(root)) { + if(string.IsNullOrWhiteSpace(root)) { return path; } return Path.Combine(root, path); } - - /// - /// To format bytes data to hex view. - /// - /// Bytes data. - /// Hex view of bytes. - private static string BytesToHexView(byte[] data) - { - var ret = new StringBuilder(); - foreach(byte b in data) { - ret.Append(b.ToString("X2")); - } - return ret.ToString(); - } } } \ No newline at end of file diff --git a/Wizard/Extensions/XProjectExtension.cs b/Wizard/Extensions/XProjectExtension.cs index c667756..22faa18 100644 --- a/Wizard/Extensions/XProjectExtension.cs +++ b/Wizard/Extensions/XProjectExtension.cs @@ -74,5 +74,16 @@ public static Guid GetPId(this IXProject xp) ) .Guid(); } + + public static void AddPackageIfNotExists(this IXProject xp, string id, string version) + { + if(xp == null) { + throw new ArgumentNullException(nameof(xp)); + } + + if(xp.GetFirstPackageReference(id ?? throw new ArgumentNullException(nameof(id))).parentItem == null) { + xp.AddPackageReference(id, version); + } + } } } \ No newline at end of file diff --git a/Wizard/Gears/IProjectGear.cs b/Wizard/Gears/IProjectGear.cs new file mode 100644 index 0000000..f05b762 --- /dev/null +++ b/Wizard/Gears/IProjectGear.cs @@ -0,0 +1,33 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +namespace net.r_eg.DllExport.Wizard.Gears +{ + internal interface IProjectGear + { + void Install(); + + void Uninstall(bool hardReset); + } +} diff --git a/Wizard/Gears/PreProcGear.cs b/Wizard/Gears/PreProcGear.cs new file mode 100644 index 0000000..467b002 --- /dev/null +++ b/Wizard/Gears/PreProcGear.cs @@ -0,0 +1,260 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +using System; +using System.Linq; +using System.Text; +using Microsoft.Build.Construction; +using net.r_eg.DllExport.Wizard.Extensions; +using net.r_eg.MvsSln.Core; +using net.r_eg.MvsSln.Log; +using static net.r_eg.DllExport.Wizard.PreProc; + +namespace net.r_eg.DllExport.Wizard.Gears +{ + internal sealed class PreProcGear: IProjectGear + { + private const string ILMERGE_TMP = ".ilm0"; + + private readonly Version incConari = new Version("1.4.0"); + private readonly Version incILMerge = new Version("3.0.29"); + + private readonly IProjectSvc prj; + private readonly ProjectPropertyGroupElement pgroup; + + private IUserConfig Config => prj.Config; + private IXProject XProject => prj.XProject; + private ISender Log => Config.Log; + + public void Install() => CfgPreProc(); + + public void Uninstall(bool hardReset) => RemovePreProcTarget(hardReset); + + public PreProcGear(IProjectSvc prj) + { + this.prj = prj ?? throw new ArgumentNullException(nameof(prj)); + pgroup = XProject.Project.Xml.AddPropertyGroup(); + } + + private void CfgPreProc() + { + CmdType type = Config.PreProc.Type; + + prj.SetProperty(MSBuildProperties.DXP_PRE_PROC_TYPE, (long)type); + Log.send(this, $"Pre-Processing type: {type}"); + + if(type == CmdType.None) { + return; + } + + var sb = new _MixStringBuilder(5); + + if((type & CmdType.Conari) == CmdType.Conari) + { + Log.send(this, $"Integrate Conari: {incConari}"); + XProject.AddPackageIfNotExists("Conari", $"{incConari}"); + sb.AppendBoth("Conari.dll "); + + // .NET Core + sb.AppendCor("Microsoft.CSharp.dll System.Reflection.Emit.dll "); + sb.AppendCor("System.Reflection.Emit.ILGeneration.dll System.Reflection.Emit.Lightweight.dll "); + OverrideCopyLocalLockFileAssemblies(); + } + + if((type & CmdType.ILMerge) == CmdType.ILMerge) + { + prj.SetProperty(MSBuildProperties.DXP_ILMERGE, Config.PreProc.Cmd); + Log.send(this, $"Merge modules via ILMerge {incILMerge}: {Config.PreProc.Cmd}"); + + XProject.AddPackageIfNotExists("ilmerge", $"{incILMerge}"); + sb.AppendBoth($"{Config.PreProc.Cmd} "); + } + + if((type & CmdType.Exec) == CmdType.Exec) + { + Log.send(this, $"Pre-Processing command: {Config.PreProc.Cmd}"); + sb.AppendBoth($"{Config.PreProc.Cmd} "); + } + + AddPreProcTarget( + FormatPreProcCmd(type, sb.Fx), + FormatPreProcCmd(type, sb.Cor), + type + ); + } + + private void AddPreProcTarget(string fxCmd, string corCmd, CmdType type) + { + var target = prj.AddTarget(MSBuildTargets.DXP_PRE_PROC); + + target.BeforeTargets = MSBuildTargets.DXP_MAIN; + target.Label = Project.METALIB_PK_TOKEN; + + var tCopy = target.AddTask("Copy"); + tCopy.SetParameter("SourceFiles", $"$({MSBuildProperties.DXP_METALIB_FPATH})"); + tCopy.SetParameter("DestinationFolder", $"$({MSBuildProperties.PRJ_TARGET_DIR})"); + tCopy.SetParameter("SkipUnchangedFiles", "true"); + tCopy.SetParameter("OverwriteReadOnlyFiles", "true"); + + bool ignoreErr = (type & CmdType.IgnoreErr) == CmdType.IgnoreErr; + ProjectTaskElement tExec; + + if(corCmd != fxCmd) + { + tExec = AddExecTask(target, fxCmd, "'$(IsNetCoreBased)'!='true'", ignoreErr); + AddExecTask(target, corCmd, "'$(IsNetCoreBased)'=='true'", ignoreErr); + } + else + { + tExec = AddExecTask(target, fxCmd, null, ignoreErr); + } + + if((type & CmdType.DebugInfo) == CmdType.DebugInfo) + { + OverrideDebugType(); + AddPreProcAfterTarget(target, tExec); + } + + var tDelete = target.AddTask("Delete"); + tDelete.SetParameter("Files", $"$({MSBuildProperties.PRJ_TARGET_DIR})$({MSBuildProperties.DXP_METALIB_NAME})"); + tDelete.ContinueOnError = "true"; + } + + private void AddPreProcAfterTarget(ProjectTargetElement ppTarget, ProjectTaskElement tExec) + { + var tMove = ppTarget.AddTask("Move"); + tMove.SetParameter("SourceFiles", $"$({MSBuildProperties.PRJ_TARGET_DIR})$({MSBuildProperties.PRJ_TARGET_F}){ILMERGE_TMP}.dll"); + tMove.SetParameter("DestinationFiles", $"$({MSBuildProperties.PRJ_TARGET_DIR})$({MSBuildProperties.PRJ_TARGET_F})"); + tMove.SetParameter("OverwriteReadOnlyFiles", "true"); + tMove.ContinueOnError = tExec.ContinueOnError; + + var target = prj.AddTarget(MSBuildTargets.DXP_PRE_PROC_AFTER); + target.AfterTargets = MSBuildTargets.DXP_MAIN; + target.Label = Project.METALIB_PK_TOKEN; + + var tDelete = target.AddTask("Delete"); + tDelete.SetParameter("Files", $"$({MSBuildProperties.PRJ_TARGET_DIR})$({MSBuildProperties.PRJ_TARGET_F}){ILMERGE_TMP}.pdb"); + tDelete.ContinueOnError = "true"; + } + + private ProjectTaskElement AddExecTask(ProjectTargetElement target, string cmd, string condition, bool continueOnError) + { + var tExec = target.AddTask("Exec"); + tExec.Condition = condition; + tExec.SetParameter("Command", cmd ?? throw new ArgumentNullException(nameof(cmd))); + tExec.SetParameter("WorkingDirectory", $"$({MSBuildProperties.PRJ_TARGET_DIR})"); + tExec.ContinueOnError = continueOnError.ToString().ToLower(); + + return tExec; + } + + private void RemovePreProcTarget(bool hardReset) + { + Log.send(this, $"Trying to remove pre-proc-targets: `{MSBuildTargets.DXP_PRE_PROC}`, `{MSBuildTargets.DXP_PRE_PROC_AFTER}`", Message.Level.Info); + while(prj.RemoveXmlTarget(MSBuildTargets.DXP_PRE_PROC)) { } + while(prj.RemoveXmlTarget(MSBuildTargets.DXP_PRE_PROC_AFTER)) { } + + if(hardReset) + { + while(RemoveCopyLocalLockFileAssemblies()) { } + while(RemoveOverridedDebugType()) { } + } + } + + private string FormatPreProcCmd(CmdType type, StringBuilder sb) + { + string cmd = sb?.ToString().TrimEnd(); + + if((type & CmdType.ILMerge) == CmdType.ILMerge) + { + return $"$(ILMergeConsolePath) {cmd} $(TargetFileName) /out:$(TargetFileName)" + + (((type & CmdType.DebugInfo) == 0) ? " /ndebug" : $"{ILMERGE_TMP}.dll"); + } + return cmd; + } + + private void OverrideCopyLocalLockFileAssemblies() + { + var prop = pgroup.SetProperty(MSBuildProperties.PRJ_CP_LOCKFILE_ASM, "true"); + prop.Condition = "$(TargetFramework.StartsWith('netc')) Or $(TargetFramework.StartsWith('nets'))"; + prop.Label = Project.METALIB_PK_TOKEN; + } + + private bool RemoveCopyLocalLockFileAssemblies() => RemoveLabeledProperty(MSBuildProperties.PRJ_CP_LOCKFILE_ASM); + + private void OverrideDebugType() + { + var prop = pgroup.SetProperty(MSBuildProperties.PRJ_DBG_TYPE, "pdbonly"); + prop.Condition = "'$(DebugType)'!='full'"; + prop.Label = Project.METALIB_PK_TOKEN; + } + + private bool RemoveOverridedDebugType() => RemoveLabeledProperty(MSBuildProperties.PRJ_DBG_TYPE); + + private bool RemoveLabeledProperty(string name) + { + // access to properties without evaluating the condition attribute + ProjectPropertyElement _Get() => XProject.Project.Xml.Properties + .FirstOrDefault(p => p.Name == name && p.Label == Project.METALIB_PK_TOKEN); + + var pp = _Get(); + if(pp?.Parent == null) { + return false; + } + + if(pp.Parent.Children.Count <= 1 && pp.Parent.Parent != null) + { + //TODO: but for sdk-style it still can leave an empty due to msbuild bug + pp.Parent.Parent.RemoveChild(pp.Parent); + } + else + { + pp.Parent.RemoveChild(pp); + } + + return _Get() != null; + } + + private sealed class _MixStringBuilder + { + public StringBuilder Fx { get; private set; } + public StringBuilder Cor { get; private set; } + + public void AppendBoth(string value) + { + AppendFx(value); + AppendCor(value); + } + + public StringBuilder AppendFx(string value) => Fx.Append(value); + public StringBuilder AppendCor(string value) => Cor.Append(value); + + public _MixStringBuilder(int capacity) + { + Fx = new StringBuilder(capacity); + Cor = new StringBuilder(capacity); + } + } + } +} diff --git a/Wizard/IProjectSvc.cs b/Wizard/IProjectSvc.cs new file mode 100644 index 0000000..5446d95 --- /dev/null +++ b/Wizard/IProjectSvc.cs @@ -0,0 +1,43 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +using Microsoft.Build.Construction; + +namespace net.r_eg.DllExport.Wizard +{ + internal interface IProjectSvc: IProject + { + ProjectTargetElement AddTarget(string name); + + bool RemoveXmlTarget(string name); + + void SetProperty(string name, string value); + + void SetProperty(string name, bool val); + + void SetProperty(string name, int val); + + void SetProperty(string name, long val); + } +} diff --git a/Wizard/IUserConfig.cs b/Wizard/IUserConfig.cs index a498567..ac6b083 100644 --- a/Wizard/IUserConfig.cs +++ b/Wizard/IUserConfig.cs @@ -80,6 +80,11 @@ public interface IUserConfig /// CompilerCfg Compiler { get; set; } + /// + /// Access to Pre-Processing. + /// + PreProc PreProc { get; set; } + /// /// Adds to top new namespace into Namespaces property. /// diff --git a/Wizard/MSBuildProperties.cs b/Wizard/MSBuildProperties.cs index b107a57..7f6458c 100644 --- a/Wizard/MSBuildProperties.cs +++ b/Wizard/MSBuildProperties.cs @@ -117,5 +117,30 @@ public struct MSBuildProperties /// Platform for DllExport tool. /// public const string DXP_PLATFORM = "DllExportPlatform"; + + /// + /// Used Pre-Processing type. + /// + public const string DXP_PRE_PROC_TYPE = "DllExportPreProcType"; + + /// + /// List of modules for ILMerge if used. + /// + public const string DXP_ILMERGE = "DllExportILMerge"; + + /// + /// Meta library full path to file. + /// + public const string DXP_METALIB_FPATH = "DllExportMetaLibFullPath"; + + public const string PRJ_TARGET_DIR = "TargetDir"; + + public const string PRJ_TARGET_F = "TargetFileName"; + + public const string PRJ_DBG_TYPE = "DebugType"; + + public const string PRJ_CP_LOCKFILE_ASM = "CopyLocalLockFileAssemblies"; + + public const string SLN_DIR = "SolutionDir"; } } diff --git a/Wizard/MSBuildTargets.cs b/Wizard/MSBuildTargets.cs new file mode 100644 index 0000000..804342f --- /dev/null +++ b/Wizard/MSBuildTargets.cs @@ -0,0 +1,60 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +namespace net.r_eg.DllExport.Wizard +{ + internal struct MSBuildTargets + { + /// + /// The name of target to restore package. + /// + internal const string DXP_PKG_R = "DllExportRestorePkg"; + + /// + /// Pre-Processing. + /// + internal const string DXP_PRE_PROC = "DllExportPreProc"; + + /// + /// Post-Actions of the main Pre-Processing. + /// + internal const string DXP_PRE_PROC_AFTER = "DllExportPreProcAfter"; + + /// + /// To support dynamic `import` section. + /// https://github.com/3F/DllExport/issues/62 + /// + internal const string DXP_R_DYN = "DllExportRPkgDynamicImport"; + + /// + /// The entry point of the main task. + /// + internal const string DXP_MAIN = "DllExportMod"; + + /// + /// Flag when imported the {DXP_MAIN} target. + /// + internal const string DXP_MAIN_FLAG = "DllExportModImported"; + } +} diff --git a/Wizard/PreProc.cs b/Wizard/PreProc.cs new file mode 100644 index 0000000..a11f435 --- /dev/null +++ b/Wizard/PreProc.cs @@ -0,0 +1,68 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +namespace net.r_eg.DllExport.Wizard +{ + public sealed class PreProc + { + /// + /// Never null command. + /// + public string Cmd + { + get; + private set; + } + + public CmdType Type + { + get; + private set; + } + + [System.Flags] + public enum CmdType: long + { + None = 0x0, + + ILMerge = 0x1, + + Conari = 0x2, + + Exec = 0x4, + + DebugInfo = 0x8, + + IgnoreErr = 0x10, + } + + public PreProc Configure(CmdType type = CmdType.None, string cmd = null) + { + Type = type; + Cmd = (type == CmdType.None || cmd == null) ? string.Empty : cmd; + + return this; + } + } +} diff --git a/Wizard/Project.cs b/Wizard/Project.cs index 23af366..dca5e69 100644 --- a/Wizard/Project.cs +++ b/Wizard/Project.cs @@ -27,8 +27,10 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using Microsoft.Build.Construction; using net.r_eg.DllExport.NSBin; using net.r_eg.DllExport.Wizard.Extensions; +using net.r_eg.DllExport.Wizard.Gears; using net.r_eg.MvsSln.Core; using net.r_eg.MvsSln.Extensions; using net.r_eg.MvsSln.Log; @@ -36,7 +38,7 @@ namespace net.r_eg.DllExport.Wizard { - public class Project: IProject + public class Project: IProject, IProjectSvc { public const string DXP_INVALID = "EEE00000-0000-0000-0000-000000000000"; @@ -50,19 +52,10 @@ public class Project: IProject /// public const string DXP_TARGET = "net.r_eg.DllExport.targets"; - /// - /// The name of target to restore package. - /// - protected const string DXP_TARGET_PKG_R = "DllExportRestorePkg"; - - /// - /// To support dynamic `import` section. - /// https://github.com/3F/DllExport/issues/62 - /// - protected const string DXP_TARGET_R_DYN = "DllExportRPkgDynamicImport"; - private const string WZ_ID = "Wz"; + private readonly IEnumerable gears; + /// /// Access to found project. /// @@ -76,18 +69,14 @@ public IXProject XProject /// Installation checking. /// public bool Installed - { - get => InternalError == null && !String.IsNullOrWhiteSpace(GetProperty(MSBuildProperties.DXP_ID)); - } + => InternalError == null + && !string.IsNullOrWhiteSpace(GetProperty(MSBuildProperties.DXP_ID)); /// /// Message if an internal error occurred, otherwise null value. /// TODO: because of DxpIsolatedEnv. See details there. /// - public string InternalError - { - get => XProject?.GetProperty(DxpIsolatedEnv.ERR_MSG, true).evaluatedValue; - } + public string InternalError => XProject?.GetProperty(DxpIsolatedEnv.ERR_MSG, true).evaluatedValue; /// /// Special identifier. Like `ProjectGuid` that is not available in SDK-based projects. @@ -102,7 +91,7 @@ public string DxpIdent } _dxpIdent = GetProperty(MSBuildProperties.DXP_ID); - if(String.IsNullOrWhiteSpace(_dxpIdent)) + if(string.IsNullOrWhiteSpace(_dxpIdent)) { _dxpIdent = Guid.NewGuid().ToString().ToUpperInvariant(); Log.send(this, $"Generated new identifier: '{_dxpIdent}'", Message.Level.Debug); @@ -132,26 +121,17 @@ public string ProjectPath /// /// Full path to root solution directory. /// - public virtual string SlnDir - { - get => XProject?.Sln?.SolutionDir ?? Config?.Wizard?.SlnDir; - } + public virtual string SlnDir => XProject?.Sln?.SolutionDir ?? Config?.Wizard?.SlnDir; /// /// Get defined namespace for project. /// - public string ProjectNamespace - { - get => GetProperty(MSBuildProperties.PRJ_NAMESPACE); - } + public string ProjectNamespace => GetProperty(MSBuildProperties.PRJ_NAMESPACE); /// /// Checks usage of external storage for this project. /// - public bool HasExternalStorage - { - get => XProject?.GetImports(null, Guids.X_EXT_STORAGE).Count() > 0; - } + public bool HasExternalStorage => XProject?.GetImports(null, Guids.X_EXT_STORAGE).Count() > 0; /// /// Active configuration of user data. @@ -218,7 +198,7 @@ public virtual string MetaLib(bool evaluate, bool corlib = false) /// Known identifier of the references. public void Recover(string id) { - if(String.IsNullOrWhiteSpace(id)) { + if(string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException(nameof(id)); } DxpIdent = id; @@ -262,6 +242,17 @@ public virtual bool Configure(ActionType type) return true; } + #region IProjectSvc + + ProjectTargetElement IProjectSvc.AddTarget(string name) => AddTarget(name); + bool IProjectSvc.RemoveXmlTarget(string name) => RemoveXmlTarget(name); + void IProjectSvc.SetProperty(string name, string value) => SetProperty(name, value); + void IProjectSvc.SetProperty(string name, bool val) => SetProperty(name, val); + void IProjectSvc.SetProperty(string name, int val) => SetProperty(name, val); + void IProjectSvc.SetProperty(string name, long val) => SetProperty(name, val); + + #endregion + /// /// public Project(IXProject xproject, IConfigInitializer init) @@ -277,6 +268,10 @@ public Project(IXProject xproject) { XProject = xproject ?? throw new ArgumentNullException(nameof(xproject)); + gears = new[] { + new PreProcGear(this) + }; + AllocateProperties( MSBuildProperties.DXP_ID, MSBuildProperties.DXP_METALIB_NAME, @@ -292,12 +287,17 @@ public Project(IXProject xproject) MSBuildProperties.DXP_TIMEOUT, MSBuildProperties.DXP_PE_CHECK, MSBuildProperties.DXP_PATCHES, - MSBuildProperties.DXP_PLATFORM + MSBuildProperties.DXP_PLATFORM, + MSBuildProperties.DXP_PRE_PROC_TYPE, + MSBuildProperties.DXP_ILMERGE ); Log.send(this, $"Identifier: {DxpIdent}", Message.Level.Info); } + protected void InstallGears() => gears.ForEach(g => g.Install()); + protected void UninstallGears(bool hardReset) => gears.ForEach(g => g.Uninstall(hardReset)); + protected void ActionRestore() { if(Installed) { @@ -361,12 +361,14 @@ protected IUserConfig GetUserConfig(IXProject project, IConfigInitializer cfg) { UseCecil = true, Platform = Platform.Auto, - Compiler = new CompilerCfg() { + Compiler = new CompilerCfg() + { ordinalsBase = 1, timeout = CompilerCfg.TIMEOUT_EXEC, peCheck = PeCheckType.PeIl, patches = PatchesType.None, }, + PreProc = new PreProc().Configure(), }; } @@ -404,7 +406,7 @@ protected void CfgNamespace() CfgDDNS(); SetProperty(MSBuildProperties.DXP_METALIB_NAME, UserConfig.METALIB_NAME); - SetProperty(MSBuildProperties.DXP_NAMESPACE, Config.Namespace ?? String.Empty); + SetProperty(MSBuildProperties.DXP_NAMESPACE, Config.Namespace ?? string.Empty); SetProperty(MSBuildProperties.DXP_DDNS_CECIL, Config.UseCecil); } @@ -484,13 +486,15 @@ protected void CfgCommonData() CfgNamespace(); CfgPlatform(); CfgCompiler(); + InstallGears(); } - protected void Reset(bool properties) + protected void Reset(bool hardReset) { RemoveDllExportLib(); + UninstallGears(hardReset); - if(properties) { + if(hardReset) { RemoveProperties(ConfigProperties.Keys.ToArray()); ConfigProperties.Clear(); } @@ -498,17 +502,9 @@ protected void Reset(bool properties) XProject.Reevaluate(); } - protected bool CmpPublicKeyTokens(string pkToken, string pkTokenAsm) - { - if(pkTokenAsm == null || String.IsNullOrWhiteSpace(pkToken)) { - return false; - } - return pkToken.Equals(pkTokenAsm, StringComparison.InvariantCultureIgnoreCase); - } - protected void AddExternalStorage() { - if(String.IsNullOrWhiteSpace(Config?.Wizard?.StoragePath)) { + if(string.IsNullOrWhiteSpace(Config?.Wizard?.StoragePath)) { throw new ArgumentException($"StoragePath is empty or null.", nameof(Config.Wizard.StoragePath)); } @@ -526,7 +522,7 @@ protected void AddDllExportLib() var lib = MetaLib(false); Log.send(this, $"Add meta library: '{lib}'", Message.Level.Info); - //XProject.AddReference(lib, false); + XProject.AddReference( $"DllExport, PublicKeyToken={METALIB_PK_TOKEN}", MakeBasePath(lib), @@ -547,14 +543,14 @@ protected void AddDllExportLib() } AddRestoreDxp( - DXP_TARGET_PKG_R, - $"'$(DllExportModImported)' != 'true' Or !Exists('{dxpTarget}')", + MSBuildTargets.DXP_PKG_R, + $"'$({MSBuildTargets.DXP_MAIN_FLAG})' != 'true' Or !Exists('{dxpTarget}')", UserConfig.MGR_FILE ); AddDynRestore( - DXP_TARGET_R_DYN, - $"'$(DllExportModImported)' != 'true' And '$(DllExportRPkgDyn)' != 'false'" + MSBuildTargets.DXP_R_DYN, + $"'$({MSBuildTargets.DXP_MAIN_FLAG})' != 'true' And '$(DllExportRPkgDyn)' != 'false'" ); } @@ -563,11 +559,11 @@ protected void AddRestoreDxp(string name, string condition, string manager) var target = AddTarget(name); target.BeforeTargets = "PrepareForBuild"; - var ifManager = $"Exists('$(SolutionDir){manager}')"; + var ifManager = $"Exists('$({MSBuildProperties.SLN_DIR}){manager}')"; var taskMsg = target.AddTask("Error"); taskMsg.Condition = $"!{ifManager}"; - taskMsg.SetParameter("Text", $"{manager} is not found. Path: '$(SolutionDir)' - https://github.com/3F/DllExport"); + taskMsg.SetParameter("Text", $"{manager} is not found. Path: '$({MSBuildProperties.SLN_DIR})' - https://github.com/3F/DllExport"); var taskExec = target.AddTask("Exec"); taskExec.Condition = $"({condition}) And {ifManager}"; @@ -578,10 +574,10 @@ protected void AddRestoreDxp(string name, string condition, string manager) args = args.Replace("%", "%%"); // part of issue 88, probably because of %(_data.FullPath) etc. } else { - args = String.Empty; + args = string.Empty; } taskExec.SetParameter("Command", $".\\{manager} {args} -action Restore"); - taskExec.SetParameter("WorkingDirectory", "$(SolutionDir)"); + taskExec.SetParameter("WorkingDirectory", $"$({MSBuildProperties.SLN_DIR})"); } // https://github.com/3F/DllExport/issues/62#issuecomment-353785676 @@ -601,7 +597,7 @@ protected void AddDynRestore(string name, string condition) taskMsb.SetParameter("Targets", "Build"); } - protected Microsoft.Build.Construction.ProjectTargetElement AddTarget(string name) + protected ProjectTargetElement AddTarget(string name) { Log.send(this, $"Add '{name}' target", Message.Level.Info); return XProject.Project.Xml.AddTarget(name); @@ -615,7 +611,7 @@ protected void RemoveDllExportLib() continue; } - if(PublicKeyTokenLimit && CmpPublicKeyTokens(METALIB_PK_TOKEN, refer.Assembly.PublicKeyToken)) { + if(PublicKeyTokenLimit && METALIB_PK_TOKEN.CmpPublicKeyTokenWith(refer.Assembly.PublicKeyToken)) { Log.send(this, $"Remove old reference pk:'{METALIB_PK_TOKEN}'", Message.Level.Info); XProject.RemoveItem(refer); // immediately modifies collection from XProject.GetReferences continue; @@ -648,11 +644,11 @@ protected void RemoveDllExportLib() Log.send(this, $"Trying to remove old Import elements via pk:'{METALIB_PK_TOKEN}'", Message.Level.Info); while(XProject.RemoveImport(XProject.GetImport(null, METALIB_PK_TOKEN))) { } - Log.send(this, $"Trying to remove old restore-target: '{DXP_TARGET_PKG_R}'", Message.Level.Info); - while(RemoveXmlTarget(DXP_TARGET_PKG_R)) { } + Log.send(this, $"Trying to remove old restore-target: '{MSBuildTargets.DXP_PKG_R}'", Message.Level.Info); + while(RemoveXmlTarget(MSBuildTargets.DXP_PKG_R)) { } - Log.send(this, $"Trying to remove dynamic `import` section: '{DXP_TARGET_R_DYN}'", Message.Level.Info); - while(RemoveXmlTarget(DXP_TARGET_R_DYN)) { } + Log.send(this, $"Trying to remove dynamic `import` section: '{MSBuildTargets.DXP_R_DYN}'", Message.Level.Info); + while(RemoveXmlTarget(MSBuildTargets.DXP_R_DYN)) { } Log.send(this, $"Trying to remove X_EXT_STORAGE Import elements: '{Guids.X_EXT_STORAGE}'", Message.Level.Info); while(XProject.RemoveImport(XProject.GetImport(null, Guids.X_EXT_STORAGE))) { } @@ -672,7 +668,7 @@ protected void RemoveDllExportLib() protected bool RemoveXmlTarget(string name) { - if(String.IsNullOrWhiteSpace(name)) { + if(string.IsNullOrWhiteSpace(name)) { return false; } @@ -688,7 +684,7 @@ protected void RemoveProperties(params string[] names) { foreach(string name in names) { - if(!String.IsNullOrWhiteSpace(name)) { + if(!string.IsNullOrWhiteSpace(name)) { // Log.send(this, $"'{ProjectPath}' Remove old properties: '{name}'", Message.Level.Trace); while(XProject.RemoveProperty(name, true)) { } } @@ -704,7 +700,7 @@ protected void RemoveProperties(params string[] names) /// Will return final added path to project or .targets file. protected string AddImport(string file, bool checking, string id) { - if(String.IsNullOrWhiteSpace(file)) { + if(string.IsNullOrWhiteSpace(file)) { throw new ArgumentNullException(nameof(file)); } @@ -733,16 +729,13 @@ protected string AddImport(string file, bool checking, string id) return targets; } - protected string GetProperty(string name) - { - return XProject?.GetPropertyValue(name); - } + protected string GetProperty(string name) => XProject?.GetPropertyValue(name); protected virtual string MakeBasePath(string path, bool prefix = true) { string ret = SlnDir?.MakeRelativePath(path); if(prefix) { - return $"$(SolutionDir){ret}"; + return $"$({MSBuildProperties.SLN_DIR}){ret}"; } return ret; } @@ -756,16 +749,14 @@ private void AllocateProperties(params string[] names) private void SetProperty(string name, string value) { - if(!String.IsNullOrWhiteSpace(name)) { + if(!string.IsNullOrWhiteSpace(name)) { Log.send(this, $"'{ProjectPath}' Schedule an adding property: '{name}':'{value}' ", Message.Level.Debug); ConfigProperties[name] = value; } } private void SetProperty(string name, bool val) => SetProperty(name, val.ToString().ToLower()); - private void SetProperty(string name, int val) => SetProperty(name, val.ToString()); - private void SetProperty(string name, long val) => SetProperty(name, val.ToString()); private string CopyLib(string src, string dest) diff --git a/Wizard/UI/ConfiguratorForm.Designer.cs b/Wizard/UI/ConfiguratorForm.Designer.cs index 2b3e636..1ee08a1 100644 --- a/Wizard/UI/ConfiguratorForm.Designer.cs +++ b/Wizard/UI/ConfiguratorForm.Designer.cs @@ -19,15 +19,11 @@ protected override void Dispose(bool disposing) fdialog?.Dispose(); icons?.Dispose(); - ctsUpdater?.Cancel(); - - if(tUpdater != null) + if(ctsUpdater != null) { - tUpdater.Wait(); - tUpdater.Dispose(); + ctsUpdater.Cancel(); + ctsUpdater.Dispose(); } - ctsUpdater?.Dispose(); - base.Dispose(disposing); } @@ -59,6 +55,8 @@ private void InitializeComponent() this.tabCtrl = new System.Windows.Forms.TabControl(); this.tabCfgDxp = new System.Windows.Forms.TabPage(); this.projectItems = new net.r_eg.DllExport.Wizard.UI.Controls.ProjectItemsControl(); + this.tabPreProc = new System.Windows.Forms.TabPage(); + this.preProcControl = new net.r_eg.DllExport.Wizard.UI.Controls.PreProcControl(); this.tabData = new System.Windows.Forms.TabPage(); this.labelStorage = new System.Windows.Forms.Label(); this.txtCfgData = new System.Windows.Forms.TextBox(); @@ -83,6 +81,7 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)(this.dgvFilter)).BeginInit(); this.tabCtrl.SuspendLayout(); this.tabCfgDxp.SuspendLayout(); + this.tabPreProc.SuspendLayout(); this.tabData.SuspendLayout(); this.tabUpdating.SuspendLayout(); this.panelUpdVerTop.SuspendLayout(); @@ -204,10 +203,10 @@ private void InitializeComponent() // this.panelPrjs.Controls.Add(this.dgvFilter); this.panelPrjs.Dock = System.Windows.Forms.DockStyle.Fill; - this.panelPrjs.Location = new System.Drawing.Point(0, 26); + this.panelPrjs.Location = new System.Drawing.Point(0, 19); this.panelPrjs.Margin = new System.Windows.Forms.Padding(0); this.panelPrjs.Name = "panelPrjs"; - this.panelPrjs.Size = new System.Drawing.Size(446, 52); + this.panelPrjs.Size = new System.Drawing.Size(446, 59); this.panelPrjs.TabIndex = 2; // // dgvFilter @@ -247,7 +246,7 @@ private void InitializeComponent() this.dgvFilter.RowTemplate.Height = 17; this.dgvFilter.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.dgvFilter.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; - this.dgvFilter.Size = new System.Drawing.Size(446, 52); + this.dgvFilter.Size = new System.Drawing.Size(446, 59); this.dgvFilter.TabIndex = 0; this.dgvFilter.RowEnter += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgvFilter_RowEnter); this.dgvFilter.KeyDown += new System.Windows.Forms.KeyEventHandler(this.dgvFilter_KeyDown); @@ -286,7 +285,7 @@ private void InitializeComponent() this.panelFilter.Location = new System.Drawing.Point(0, 0); this.panelFilter.Margin = new System.Windows.Forms.Padding(0); this.panelFilter.Name = "panelFilter"; - this.panelFilter.Size = new System.Drawing.Size(446, 26); + this.panelFilter.Size = new System.Drawing.Size(446, 19); this.panelFilter.TabIndex = 1; // // tabCtrl @@ -296,6 +295,7 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.tabCtrl.Appearance = System.Windows.Forms.TabAppearance.Buttons; this.tabCtrl.Controls.Add(this.tabCfgDxp); + this.tabCtrl.Controls.Add(this.tabPreProc); this.tabCtrl.Controls.Add(this.tabData); this.tabCtrl.Controls.Add(this.tabUpdating); this.tabCtrl.Controls.Add(this.tabBuildInfo); @@ -334,6 +334,24 @@ private void InitializeComponent() this.projectItems.Size = new System.Drawing.Size(448, 247); this.projectItems.TabIndex = 2; // + // tabPreProc + // + this.tabPreProc.Controls.Add(this.preProcControl); + this.tabPreProc.Location = new System.Drawing.Point(4, 25); + this.tabPreProc.Name = "tabPreProc"; + this.tabPreProc.Size = new System.Drawing.Size(442, 230); + this.tabPreProc.TabIndex = 4; + this.tabPreProc.Text = "Pre-Processing"; + this.tabPreProc.UseVisualStyleBackColor = true; + // + // preProcControl + // + this.preProcControl.Dock = System.Windows.Forms.DockStyle.Fill; + this.preProcControl.Location = new System.Drawing.Point(0, 0); + this.preProcControl.Name = "preProcControl"; + this.preProcControl.Size = new System.Drawing.Size(442, 230); + this.preProcControl.TabIndex = 0; + // // tabData // this.tabData.Controls.Add(this.labelStorage); @@ -542,6 +560,7 @@ private void InitializeComponent() ((System.ComponentModel.ISupportInitialize)(this.dgvFilter)).EndInit(); this.tabCtrl.ResumeLayout(false); this.tabCfgDxp.ResumeLayout(false); + this.tabPreProc.ResumeLayout(false); this.tabData.ResumeLayout(false); this.tabData.PerformLayout(); this.tabUpdating.ResumeLayout(false); @@ -588,5 +607,7 @@ private void InitializeComponent() private System.Windows.Forms.ComboBox comboBoxStorage; private System.Windows.Forms.TextBox txtCfgData; private System.Windows.Forms.Label labelSrcMit; + private System.Windows.Forms.TabPage tabPreProc; + private Controls.PreProcControl preProcControl; } } \ No newline at end of file diff --git a/Wizard/UI/ConfiguratorForm.cs b/Wizard/UI/ConfiguratorForm.cs index a7e7209..fbd4827 100644 --- a/Wizard/UI/ConfiguratorForm.cs +++ b/Wizard/UI/ConfiguratorForm.cs @@ -37,6 +37,7 @@ using net.r_eg.DllExport.Wizard.Extensions; using net.r_eg.DllExport.Wizard.UI.Extensions; using net.r_eg.DllExport.Wizard.UI.Kit; +using net.r_eg.MvsSln.Extensions; using net.r_eg.MvsSln.Log; namespace net.r_eg.DllExport.Wizard.UI @@ -55,7 +56,8 @@ internal sealed partial class ConfiguratorForm: Form, IRender private readonly Caller caller; private readonly PackageInfo pkgVer; private readonly IConfFormater confFormater; - private int prevSlnItemIndex = 0; + private volatile int prevSlnItemIndex = 0; + private volatile int prevPrjIndex = -1; private volatile bool _suspendCbSln; private readonly string updaterInitName; private CancellationTokenSource ctsUpdater; @@ -69,10 +71,7 @@ internal sealed partial class ConfiguratorForm: Form, IRender /// To apply filter for rendered projects. /// /// - public void ApplyFilter(ProjectFilter filter) - { - RenderProjects(exec.ActiveSlnFile, filter); - } + public void ApplyFilter(ProjectFilter filter) => RenderProjects(exec.ActiveSlnFile, filter); public void ShowProgressLine(bool enabled) { @@ -115,23 +114,6 @@ public ConfiguratorForm(IExecutor exec) projectItems.Set(null); // TODO: this only when no projects in solution and only when initial start } - private void ConfiguratorForm_Load(object sender, EventArgs e) - { - TopMost = false; TopMost = true; - - if(!string.IsNullOrEmpty(pkgVer.Activated)) - { - UpdateListOfPackages(); - txtLogUpd.SetData($"{CmdUpdate} ..."); - } - else - { - panelUpdVerTop.Enabled = false; - btnToOnline.Visible = true; - txtLogUpd.SetData("You're using an offline version or such `-dxp-version actual`."); - } - } - private void UpdateListOfPackages() { const int _ANI_DELAY = 550; //ms @@ -413,6 +395,42 @@ void std(object _, DataReceivedEventArgs _e) )); } + private void UpdateRefBefore(IProject prj) + { + preProcControl.Export(prj.Config.PreProc); + //TODO: to change processing of projectItems to this way + } + + private void UpdateRefAfter(IProject prj) + { + //TODO: multiple controls are obsolete now because of new layout in 1.7+ + projectItems.Pause(); + projectItems.Set(prj); + projectItems.Resume(); + + preProcControl.Render(prj.Config.PreProc); + + txtCfgData.Text = confFormater.Parse(prj); + } + + private IProject GetProject(int index) + { + if(index == -1 || index >= dgvFilter.RowCount) { + return null; + } + + string path = dgvFilter.Rows[index].Cells[gcPath.Name].Value.ToString(); + return GetProjects(exec.ActiveSlnFile).FirstOrDefault(p => p.ProjectPath == path); + } + + private void UpdateRefBefore() + { + IProject prevPrj = GetProject(prevPrjIndex); + if(prevPrj != null) { + UpdateRefBefore(prevPrj); + } + } + private void EnableTabsWhenNoSln(bool status) => ((Control)tabCfgDxp).Enabled = status; private string GetBuildInfo() @@ -438,6 +456,23 @@ private string GetBuildInfo() return sb.ToString(); } + private void ConfiguratorForm_Load(object sender, EventArgs e) + { + TopMost = false; TopMost = true; + + if(!string.IsNullOrEmpty(pkgVer.Activated)) + { + UpdateListOfPackages(); + txtLogUpd.SetData($"{CmdUpdate} ..."); + } + else + { + panelUpdVerTop.Enabled = false; + btnToOnline.Visible = true; + txtLogUpd.SetData("You're using an offline version or such `-dxp-version actual`."); + } + } + private void comboBoxSln_SelectedIndexChanged(object sender, EventArgs e) { if(_suspendCbSln) { return; } @@ -456,6 +491,7 @@ private void comboBoxSln_SelectedIndexChanged(object sender, EventArgs e) private void btnApply_Click(object sender, EventArgs e) { exec.TargetsFileIfCfg?.Reset(); + UpdateRefBefore(); if(!SaveProjects(projectItems.Data)) { return; @@ -473,18 +509,14 @@ private void btnApply_Click(object sender, EventArgs e) private void dgvFilter_RowEnter(object sender, DataGridViewCellEventArgs e) { - if(e.RowIndex == -1 || e.RowIndex >= dgvFilter.RowCount) { - return; - } - - string path = dgvFilter.Rows[e.RowIndex].Cells[gcPath.Name].Value.ToString(); - IProject prj = GetProjects(exec.ActiveSlnFile).FirstOrDefault(p => p.ProjectPath == path); + UpdateRefBefore(); - projectItems.Pause(); - projectItems.Set(prj); - projectItems.Resume(); - - txtCfgData.Text = confFormater.Parse(prj); + IProject prj = GetProject(e.RowIndex); + if(prj != null) + { + UpdateRefAfter(prj); + prevPrjIndex = e.RowIndex; + } } private void dgvFilter_KeyDown(object sender, KeyEventArgs e) diff --git a/Wizard/UI/Controls/PreProcControl.Designer.cs b/Wizard/UI/Controls/PreProcControl.Designer.cs new file mode 100644 index 0000000..dd4b1bd --- /dev/null +++ b/Wizard/UI/Controls/PreProcControl.Designer.cs @@ -0,0 +1,196 @@ +namespace net.r_eg.DllExport.Wizard.UI.Controls +{ + partial class PreProcControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.radioPreProcDisabled = new System.Windows.Forms.RadioButton(); + this.linkPreProc = new System.Windows.Forms.LinkLabel(); + this.linkAboutConari = new System.Windows.Forms.LinkLabel(); + this.txtPreProc = new System.Windows.Forms.TextBox(); + this.radioRawExec = new System.Windows.Forms.RadioButton(); + this.radioILMerge = new System.Windows.Forms.RadioButton(); + this.chkMergeConari = new System.Windows.Forms.CheckBox(); + this.chkIgnoreErrors = new System.Windows.Forms.CheckBox(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.chkGenDebugInfo = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // radioPreProcDisabled + // + this.radioPreProcDisabled.AutoSize = true; + this.radioPreProcDisabled.Checked = true; + this.radioPreProcDisabled.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.radioPreProcDisabled.Location = new System.Drawing.Point(373, 51); + this.radioPreProcDisabled.Name = "radioPreProcDisabled"; + this.radioPreProcDisabled.Size = new System.Drawing.Size(65, 17); + this.radioPreProcDisabled.TabIndex = 25; + this.radioPreProcDisabled.TabStop = true; + this.radioPreProcDisabled.Text = "Disabled"; + this.toolTip1.SetToolTip(this.radioPreProcDisabled, "To disable Pre-Processing"); + this.radioPreProcDisabled.UseVisualStyleBackColor = true; + this.radioPreProcDisabled.CheckedChanged += new System.EventHandler(this.RadioPreProcDisabled_CheckedChanged); + // + // linkPreProc + // + this.linkPreProc.AutoSize = true; + this.linkPreProc.Location = new System.Drawing.Point(6, 27); + this.linkPreProc.Name = "linkPreProc"; + this.linkPreProc.Size = new System.Drawing.Size(13, 13); + this.linkPreProc.TabIndex = 24; + this.linkPreProc.TabStop = true; + this.linkPreProc.Text = "?"; + this.linkPreProc.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkPreProc_LinkClicked); + // + // linkAboutConari + // + this.linkAboutConari.AutoSize = true; + this.linkAboutConari.Location = new System.Drawing.Point(88, 5); + this.linkAboutConari.Name = "linkAboutConari"; + this.linkAboutConari.Size = new System.Drawing.Size(37, 13); + this.linkAboutConari.TabIndex = 23; + this.linkAboutConari.TabStop = true; + this.linkAboutConari.Text = "Conari"; + this.toolTip1.SetToolTip(this.linkAboutConari, "Adds Conari inside final module for easy access to unmanaged features"); + this.linkAboutConari.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.LinkAboutConari_LinkClicked); + // + // txtPreProc + // + this.txtPreProc.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtPreProc.BackColor = System.Drawing.SystemColors.Control; + this.txtPreProc.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.txtPreProc.Enabled = false; + this.txtPreProc.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.txtPreProc.Location = new System.Drawing.Point(3, 74); + this.txtPreProc.Multiline = true; + this.txtPreProc.Name = "txtPreProc"; + this.txtPreProc.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtPreProc.Size = new System.Drawing.Size(436, 150); + this.txtPreProc.TabIndex = 22; + // + // radioRawExec + // + this.radioRawExec.AutoSize = true; + this.radioRawExec.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.radioRawExec.Location = new System.Drawing.Point(25, 49); + this.radioRawExec.Name = "radioRawExec"; + this.radioRawExec.Size = new System.Drawing.Size(97, 17); + this.radioRawExec.TabIndex = 20; + this.radioRawExec.Text = "Exec command"; + this.toolTip1.SetToolTip(this.radioRawExec, "Execute command via \""); + this.radioRawExec.UseVisualStyleBackColor = true; + this.radioRawExec.CheckedChanged += new System.EventHandler(this.RadioRawExec_CheckedChanged); + // + // radioILMerge + // + this.radioILMerge.AutoSize = true; + this.radioILMerge.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.radioILMerge.Location = new System.Drawing.Point(25, 27); + this.radioILMerge.Name = "radioILMerge"; + this.radioILMerge.Size = new System.Drawing.Size(155, 17); + this.radioILMerge.TabIndex = 19; + this.radioILMerge.Text = "Merge modules via ILMerge"; + this.toolTip1.SetToolTip(this.radioILMerge, "Separated by a whitespace char: Module1 Module2 ..."); + this.radioILMerge.UseVisualStyleBackColor = true; + this.radioILMerge.CheckedChanged += new System.EventHandler(this.RadioILMerge_CheckedChanged); + // + // chkMergeConari + // + this.chkMergeConari.AutoSize = true; + this.chkMergeConari.FlatStyle = System.Windows.Forms.FlatStyle.Popup; + this.chkMergeConari.Location = new System.Drawing.Point(25, 4); + this.chkMergeConari.Name = "chkMergeConari"; + this.chkMergeConari.Size = new System.Drawing.Size(66, 17); + this.chkMergeConari.TabIndex = 18; + this.chkMergeConari.Text = "Integrate"; + this.toolTip1.SetToolTip(this.chkMergeConari, "Adds Conari inside final module for easy access to unmanaged features"); + this.chkMergeConari.UseVisualStyleBackColor = true; + this.chkMergeConari.CheckedChanged += new System.EventHandler(this.ChkMergeConari_CheckedChanged); + // + // chkIgnoreErrors + // + this.chkIgnoreErrors.AutoSize = true; + this.chkIgnoreErrors.Enabled = false; + this.chkIgnoreErrors.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.chkIgnoreErrors.Location = new System.Drawing.Point(186, 49); + this.chkIgnoreErrors.Name = "chkIgnoreErrors"; + this.chkIgnoreErrors.Size = new System.Drawing.Size(82, 17); + this.chkIgnoreErrors.TabIndex = 26; + this.chkIgnoreErrors.Text = "Ignore errors"; + this.chkIgnoreErrors.UseVisualStyleBackColor = true; + // + // chkGenDebugInfo + // + this.chkGenDebugInfo.AutoSize = true; + this.chkGenDebugInfo.Enabled = false; + this.chkGenDebugInfo.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.chkGenDebugInfo.Location = new System.Drawing.Point(186, 27); + this.chkGenDebugInfo.Name = "chkGenDebugInfo"; + this.chkGenDebugInfo.Size = new System.Drawing.Size(120, 17); + this.chkGenDebugInfo.TabIndex = 27; + this.chkGenDebugInfo.Text = "Generate debug info"; + this.chkGenDebugInfo.UseVisualStyleBackColor = true; + // + // PreProcControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.chkGenDebugInfo); + this.Controls.Add(this.chkIgnoreErrors); + this.Controls.Add(this.radioPreProcDisabled); + this.Controls.Add(this.linkPreProc); + this.Controls.Add(this.linkAboutConari); + this.Controls.Add(this.txtPreProc); + this.Controls.Add(this.radioRawExec); + this.Controls.Add(this.radioILMerge); + this.Controls.Add(this.chkMergeConari); + this.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); + this.Name = "PreProcControl"; + this.Size = new System.Drawing.Size(441, 227); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.RadioButton radioPreProcDisabled; + private System.Windows.Forms.LinkLabel linkPreProc; + private System.Windows.Forms.LinkLabel linkAboutConari; + private System.Windows.Forms.TextBox txtPreProc; + private System.Windows.Forms.RadioButton radioRawExec; + private System.Windows.Forms.RadioButton radioILMerge; + private System.Windows.Forms.CheckBox chkMergeConari; + private System.Windows.Forms.CheckBox chkIgnoreErrors; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.CheckBox chkGenDebugInfo; + } +} diff --git a/Wizard/UI/Controls/PreProcControl.cs b/Wizard/UI/Controls/PreProcControl.cs new file mode 100644 index 0000000..9fa7fdb --- /dev/null +++ b/Wizard/UI/Controls/PreProcControl.cs @@ -0,0 +1,110 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2020 Denis Kuzmin < x-3F@outlook.com > GitHub/3F + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. +*/ + +using System; +using System.Drawing; +using System.Windows.Forms; +using net.r_eg.DllExport.Wizard.Extensions; +using static net.r_eg.DllExport.Wizard.PreProc; + +namespace net.r_eg.DllExport.Wizard.UI.Controls +{ + internal partial class PreProcControl: UserControl + { + public CmdType Type => GetCmdType(); + + public string Cmd => txtPreProc.Text; + + public void Render(PreProc instance) + { + if(instance == null) { + throw new ArgumentNullException(nameof(instance)); + } + + SetCmdType(instance.Type); + txtPreProc.Text = instance.Cmd; + } + + public PreProc Export(PreProc obj) + => (obj ?? throw new ArgumentNullException(nameof(obj))).Configure(Type, Cmd); + + public PreProcControl() => InitializeComponent(); + + private void SetCmdType(CmdType type) + { + if(type == CmdType.None) + { + radioPreProcDisabled.Checked = true; + return; + } + + if((type & CmdType.ILMerge) == CmdType.ILMerge) { radioILMerge.Checked = true; } + if((type & CmdType.Exec) == CmdType.Exec) { radioRawExec.Checked = true; } + + chkMergeConari.Checked = ((type & CmdType.Conari) == CmdType.Conari); + chkIgnoreErrors.Checked = ((type & CmdType.IgnoreErr) == CmdType.IgnoreErr); + chkGenDebugInfo.Checked = ((type & CmdType.DebugInfo) == CmdType.DebugInfo); + } + + private CmdType GetCmdType() + { + CmdType ret = CmdType.None; + + if(radioILMerge.Checked) { ret = CmdType.ILMerge; } + if(radioRawExec.Checked) { ret = CmdType.Exec; } + + if(chkMergeConari.Checked) { ret |= CmdType.Conari; } + if(chkIgnoreErrors.Checked) { ret |= CmdType.IgnoreErr; } + if(chkGenDebugInfo.Checked) { ret |= CmdType.DebugInfo; } + + return ret; + } + + private void SetUIMergeConari(bool disabled) + { + if(disabled) chkMergeConari.Checked = false; + } + + private bool SetUIPreProcCmd(bool disabled) + { + txtPreProc.Enabled = !disabled; + txtPreProc.BackColor = (disabled) ? SystemColors.Control : SystemColors.Window; + + chkIgnoreErrors.Checked = chkIgnoreErrors.Enabled + = !disabled; + return disabled; + } + + private void ChkMergeConari_CheckedChanged(object sender, EventArgs e) + { + if(chkMergeConari.Checked) radioILMerge.Checked = true; + } + + private void RadioPreProcDisabled_CheckedChanged(object sender, EventArgs e) => SetUIMergeConari(SetUIPreProcCmd(radioPreProcDisabled.Checked)); + private void RadioRawExec_CheckedChanged(object sender, EventArgs e) => SetUIMergeConari(radioRawExec.Checked); + private void LinkAboutConari_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) => "https://github.com/3F/DllExport/wiki/Quick-start#when-conari-can-help-you".OpenUrl(); + private void LinkPreProc_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) => "https://github.com/3F/DllExport/issues/40".OpenUrl(); + private void RadioILMerge_CheckedChanged(object sender, EventArgs e) => chkGenDebugInfo.Checked = chkGenDebugInfo.Enabled = radioILMerge.Checked; + } +} diff --git a/Wizard/UI/Controls/PreProcControl.resx b/Wizard/UI/Controls/PreProcControl.resx new file mode 100644 index 0000000..df8339b --- /dev/null +++ b/Wizard/UI/Controls/PreProcControl.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Wizard/UI/Controls/ProjectItemsControl.cs b/Wizard/UI/Controls/ProjectItemsControl.cs index 2411dea..8b5e558 100644 --- a/Wizard/UI/Controls/ProjectItemsControl.cs +++ b/Wizard/UI/Controls/ProjectItemsControl.cs @@ -121,7 +121,7 @@ public void Reset(bool disposing) public IEnumerable GetInactiveInstalled(IEnumerable projects) { return projects?.Where(p => p.Installed && Data.All(d => d.DxpIdent != p.DxpIdent)) - .ForEach(p => p.Config.Install = true); // since we're not using ConfigureProject() + .Each(p => p.Config.Install = true); // since we're not using ConfigureProject() } public ProjectItemsControl() diff --git a/Wizard/UserConfig.cs b/Wizard/UserConfig.cs index 1973f45..6da3785 100644 --- a/Wizard/UserConfig.cs +++ b/Wizard/UserConfig.cs @@ -24,11 +24,13 @@ using System; using System.Collections.Generic; +using System.Linq; using net.r_eg.DllExport.NSBin; using net.r_eg.DllExport.Wizard.Extensions; using net.r_eg.MvsSln.Core; using net.r_eg.MvsSln.Log; using RGiesecke.DllExport; +using static net.r_eg.DllExport.Wizard.PreProc; namespace net.r_eg.DllExport.Wizard { @@ -139,6 +141,15 @@ public CompilerCfg Compiler set; } + /// + /// Access to Pre-Processing. + /// + public PreProc PreProc + { + get; + set; + } + /// /// Adds to top new namespace into Namespaces property. /// @@ -189,6 +200,9 @@ public UserConfig(IWizardConfig cfg, IXProject xp) peCheck = (PeCheckType)GetValue(MSBuildProperties.DXP_PE_CHECK, xp).ToInteger(), patches = (PatchesType)GetValue(MSBuildProperties.DXP_PATCHES, xp).ToLongInteger() }; + + var cmdType = (CmdType)GetValue(MSBuildProperties.DXP_PRE_PROC_TYPE, xp).ToLongInteger(); + PreProc = new PreProc().Configure(cmdType, GetPreProcCmd(cmdType, xp)); } public UserConfig(IConfigInitializer cfg) @@ -241,6 +255,29 @@ protected Platform GetPlatform(string value) return Platform.Default; } + protected string GetPreProcCmd(CmdType type, IXProject xp) + { + if((type & CmdType.ILMerge) == CmdType.ILMerge) + { + return GetUnevaluatedValue(MSBuildProperties.DXP_ILMERGE, xp); + } + + if((type & CmdType.Exec) == CmdType.Exec) + { + var tExec = xp?.Project.Xml?.Targets + .FirstOrDefault(t => t.Name == MSBuildTargets.DXP_PRE_PROC && t.Label == Project.METALIB_PK_TOKEN)? + .Tasks + .FirstOrDefault(t => t.Name == "Exec"); + + if(tExec != null) + { + return tExec.GetParameter("Command"); + } + } + + return null; + } + private string GetValue(string property, IXProject project) { return project?.GetPropertyValue(property); diff --git a/Wizard/Wizard.csproj b/Wizard/Wizard.csproj index 9312827..f0b4277 100644 --- a/Wizard/Wizard.csproj +++ b/Wizard/Wizard.csproj @@ -57,6 +57,8 @@ + + @@ -72,6 +74,9 @@ + + + @@ -89,6 +94,12 @@ Component + + UserControl + + + PreProcControl.cs + True @@ -176,6 +187,9 @@ ConfiguratorForm.cs Designer + + PreProcControl.cs + ResXFileCodeGenerator IconResources.Designer.cs