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