|  |  | 
 |  |  | 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; }
 | 
 |  |  | 
 |  |  | 
 | 
 |  |  |         public static void StartBuild(
 | 
 |  |  |             EnvDTE.Project project,
 | 
 |  |  |             string projectPath,
 | 
 |  |  |             string configName,
 | 
 |  |  |             Dictionary<string, string> properties,
 | 
 |  |  |             IEnumerable<string> targets,
 | 
 |  |  | 
 |  |  |             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,
 | 
 |  |  | 
 |  |  |                 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();
 | 
 |  |  | 
 |  |  |             BuildQueue.Enqueue(new QtProjectBuild()
 | 
 |  |  |             {
 | 
 |  |  |                 Project = project,
 | 
 |  |  |                 VcProject = tracker.VcProject,
 | 
 |  |  |                 UnconfiguredProject = tracker.UnconfiguredProject,
 | 
 |  |  |                 ConfiguredProject = configuredProject,
 | 
 |  |  |                 Properties = properties?.ToDictionary(x => x.Key, x => x.Value),
 | 
 |  |  | 
 |  |  |             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()
 | 
 |  |  | 
 |  |  |                     }
 | 
 |  |  |                     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
 | 
 |  |  | 
 |  |  |             }
 | 
 |  |  |         }
 | 
 |  |  | 
 | 
 |  |  |         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) {
 | 
 |  |  | 
 |  |  |   * 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))),
 | 
 |  |  | 
 |  |  | 
 | 
 |  |  |             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}",
 | 
 |  |  | 
 |  |  |                 Messages.Print(string.Format(
 | 
 |  |  | @"
 | 
 |  |  | == {0}: build {1}",
 | 
 |  |  |                     Project.Name, ok ? "successful" : "ERROR"));
 | 
 |  |  |                     Path.GetFileNameWithoutExtension(UnconfiguredProject.FullPath),
 | 
 |  |  |                     ok ? "successful" : "ERROR"));
 | 
 |  |  |             }
 | 
 |  |  |         }
 | 
 |  |  |     }
 |