Robocopy 在成功时输出 1,这与大多数在成功时以 0 退出的程序不同。Visual Studio(和 MSBUILD)将退出代码 1 解释为错误。
Robocopy 如何在 Visual Studio 构建后和构建前事件中使用,以便构建环境正确识别其失败和成功?
注意:这或多或少是这篇文章的转贴。
Robocopy 在成功时输出 1,这与大多数在成功时以 0 退出的程序不同。Visual Studio(和 MSBUILD)将退出代码 1 解释为错误。
Robocopy 如何在 Visual Studio 构建后和构建前事件中使用,以便构建环境正确识别其失败和成功?
注意:这或多或少是这篇文章的转贴。
根据请求添加此答案。基于 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 未发生错误,未进行复制。源目录树和目标目录树完全同步。
<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
简单地检查退出代码 1 是不正确的,因为任何低于 8 的退出代码都是非错误的:
任何大于 8 的值都表示在复制操作期间至少发生了一次故障。
(澄清一下,退出代码 8 也是一个错误Several files did not copy
:)
那么,正确的代码应该如下所示:
IF %ERRORLEVEL% GEQ 8 exit 1
exit 0
从语法上讲,这里是一个每命令一行的版本,它直接在 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
参考:
MSBuild 扩展包包含一个可以在构建过程中使用的 Robocopy 任务。
这可以成为您的解决方案,而不是 VS pre/postbuild 事件吗?
如果是这样,您可以通过覆盖 BeforeBuild、AfterBuild 目标和调用 Robocopy 任务来扩展 Visual Studio 构建过程(如果它们更适合您的需要,您也可以覆盖其他目标,请参阅链接的 MSDN 页面中的列表)
所以实际上您应该下载并安装 MSBuild 扩展包,而不是打开项目的 csproj/vbproj 文件并按以下方式编辑:
添加以下条目以导入 MSBuild 扩展包的 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>
接受的答案是过度杀伤国际海事组织。Robocopy 已经定义了它的退出代码,所以我们通常可以假设任何值8
或更少都表明事情进展顺利。
“任何大于 8 的值都表示在复制操作过程中至少出现了一次故障。”
因此,假设您的命令是 ,ROBOCOPY $(Source) $(Dest) *.*
我将其简称为$(RobocopyBinCommand)
。
在 Visual Studio 中为您的 Pre-Build 或 Post-Build 事件,单击下拉菜单并选择<Edit...>
在您的命令下方创建一个新行,然后放置IF %ERRORLEVEL% LEQ 8 EXIT 0
然后应用并关闭“属性”窗口,例如:
假设您只希望在 ROBOCOPY 返回1
或3
. 上面的 if-check 甚至不允许您使用OR
CMD.exe 支持的类似行为来解决问题。您可以通过多种方式解决此限制,但我认为这是最简洁的方法之一。
if %errorlevel% LEQ 3 echo %errorlevel%|findstr "1 3"
基本上,我们正在管道回显findstr
正在寻找 1 或 3 的错误级别的结果。我们不必担心其中包含 3 或 1 的值,23
或者16
因为第一次评估确保该值为 3 或更小。一旦该评估通过,如果它确实通过了,则将 errorlevel 传递给该评估findstr
,然后将 errorlevel 与1
or进行比较3
。如果 findstr 检测到任何一个,findstr 将退出 0,否则不会。如果 errorlevel 不是 3 或更低,errorlevel 将保持不变,并且构建任务将照常退出 1,不再使用 ROBOCOPY。
我发现启动robocopy 比尝试在 Visual Studio 中调用它要容易得多。这种方式 Visual Studio 不关心来自 robocopy 的返回码。
start robocopy . ..\latestbuild
我能看到的唯一区别是您会看到命令提示符出现和消失以执行 robocopy 命令。
使用call而不是start实际上不会打开命令提示符,甚至更好地将输出从 robocopy 重定向到 Visual Studio 输出窗口。
call robocopy . ..\latestbuild
出于某种原因,这种方法仅在预构建事件命令行中使用时才有效。