使用 robocopy 与 Visual Studio 2010 的后期生成和预期生成事件

37

Robocopy在成功完成时会输出1,而大多数程序在成功时会以0退出。Visual Studio(和MSBUILD)将1的退出代码解释为错误。

如何在Visual Studio的后期和前期构建事件中使用Robocopy,以便构建环境可以正确识别其失败和成功呢?

注意:这更或多或少是此帖子的转载。

7个回答

31

按照请求,我进行了翻译。根据Asaf的解决方案,并加上skrebbel的评论。

您可以将检查简化为:

robocopy <opt> <src> <tgt>
if %errorlevel% leq 1 exit 0 else exit %errorlevel%

如评论中所言,您可能需要调整“1”的值:这取决于您的操作应该将什么视为错误。请参考组合成robocopy返回数字的位的含义

0×10 严重错误。Robocopy未复制任何文件。这可能是使用错误或源或目标目录上权限不足引起的错误。

0×08 一些文件或目录无法复制(发生复制错误并超过重试限制)。进一步检查这些错误。

0×04 检测到一些不匹配的文件或目录。检查输出日志。可能需要进行清理工作。

0×02 检测到一些额外的文件或目录。检查输出日志。可能需要进行一些清理工作。

0×01 一个或多个文件已成功复制(也就是说,新文件已到达)。

0×00 没有发生错误,并且没有进行任何复制。源和目标目录树完全同步。


19

假设 <src> 为拷贝源,<tgt> 为目标,<opt> 为 robocopy 选项:

robocopy <opt> <src> <tgt>
set rce=%errorlevel%
if not %rce%==1 exit %rce% else exit 0

比如,如果我们想将项目目标复制到 c:\temp,不进行重试并且保留所有子目录(无论是否为空),我们可以使用以下代码:

robocopy /R:0 /E $(TargetDir) c:\temp
set rce=%errorlevel%
if not %rce%==1 exit %rce% else exit 0

9
您可以直接使用 if errorlevel 1 exit 0 else exit %errorlevel% - mafu
1
@mafutrct:太棒了!如果您将您的评论作为答案添加,我会点赞它。 - Valamas
5
不!"@mafutrct No! if errorlevel 1 ..."的意思是"如果错误等于或大于1"。换句话说,这可以捕获所有错误,但不能捕获“不需要复制”(robocopy的0退出代码)。但我认为您需要使用if %errorlevel% leq 1 exit 0 else exit %errorlevel% - skrebbel
实际上,只有大于7(8及以上)的值表示失败,请参见我下面的答案。 - Ohad Schneider

11

仅仅检查退出代码为1是不正确的,因为任何小于8的退出代码都是非错误的:

大于8的任何值表示在复制操作期间至少有一个失败。

(仅澄清一下,退出代码为8也是一个错误:多个文件未能成功复制

正确的代码应该像这样:

IF %ERRORLEVEL% GEQ 8 exit 1
exit 0

5

在语法上,以下是一种逐条运行指令的格式,可以直接在PreBuild步骤中使用:

(robocopy "$(ProjectDir)..\Dir1" "$(ProjectDir)Dir1" "Match.*" /a+:R) ^& IF %ERRORLEVEL% GEQ 8 exit 1
(robocopy "$(ProjectDir)..\Dir2" "$(ProjectDir)Dir2" "Match.*" /a+:R) ^& IF %ERRORLEVEL% GEQ 8 exit 1
exit 0

参考文献:


4

MSBuild extensionpack包含一个Robocopy任务,您可以在构建过程中使用它。
这是否可以成为您替代VS预/后生成事件的解决方案?

如果是这样,您可以通过覆盖BeforeBuild、AfterBuild目标并调用Robocopy任务(如果其他目标更适合您的需求,则也可以覆盖其他目标,请参见链接的MSDN页面中的列表)扩展Visual Studio Build Process
因此,实际上您应该下载并安装MSBuild extensionpack,然后打开项目的csproj/vbproj文件并进行以下编辑:

添加以下条目以导入MSBuild extensionpack的Robocopy任务

<PropertyGroup>
    <TPath>$(MSBuildExtensionsPath32)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks</TPath>        
</PropertyGroup>
<Import Project="$(TPath)"/>

覆盖BeforeBuild、AfterBuild并执行Robocopy任务


<Target Name="BeforeBuild">
<Message Text="Beforebuild" />
  <MSBuild.ExtensionPack.FileSystem.RoboCopy Source="C:\temp\robo_src1" Destination="C:\temp\robo_dest1" Files="*.*" Options="/MIR">
      <Output TaskParameter="ExitCode" PropertyName="Exit" />
      <Output TaskParameter="ReturnCode" PropertyName="Return" />
  </MSBuild.ExtensionPack.FileSystem.RoboCopy>
  <Message Text="ExitCode = $(Exit)"/>
  <Message Text="ReturnCode = $(Return)"/>
</Target>
<Target Name="AfterBuild">
  <MSBuild.ExtensionPack.FileSystem.RoboCopy Source="C:\temp\robo_src2" Destination="C:\temp\robo_dest2" Files="*.*" Options="/MIR">
      <Output TaskParameter="ExitCode" PropertyName="Exit" />
      <Output TaskParameter="ReturnCode" PropertyName="Return" />
  </MSBuild.ExtensionPack.FileSystem.RoboCopy>
  <Message Text="ExitCode = $(Exit)"/>
  <Message Text="ReturnCode = $(Return)"/>
</Target>

请在您的回答中解释如何添加它以达到相同的目的。 - Asaf R
抱歉,代码被截断了,我对这个编辑器不是很熟悉。我正在努力解决如何正确添加代码的问题。 - János Nagy
好的,同时我终于解决了。希望这有所帮助,如果需要更多信息,请告诉我。 - János Nagy

4
被接受的答案在我看来有些过度了。Robocopy已经定义了其退出代码,因此我们通常可以假定任何小于等于8的值表示操作成功。

"大于8的任何值都表示复制操作中至少发生了一个失败。"

假设您的命令是ROBOCOPY $(Source) $(Dest) *.*,我将简称为$(RobocopyBinCommand)
在Visual Studio中,对于预生成或后生成事件,请单击下拉列表并选择<Edit...>
在您的命令下创建一行新行,并放置IF %ERRORLEVEL% LEQ 8 EXIT 0,然后应用并关闭属性窗口,例如:

example

高级退出码要求

假设您只希望在ROBOCOPY返回13时才通过构建。上面的if检查甚至不允许您使用CMD.exe支持的类似于OR的行为来解决问题。您可以多种方式解决此限制,但我认为这是最简洁的方法之一。

if %errorlevel% LEQ 3 echo %errorlevel%|findstr "1 3"

一行代码解释

基本上,我们正在将回显错误级别的结果传输到findstr,它正在查找1或3。我们不必担心像2316这样的值,因为第一个评估确保该值小于等于3。一旦该评估通过(如果确实通过),则将错误级别传输到findstr,然后将错误级别与13进行比较。如果findstr检测到任何一个,则findstr将退出0,否则不会退出。如果错误级别不是3或更低,则错误级别将保持不变,并且使用ROBOCOPY时构建任务将像往常一样退出1。

0
我发现使用`robocopy`启动要比尝试在Visual Studio中内联调用它容易得多。这样,Visual Studio就不需要关心`robocopy`的返回代码。
start robocopy . ..\latestbuild

我唯一能看到的区别是,你会看到一个命令提示符出现和消失来执行robocopy命令。
使用call而不是start实际上不会打开命令提示符,更好的是,将robocopy的输出重定向到Visual Studio输出窗口。
call robocopy . ..\latestbuild

由于某些原因,只有在预构建事件命令行中使用此方法才有效。


“start” 仅在新的 “CMD” 实例中生成命令,并不受当前 CMD 默认检查,因此不应该在这种用例中失败,也可能不可靠。 - kayleeFrye_onDeck

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接