无阻塞的MSBuild exec任务

无阻塞的MSBuild exec任务,msbuild,Msbuild,是否有人碰巧知道阻止此MSBuild任务的诀窍?我真的希望浏览器打开,构建脚本继续运行。当前,它在Exec任务中阻塞,直到资源管理器窗口关闭 <Target Name="OpenExplorer"> <Exec Command='explorer.exe "$(DestinationDir)"' IgnoreExitCode="true" /> </Target> 谢谢 编辑:我希望避免为此创建自定义任务。可能存在一些命令行魔术,可以为命令?内联

是否有人碰巧知道阻止此MSBuild任务的诀窍?我真的希望浏览器打开,构建脚本继续运行。当前,它在Exec任务中阻塞,直到资源管理器窗口关闭

<Target Name="OpenExplorer">
    <Exec Command='explorer.exe "$(DestinationDir)"' IgnoreExitCode="true" />
</Target>

谢谢


编辑:我希望避免为此创建自定义任务。可能存在一些命令行魔术,可以为
命令

内联放置。您不能使用本机
Exec
。但是您可以编写自己的异步激发,如中所述:

  public class AsyncExec : Exec {
    protected override int ExecuteTool(string pathToTool,
                                       string responseFileCommands,
                                       string commandLineCommands) {
      Process process = new Process();
      process.StartInfo = GetProcessStartInfo(pathToTool, commandLineCommands);
      process.Start();
      return 0;
    }

    protected virtual ProcessStartInfo GetProcessStartInfo(string executable,
                                                           string arguments) {
      if (arguments.Length > 0x7d00) {
        this.Log.LogWarningWithCodeFromResources("ToolTask.CommandTooLong", new object[] { base.GetType().Name });
      }
      ProcessStartInfo startInfo = new ProcessStartInfo(executable, arguments);
      startInfo.WindowStyle = ProcessWindowStyle.Hidden;
      startInfo.CreateNoWindow = true;
      startInfo.UseShellExecute = true;
      string workingDirectory = this.GetWorkingDirectory();
      if (workingDirectory != null) {
        startInfo.WorkingDirectory = workingDirectory;
      }
      StringDictionary environmentOverride = this.EnvironmentOverride;
      if (environmentOverride != null) {
        foreach (DictionaryEntry entry in environmentOverride) {
          startInfo.EnvironmentVariables.Remove(entry.Key.ToString());
          startInfo.EnvironmentVariables.Add(entry.Key.ToString(), entry.Value.ToString());
        }
      }
      return startInfo;
    }
  }
然后您可以使用它运行:

<AsyncExec Command="..." />

在回答时



Exec中的命令放置在批处理文件中并执行。因此,您可以在命令中使用“start”关键字,就像在控制台窗口中一样。这就可以了。

在中尝试AsyncExec。

这里是一种只使用msbuild和内联任务异步执行进程的简单方法。这仅适用于MSBuild V4.0及以上版本(上帝保佑MSBuild的家伙们添加了此功能!)。您不需要任何外部扩展包

实际上,我们正在使用上面建议的代码并将其放入一个内联任务中。可以随意修改代码以满足您的需要

此解决方案的要点是,它使您能够实现结果,而无需为自定义任务创建单独的dll。扩展包中的实现肯定更加可靠,但这是一种快速解决此问题的方法。您还可以精确地定制它的运行方式

  <!--Launch a Process in Parallel-->
  <UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <!--The file path is the full path to the executable file to run-->
      <FilePath ParameterType="System.String" Required="true" />
      <!--The arguments should contain all the command line arguments that need to be sent to the application-->
      <Arguments ParameterType="System.String" Required="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs">
        <![CDATA[
  string name = System.IO.Path.GetFileNameWithoutExtension(FilePath);
  Log.LogMessage("Starting {0}...", name);        
  System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(FilePath, Arguments);
  processStartInfo.UseShellExecute = true;
  System.Diagnostics.Process.Start(processStartInfo);
  Log.LogMessage("Finished running process {0}.", name);
  ]]>
      </Code>
    </Task>
  </UsingTask>

)
  • 接下来,我们开始我们的计划,我们想收集覆盖范围
  • 完成后,我们关闭覆盖工具

  • 如果这是我唯一的选择,那答案就太好了。但希望避免它。请注意,您可以将此代码内联,而无需使用MSBuild 4.0内联任务()编译单独的DLL(编辑:忽略我,我看到Luke在另一个答案中提到了这一点!)根据我的经验,这似乎不起作用。我怀疑任务在某种程度上阻止了它。如果不希望任务超时时发出错误信号,还应设置ContinueOnError=“true”。@LewisJubb您能进一步解释一下吗?对我来说,这只是一个警告,似乎没有任何效果。您使用的是哪个版本的MS build?当超时发生时,进程似乎被终止。这可能不是您想要的。我对
    GetFileNameWithoutExtension()
    有问题,所以我删除了日志行,它工作得很好。它甚至可以正常工作,因为
    这是一个极好的答案。请注意,对于.NET核心项目,您需要调整任务工厂和程序集文件,如下所示:
    似乎已移动到此处:
      <!--Launch a Process in Parallel-->
      <UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
        <ParameterGroup>
          <!--The file path is the full path to the executable file to run-->
          <FilePath ParameterType="System.String" Required="true" />
          <!--The arguments should contain all the command line arguments that need to be sent to the application-->
          <Arguments ParameterType="System.String" Required="true" />
        </ParameterGroup>
        <Task>
          <Code Type="Fragment" Language="cs">
            <![CDATA[
      string name = System.IO.Path.GetFileNameWithoutExtension(FilePath);
      Log.LogMessage("Starting {0}...", name);        
      System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(FilePath, Arguments);
      processStartInfo.UseShellExecute = true;
      System.Diagnostics.Process.Start(processStartInfo);
      Log.LogMessage("Finished running process {0}.", name);
      ]]>
          </Code>
        </Task>
      </UsingTask>
    
    <!--Start listening for coverage data:-->
    <Message Text="Starting to listen for coverage..."/>
    <ExecAsync FilePath='$(VSPerfCmdExePath)' Arguments='/start:coverage /output:"$(CoverageFilePath)"' ContinueOnError='true'/>
    <Message Text="Listening for coverage..."/>
    
    <!--Start App with Coverage:-->
    <Message Text="Starting App..."/>
    <Exec Command='"$(AppCoverageLatestExePath)"' ContinueOnError='true' WorkingDirectory='$(AppCoverageLatestFolder)'/>
    <Message Text="App shut down."/>
    
    <!--Stop gathering coverage results:-->
    <Message Text="Stopping listening for coverage..."/>
    <Exec Command='"$(VSPerfCmdExePath)" /shutdown'/>
    <Message Text="Coverage shut down."/>