Robocopy在成功完成时会输出1,而大多数程序在成功时会以0退出。Visual Studio(和MSBUILD)将1的退出代码解释为错误。
如何在Visual Studio的后期和前期构建事件中使用Robocopy,以便构建环境可以正确识别其失败和成功呢?
注意:这更或多或少是此帖子的转载。
Robocopy在成功完成时会输出1,而大多数程序在成功时会以0退出。Visual Studio(和MSBUILD)将1的退出代码解释为错误。
如何在Visual Studio的后期和前期构建事件中使用Robocopy,以便构建环境可以正确识别其失败和成功呢?
注意:这更或多或少是此帖子的转载。
按照请求,我进行了翻译。根据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也是一个错误:多个文件未能成功复制
)
正确的代码应该像这样:
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 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>
8
的值表示操作成功。
假设您的命令是"大于8的任何值都表示复制操作中至少发生了一个失败。"
ROBOCOPY $(Source) $(Dest) *.*
,我将简称为$(RobocopyBinCommand)
。<Edit...>
。IF %ERRORLEVEL% LEQ 8 EXIT 0
,然后应用并关闭属性窗口,例如:
假设您只希望在ROBOCOPY返回1
或3
时才通过构建。上面的if检查甚至不允许您使用CMD.exe支持的类似于OR
的行为来解决问题。您可以多种方式解决此限制,但我认为这是最简洁的方法之一。
if %errorlevel% LEQ 3 echo %errorlevel%|findstr "1 3"
findstr
,它正在查找1或3。我们不必担心像23
或16
这样的值,因为第一个评估确保该值小于等于3。一旦该评估通过(如果确实通过),则将错误级别传输到findstr
,然后将错误级别与1
或3
进行比较。如果findstr检测到任何一个,则findstr将退出0,否则不会退出。如果错误级别不是3或更低,则错误级别将保持不变,并且使用ROBOCOPY时构建任务将像往常一样退出1。start robocopy . ..\latestbuild
call robocopy . ..\latestbuild
由于某些原因,只有在预构建事件命令行中使用此方法才有效。
if errorlevel 1 exit 0 else exit %errorlevel%
。 - mafuif errorlevel 1 ...
"的意思是"如果错误等于或大于1"。换句话说,这可以捕获所有错误,但不能捕获“不需要复制”(robocopy的0退出代码)。但我认为您需要使用if %errorlevel% leq 1 exit 0 else exit %errorlevel%
。 - skrebbel