From ca47896204482bf4a6979e3838bf7f09f61cebeb Mon Sep 17 00:00:00 2001
From: giy <giy@omp-system.ru>
Date: Fri, 02 Sep 2022 14:16:56 +0300
Subject: [PATCH] Обновление до версии 2.9.0

---
 QtVsTools.Package/QtMsBuild/QtProjectBuild.cs |  377 +++++++++++++++++++++++++++++++----------------------
 1 files changed, 220 insertions(+), 157 deletions(-)

diff --git a/QtVsTools.Package/QtMsBuild/QtProjectBuild.cs b/QtVsTools.Package/QtMsBuild/QtProjectBuild.cs
index f2fea0b..44023ee 100644
--- a/QtVsTools.Package/QtMsBuild/QtProjectBuild.cs
+++ b/QtVsTools.Package/QtMsBuild/QtProjectBuild.cs
@@ -40,36 +40,42 @@
 using Microsoft.VisualStudio.TaskStatusCenter;
 using Microsoft.VisualStudio.Threading;
 using Microsoft.VisualStudio.VCProjectEngine;
-using EnvDTE;
+
+using Thread = System.Threading.Thread;
 
 namespace QtVsTools.QtMsBuild
 {
+    using Common;
     using Core;
     using VisualStudio;
-    using Thread = System.Threading.Thread;
+    using static Common.EnumExt;
 
     class QtProjectBuild : Concurrent<QtProjectBuild>
     {
-        static PunisherQueue<QtProjectBuild> _BuildQueue;
-        static PunisherQueue<QtProjectBuild> BuildQueue =>
-            StaticThreadSafeInit(() => _BuildQueue,
-                () => _BuildQueue = new PunisherQueue<QtProjectBuild>(
-                    getItemKey: (QtProjectBuild build) =>
-                    {
-                        return build.ConfiguredProject;
-                    })
-                );
+        static LazyFactory StaticLazy { get; } = new LazyFactory();
 
-        static ConcurrentStopwatch _RequestTimer;
-        static ConcurrentStopwatch RequestTimer =>
-            StaticThreadSafeInit(() => _RequestTimer, () => _RequestTimer = new ConcurrentStopwatch());
+        public enum Target
+        {
+            // Mark project as dirty, but do not request a build
+            [String("QtVsTools.QtMsBuild.QtProjectBuild.Target.SetOutdated")] SetOutdated
+        }
 
-        static IVsTaskStatusCenterService _StatusCenter;
-        static IVsTaskStatusCenterService StatusCenter => StaticThreadSafeInit(() => _StatusCenter,
-                () => _StatusCenter = VsServiceProvider
-                    .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
+        static PunisherQueue<QtProjectBuild> BuildQueue => StaticLazy.Get(() =>
+            BuildQueue, () => new PunisherQueue<QtProjectBuild>(
+                getItemKey: (QtProjectBuild build) =>
+                {
+                    return build.ConfiguredProject;
+                }));
+
+        static ConcurrentStopwatch RequestTimer => StaticLazy.Get(() =>
+            RequestTimer, () => new ConcurrentStopwatch());
+
+        static IVsTaskStatusCenterService StatusCenter => StaticLazy.Get(() =>
+            StatusCenter, () => VsServiceProvider
+                .GetService<SVsTaskStatusCenterService, IVsTaskStatusCenterService>());
 
         EnvDTE.Project Project { get; set; }
+        VCProject VcProject { get; set; }
         UnconfiguredProject UnconfiguredProject { get; set; }
         ConfiguredProject ConfiguredProject { get; set; }
         Dictionary<string, string> Properties { get; set; }
@@ -80,6 +86,7 @@
 
         public static void StartBuild(
             EnvDTE.Project project,
+            string projectPath,
             string configName,
             Dictionary<string, string> properties,
             IEnumerable<string> targets,
@@ -90,11 +97,13 @@
             if (configName == null)
                 throw new ArgumentException("Configuration name cannot be null.");
 
-            Task.Run(() => StartBuildAsync(project, configName, properties, targets, verbosity));
+            _ = Task.Run(() => StartBuildAsync(
+                project, projectPath, configName, properties, targets, verbosity));
         }
 
         public static async Task StartBuildAsync(
             EnvDTE.Project project,
+            string projectPath,
             string configName,
             Dictionary<string, string> properties,
             IEnumerable<string> targets,
@@ -106,15 +115,15 @@
                 throw new ArgumentException("Configuration name cannot be null.");
 
             RequestTimer.Restart();
+            var tracker = QtProjectTracker.Get(project, projectPath);
+            await tracker.Initialized;
+
             if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
                 Messages.Print(string.Format(
                 "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Request [{2}] {3}",
                 DateTime.Now, Thread.CurrentThread.ManagedThreadId,
-                configName, project.FullName));
+                configName, tracker.UnconfiguredProject.FullPath));
             }
-
-            var tracker = QtProjectTracker.Get(project);
-            await tracker.Initialized;
 
             var knownConfigs = await tracker.UnconfiguredProject.Services
                 .ProjectConfigurationsService.GetKnownProjectConfigurationsAsync();
@@ -134,6 +143,7 @@
             BuildQueue.Enqueue(new QtProjectBuild()
             {
                 Project = project,
+                VcProject = tracker.VcProject,
                 UnconfiguredProject = tracker.UnconfiguredProject,
                 ConfiguredProject = configuredProject,
                 Properties = properties?.ToDictionary(x => x.Key, x => x.Value),
@@ -143,6 +153,35 @@
             StaticThreadSafeInit(() => BuildDispatcher,
                 () => BuildDispatcher = Task.Run(BuildDispatcherLoopAsync))
                 .Forget();
+        }
+
+        public static void SetOutdated(
+            EnvDTE.Project project,
+            string projectPath,
+            string configName,
+            LoggerVerbosity verbosity = LoggerVerbosity.Quiet)
+        {
+            if (project == null)
+                throw new ArgumentException("Project cannot be null.");
+            if (configName == null)
+                throw new ArgumentException("Configuration name cannot be null.");
+
+            _ = Task.Run(() => SetOutdatedAsync(project, projectPath, configName, verbosity));
+        }
+
+        public static async Task SetOutdatedAsync(
+            EnvDTE.Project project,
+            string projectPath,
+            string configName,
+            LoggerVerbosity verbosity = LoggerVerbosity.Quiet)
+        {
+            await StartBuildAsync(
+                project,
+                projectPath,
+                configName,
+                null,
+                new[] { Target.SetOutdated.Cast<string>() },
+                verbosity);
         }
 
         public static void Reset()
@@ -161,8 +200,7 @@
                     }
                     await Task.Delay(100);
                 }
-                QtProjectBuild buildRequest;
-                if (BuildQueue.TryDequeue(out buildRequest)) {
+                if (BuildQueue.TryDequeue(out QtProjectBuild buildRequest)) {
                     if (dispatchStatus == null) {
                         dispatchStatus = StatusCenter.PreRegister(
                             new TaskHandlerOptions
@@ -202,6 +240,140 @@
             }
         }
 
+        async Task<bool> BuildProjectAsync(ProjectWriteLockReleaser writeAccess)
+        {
+            var msBuildProject = await writeAccess.GetProjectAsync(ConfiguredProject);
+
+            if (Targets.Any(t => t == Target.SetOutdated.Cast<string>())) {
+                msBuildProject.MarkDirty();
+                await writeAccess.ReleaseAsync();
+                return true;
+            }
+
+            var solutionPath = QtProjectTracker.SolutionPath;
+            var configProps = new Dictionary<string, string>(
+                ConfiguredProject.ProjectConfiguration.Dimensions.ToImmutableDictionary())
+                {
+                    { "SolutionPath", solutionPath },
+                    { "SolutionFileName", Path.GetFileName(solutionPath) },
+                    { "SolutionName", Path.GetFileNameWithoutExtension(solutionPath) },
+                    { "SolutionExt", Path.GetExtension(solutionPath) },
+                    { "SolutionDir", Path.GetDirectoryName(solutionPath).TrimEnd('\\') + '\\'  }
+                };
+
+            foreach (var property in Properties)
+                configProps[property.Key] = property.Value;
+
+            var projectInstance = new ProjectInstance(msBuildProject.Xml,
+                configProps, null, new ProjectCollection());
+
+            var loggerVerbosity = LoggerVerbosity;
+            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation)
+                loggerVerbosity = QtVsToolsPackage.Instance.Options.BuildLoggerVerbosity;
+            var buildParams = new BuildParameters()
+            {
+                Loggers = (loggerVerbosity != LoggerVerbosity.Quiet)
+                        ? new[] { new QtProjectLogger() { Verbosity = loggerVerbosity } }
+                        : null
+            };
+
+            var buildRequest = new BuildRequestData(projectInstance,
+                Targets.ToArray(),
+                hostServices: null,
+                flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild);
+
+            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
+                Messages.Print(string.Format(
+                    "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Build [{2}] {3}",
+                    DateTime.Now, Thread.CurrentThread.ManagedThreadId,
+                    ConfiguredProject.ProjectConfiguration.Name,
+                    UnconfiguredProject.FullPath));
+                Messages.Print("=== Targets");
+                foreach (var target in buildRequest.TargetNames)
+                    Messages.Print(string.Format("    {0}", target));
+                Messages.Print("=== Properties");
+                foreach (var property in Properties) {
+                    Messages.Print(string.Format("    {0}={1}",
+                        property.Key, property.Value));
+                }
+            }
+
+            BuildResult result = null;
+            while (result == null) {
+                try {
+                    result = BuildManager.DefaultBuildManager.Build(
+                        buildParams, buildRequest);
+                } catch (InvalidOperationException) {
+                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
+                        Messages.Print(string.Format(
+                            "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] "
+                            + "Warning: Another build is in progress; waiting...",
+                            DateTime.Now,
+                            Thread.CurrentThread.ManagedThreadId,
+                            ConfiguredProject.ProjectConfiguration.Name));
+                    }
+                    await Task.Delay(3000);
+                }
+            }
+
+            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
+                string resMsg;
+                StringBuilder resInfo = new StringBuilder();
+                if (result?.OverallResult == BuildResultCode.Success) {
+                    resMsg = "Build ok";
+                } else {
+                    resMsg = "Build FAIL";
+                    if (result == null) {
+                        resInfo.AppendLine("####### Build returned 'null'");
+                    } else {
+                        resInfo.AppendLine("####### Build returned 'Failure' code");
+                        if (result.ResultsByTarget != null) {
+                            foreach (var tr in result.ResultsByTarget) {
+                                var res = tr.Value;
+                                if (res.ResultCode != TargetResultCode.Failure)
+                                    continue;
+                                resInfo.AppendFormat("### Target '{0}' FAIL\r\n", tr.Key);
+                                if (res.Items != null && res.Items.Length > 0) {
+                                    resInfo.AppendFormat(
+                                        "Items: {0}\r\n", string.Join(", ", res.Items
+                                            .Select(it => it.ItemSpec)));
+                                }
+                                var e = tr.Value?.Exception;
+                                if (e != null) {
+                                    resInfo.AppendFormat(
+                                        "Exception: {0}\r\nStacktrace:\r\n{1}\r\n",
+                                        e.Message, e.StackTrace);
+                                }
+                            }
+                        }
+                    }
+                }
+                Messages.Print(string.Format(
+                    "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] {3}\r\n{4}",
+                    DateTime.Now, Thread.CurrentThread.ManagedThreadId,
+                    ConfiguredProject.ProjectConfiguration.Name,
+                    resMsg, resInfo.ToString()));
+            }
+
+            bool ok = false;
+            if (result == null
+                || result.ResultsByTarget == null
+                || result.OverallResult != BuildResultCode.Success) {
+                Messages.Print(string.Format("{0}: background build FAILED!",
+                        Path.GetFileName(UnconfiguredProject.FullPath)));
+            } else {
+                var checkResults = result.ResultsByTarget
+                    .Where(x => Targets.Contains(x.Key))
+                    .Select(x => x.Value);
+                ok = checkResults.Any()
+                    && checkResults.All(x => x.ResultCode == TargetResultCode.Success);
+                if (ok)
+                    msBuildProject.MarkDirty();
+            }
+            await writeAccess.ReleaseAsync();
+            return ok;
+        }
+
         async Task BuildAsync()
         {
             if (LoggerVerbosity != LoggerVerbosity.Quiet) {
@@ -211,7 +383,7 @@
   * Properties: {1}
   * Targets: {2}
 ",
-                    /*{0}*/ Project.Name,
+                    /*{0}*/ Path.GetFileNameWithoutExtension(UnconfiguredProject.FullPath),
                     /*{1}*/ string.Join("", Properties
                         .Select(property => string.Format(@"
         {0} = {1}",     /*{0}*/ property.Key, /*{1}*/ property.Value))),
@@ -222,151 +394,41 @@
 
             bool ok = false;
             try {
-                ProjectWriteLockReleaser writeAccess;
                 var timer = ConcurrentStopwatch.StartNew();
                 while (timer.IsRunning) {
                     try {
-                        writeAccess = await lockService.WriteLockAsync();
+#if VS2017
+                        using (var writeAccess = await lockService.WriteLockAsync())
+                            ok = await BuildProjectAsync(writeAccess);
+#else
+                        await lockService.WriteLockAsync(
+                            async (ProjectWriteLockReleaser writeAccess) =>
+                            {
+                                ok = await BuildProjectAsync(writeAccess);
+                            });
+#endif
                         timer.Stop();
                     } catch (InvalidOperationException) {
                         if (timer.ElapsedMilliseconds >= 5000)
                             throw;
+#if VS2017
                         using (var readAccess = await lockService.ReadLockAsync())
                             await readAccess.ReleaseAsync();
+#else
+                        await lockService.ReadLockAsync(
+                            async (ProjectLockReleaser readAccess) =>
+                            {
+                                await readAccess.ReleaseAsync();
+                            });
+#endif
                     }
-                }
-
-                using (writeAccess) {
-                    var msBuildProject = await writeAccess.GetProjectAsync(ConfiguredProject);
-
-                    var solutionPath = QtProjectTracker.SolutionPath;
-                    var configProps = new Dictionary<string, string>(
-                        ConfiguredProject.ProjectConfiguration.Dimensions.ToImmutableDictionary())
-                    {
-                        { "SolutionPath", solutionPath },
-                        { "SolutionFileName", Path.GetFileName(solutionPath) },
-                        { "SolutionName", Path.GetFileNameWithoutExtension(solutionPath) },
-                        { "SolutionExt", Path.GetExtension(solutionPath) },
-                        { "SolutionDir", Path.GetDirectoryName(solutionPath).TrimEnd('\\') + '\\'  }
-                    };
-
-                    foreach (var property in Properties)
-                        configProps[property.Key] = property.Value;
-
-                    var projectInstance = new ProjectInstance(msBuildProject.Xml,
-                        configProps, null, new ProjectCollection());
-
-                    var loggerVerbosity = LoggerVerbosity;
-                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation)
-                        loggerVerbosity = QtVsToolsPackage.Instance.Options.BuildLoggerVerbosity;
-                    var buildParams = new BuildParameters()
-                    {
-                        Loggers = (loggerVerbosity != LoggerVerbosity.Quiet)
-                                ? new[] { new QtProjectLogger() { Verbosity = loggerVerbosity } }
-                                : null
-                    };
-
-                    var buildRequest = new BuildRequestData(projectInstance,
-                        Targets.ToArray(),
-                        hostServices: null,
-                        flags: BuildRequestDataFlags.ProvideProjectStateAfterBuild);
-
-                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
-                        Messages.Print(string.Format(
-                            "{0:HH:mm:ss.FFF} QtProjectBuild({1}): Build [{2}] {3}",
-                            DateTime.Now, Thread.CurrentThread.ManagedThreadId,
-                            ConfiguredProject.ProjectConfiguration.Name,
-                            UnconfiguredProject.FullPath));
-                        Messages.Print("=== Targets");
-                        foreach (var target in buildRequest.TargetNames)
-                            Messages.Print(string.Format("    {0}", target));
-                        Messages.Print("=== Properties");
-                        foreach (var property in Properties) {
-                            Messages.Print(string.Format("    {0}={1}",
-                                property.Key, property.Value));
-                        }
-                    }
-
-                    BuildResult result = null;
-                    while (result == null) {
-                        try {
-                            result = BuildManager.DefaultBuildManager.Build(
-                                buildParams, buildRequest);
-                        } catch (InvalidOperationException) {
-                            if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
-                                Messages.Print(string.Format(
-                                    "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] "
-                                    + "Warning: Another build is in progress; waiting...",
-                                    DateTime.Now,
-                                    Thread.CurrentThread.ManagedThreadId,
-                                    ConfiguredProject.ProjectConfiguration.Name));
-                            }
-                            await Task.Delay(3000);
-                        }
-                    }
-
-                    if (QtVsToolsPackage.Instance.Options.BuildDebugInformation) {
-                        string resMsg;
-                        StringBuilder resInfo = new StringBuilder();
-                        if (result?.OverallResult == BuildResultCode.Success) {
-                            resMsg = "Build ok";
-                        } else {
-                            resMsg = "Build FAIL";
-                            if (result == null) {
-                                resInfo.AppendLine("####### Build returned 'null'");
-                            } else {
-                                resInfo.AppendLine("####### Build returned 'Failure' code");
-                                if (result.ResultsByTarget != null) {
-                                    foreach (var tr in result.ResultsByTarget) {
-                                        var res = tr.Value;
-                                        if (res.ResultCode != TargetResultCode.Failure)
-                                            continue;
-                                        resInfo.AppendFormat("### Target '{0}' FAIL\r\n", tr.Key);
-                                        if (res.Items != null && res.Items.Length > 0) {
-                                            resInfo.AppendFormat(
-                                                "Items: {0}\r\n", string.Join(", ", res.Items
-                                                    .Select(it => it.ItemSpec)));
-                                        }
-                                        var e = tr.Value?.Exception;
-                                        if (e != null) {
-                                            resInfo.AppendFormat(
-                                                "Exception: {0}\r\nStacktrace:\r\n{1}\r\n",
-                                                e.Message, e.StackTrace);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        Messages.Print(string.Format(
-                            "{0:HH:mm:ss.FFF} QtProjectBuild({1}): [{2}] {3}\r\n{4}",
-                            DateTime.Now, Thread.CurrentThread.ManagedThreadId,
-                            ConfiguredProject.ProjectConfiguration.Name,
-                            resMsg, resInfo.ToString()));
-                    }
-
-                    if (result == null
-                        || result.ResultsByTarget == null
-                        || result.OverallResult != BuildResultCode.Success) {
-                        Messages.Print(string.Format("{0}: background build FAILED!",
-                                Path.GetFileName(UnconfiguredProject.FullPath)));
-                    } else {
-                        var checkResults = result.ResultsByTarget
-                            .Where(x => Targets.Contains(x.Key))
-                            .Select(x => x.Value);
-                        ok = checkResults.Any()
-                            && checkResults.All(x => x.ResultCode == TargetResultCode.Success);
-                        if (ok)
-                            msBuildProject.MarkDirty();
-                    }
-                    await writeAccess.ReleaseAsync();
                 }
 
                 if (ok) {
-                    var vcProject = Project.Object as VCProject;
-                    var vcConfigs = vcProject.Configurations as IVCCollection;
+                    var vcConfigs = VcProject.Configurations as IVCCollection;
                     var vcConfig = vcConfigs.Item(ConfiguredProject.ProjectConfiguration.Name) as VCConfiguration;
                     var props = vcConfig.Rules.Item("QtRule10_Settings") as IVCRulePropertyStorage;
-                    props.SetPropertyValue("QtLastBackgroundBuild", DateTime.UtcNow.ToString("o"));
+                    props?.SetPropertyValue("QtLastBackgroundBuild", DateTime.UtcNow.ToString("o"));
                 }
             } catch (Exception e) {
                 Messages.Print(string.Format("{0}: background build ERROR: {1}",
@@ -377,7 +439,8 @@
                 Messages.Print(string.Format(
 @"
 == {0}: build {1}",
-                    Project.Name, ok ? "successful" : "ERROR"));
+                    Path.GetFileNameWithoutExtension(UnconfiguredProject.FullPath),
+                    ok ? "successful" : "ERROR"));
             }
         }
     }

--
Gitblit v1.9.1