0

I have a problem with my post-build event using robocopy. The code used to work a couple of days ago and now it is not working in any of my projects. Below, the code I use for my post-built events for creating plugins for different builds of the same app.

Edited as per Squashman and Mofi recommendations:

echo Configuration: $(Configuration)
 
set "Dir2018=C:\Program Files\Rune\Altem 2018\Plugins\"
set "Dir2019=C:\Program Files\Rune\Altem 2019\Plugins\"
set "Dir2020=C:\Program Files\Rune\Altem 2020\Plugins\"
 
if $(Configuration) == Debug2018 goto 2018
if $(Configuration) == Debug2019 goto 2019
if $(Configuration) == Debug2020 goto 2020
 
:2018
 
echo Copying results to 2018
if not exist "%Dir2018%$(ProjectName)" mkdir "%Dir2018%$(ProjectName)"
robocopy $(TargetDir) "%Dir2018%$(ProjectName)" /XF "*.pdb" /E
if %errorlevel% leq 1 exit 0 else exit %errorlevel%
 
goto exit
 
:2019
 
echo Copying results to 2019
if not exist "%Dir2019%$(ProjectName)" mkdir "%Dir2019%$(ProjectName)"
robocopy "$(TargetDir)" "%Dir2019%$(ProjectName)" /XF "*.pdb" /E
if %errorlevel% leq 1 exit 0 else exit %errorlevel%
 
goto exit
 
:2020
 
echo Copying results to 2020
if not exist "%Dir2020%$(ProjectName)" mkdir "%Dir2020%$(ProjectName)"
robocopy $(TargetDir) "%Dir2020%$(ProjectName)" /XF "*.pdb" /E
if %errorlevel% leq 1 exit 0 else exit %errorlevel%
 
goto exit
 
:exit

The errors are shown in the visual studio:

Severity    Code    Description Project File    Line    Suppression State
Error       The command "echo Configuration: Debug2019
 
set "Dir2018=C:\Program Files\Rune\Altem 2018\Plugins\"
set "Dir2019=C:\Program Files\Rune\Altem 2019\Plugins\"
set "Dir2020=C:\Program Files\Rune\Altem 2020\Plugins\"
 
if Debug2019 == Debug2018 goto 2018
if Debug2019 == Debug2019 goto 2019
if Debug2019 == Debug2020 goto 2020
 
:2018
 
echo Copying results to 2018
if not exist "%Dir2018%SnoopTool" mkdir "%Dir2018%SnoopTool"
robocopy C:\Users\username\OneDrive\_WORK FILES\_REPOS\nw_issuecreator\nw_issuecreator\SnoopTool\bin\Debug2019\ "%Dir2018%SnoopTool" /XF "*.pdb" /E
if %errorlevel% leq 1 exit 0 else exit %errorlevel%
 
goto exit
 
:2019
 
echo Copying results to 2019
if not exist "%Dir2019%SnoopTool" mkdir "%Dir2019%SnoopTool"
robocopy "C:\Users\username\OneDrive\_WORK FILES\_REPOS\nw_issuecreator\nw_issuecreator\SnoopTool\bin\Debug2019\" "%Dir2019%SnoopTool" /XF "*.pdb" /E
if %errorlevel% leq 1 exit 0 else exit %errorlevel%
 
goto exit
 
:2020
 
echo Copying results to 2020
if not exist "%Dir2020%SnoopTool" mkdir "%Dir2020%SnoopTool"
robocopy C:\Users\username\OneDrive\_WORK FILES\_REPOS\nw_issuecreator\nw_issuecreator\SnoopTool\bin\Debug2019\ "%Dir2020%SnoopTool" /XF "*.pdb" /E
if %errorlevel% leq 1 exit 0 else exit %errorlevel%
 
goto exit
 
:exit" exited with code 16. SnoopTool
Invalid Parameter #3 : "C:\Program Files\Rune\Altem 2019\Plugins\SnoopTool" 

The line if not exist %Dir2019%$(ProjectName) mkdir %Dir2019%$(ProjectName) gets successfully executed. it creates the directory if it doesn't exist. However, I believe the problem is with the next line robocopy $(TargetDir) %Dir2019%$(ProjectName) /XF "*.pdb" /E. I haven't changed anything in the apps, and this is happening in all my projects. Could has it been an update on how robocopy works? I am not sure. Completely puzzled here.

EDIT:

I updated my code to follow the recommendations from Squashman and Mofi, but the error persisted. I proceeded to delete most of the post-event code to narrow down the error:

echo Configuration: $(Configuration)
 
echo Copying results to 2019
robocopy $(TargetDir) "C:\Users\username\Documents\New folder (2)"
if %errorlevel% leq 1 exit 0 else exit %errorlevel%

:exit

Even with the clearly explicit path, the error is still popping up:

Invalid Parameter #3 : "C:\Users\oscarramirez\Documents\New folder (2)"

Thanks for any help. And in case it is not obvious, I'm an amateur when it comes to code. I am not a developer by any mean, so be gentle if my error is a massive rookie mistake.

  • 1
    With Robocopy do not end the source or destination paths with a backslash and always use quotes to be protect spaces and special characters in file paths. `robocopy "C:\some\source\path" "D:\to\destination\path"` – Squashman May 15 '21 at 05:43
  • 1
    I highly recommend that you do not assign quotes to variables. Best practice for variable assignment is `set "Dir2018=C:\Program Files\Rune\Altem 2018\Plugins\"`. This uses quotes to protect the variable assignment but does not make the quotes apart of the value of the variable. Then when you need to use the variable use quotes around it. `if not exist "%Dir2018%IssueCreator" mkdir "%Dir2018%IssueCreator"`. – Squashman May 15 '21 at 05:48
  • That error is because your Source directory is not quoted correctly. Update your question with all of the verbose output from the script including all the robocopy messages. – Squashman May 15 '21 at 18:40

1 Answers1

0

I suggest following code for the batch file:

echo Configuration: $(Configuration)
set "ConfigNumber=$(Configuration)"
set "ConfigNumber=%ConfigNumber:~-4%"
echo Copying results to %ConfigNumber%
%SystemRoot%\System32\robocopy.exe "$(TargetDir)\" "C:\Program Files\Rune\Altem %ConfigNumber%\Plugins\$(ProjectName)" /XF "*.pdb" /E /R:3 /W:5

That is it.

Another working code would be:

echo Configuration: $(Configuration)
set "ConfigNumber=$(Configuration)"
set "ConfigNumber=%ConfigNumber:~-4%"
echo Copying results to %ConfigNumber%
set "SourceDirectory=$(TargetDir)"
if "%SourceDirectory:~-1%" == "\" set "SourceDirectory=%SourceDirectory%\"
%SystemRoot%\System32\robocopy.exe "%SourceDirectory%" "C:\Program Files\Rune\Altem %ConfigNumber%\Plugins\$(ProjectName)" /XF "*.pdb" /E /R:3 /W:5

This second code would work also for $(TargetDir) being replaced by a directory path not ending with a backslash.

The string value of Visual Studio variable Configuration referenced with $(Configuration) is replaced by Visual Studio, for example, by Debug2019 in first and second command line. For that reason the second line defines the environment variable ConfigNumber with the string Debug2019.

The third line redefines the environment variable ConfigNumber with just the last four characters of the current string value. So ConfigNumber is defined with the string 2019 after the third line.

For the last command line two facts about ROBOCOPY in addition to usage help output on running robocopy /? in a command prompt window and the Microsoft documentation for robocopy and the SS64 articles about ROBOCOPY.exe and ROBOCOPY Exit Codes:

  1. A backslash \ left to one more \ or " is interpreted by ROBOCOPY as escape character for \ respectively ". In all other cases a backslash is interpreted as literal character.
  2. The entire destination directory tree is created automatically by ROBOCOPY on not already existing.

Visual Studio replaces $(TargetDir) by the string value assigned to Visual Studio variable TargetDir which ends with a backslash.

It is necessary for the Windows command processor cmd.exe to use in the ROBOCOPY command line "$(TargetDir)" to work also for a directory path containing a space or one of these characters &(){}^=;!'+,`~.

The backslash at end of directory path is interpreted by ROBOCOPY as an escape character for " because of the first fact. So ROBOCOPY would interpret " at end of the destination path as literal character which belongs to destination path although a directory path cannot contain the character " at all as described by Microsoft in documentation about Naming Files, Paths, and Namespaces. Therefore ROBOCOPY would interpret everything up to " at beginning of destination argument string as source argument string. That is clearly a not wanted arguments string interpretation by ROBOCOPY.

The solution is appending to $(TargetDir) an additional backslash and use for source argument "$(TargetDir)\". The backslash at end of the target directory path of the Visual Studio build is interpreted now as escape character for the backslash added explicitly to the argument string by ROBOCOPY. The double quote character at end of the first directory path argument string is interpreted now by ROBOCOPY as end of the source directory path.

It would be also possible to use "$(TargetDir)." with the dot meaning current directory. ROBOCOPY interprets in this case the backslash as literal character because of next character is . and not \ or ". Windows file management would replace the directory path ending now with \. by the absolute directory path not ending with \. on ROBOCOPY accessing the source directory.

It is not necessary to check for existence of the destination directory of the file copying task with the command IF and create the destination directory with command MKDIR on not existing. ROBOCOPY creates the entire directory tree to specified destination directory automatically on destination directory not existing already.

The Windows command processor passes the exit code of ROBOCOPY to the calling batch file respectively calling process. So there is no more line needed.

It would be also possible to use in second code:

if "%SourceDirectory:~-1%" == "\" set "SourceDirectory=%SourceDirectory:~0,-1%"

That command line removes the backslash at end of the directory path instead of appending one more backslash. That works for all directory paths with the exception of the path of the root directory of a drive, i.e. C:\ or E:\. It is very unlikely that the target directory of a Visual Studio build is the root directory of a drive, but it is nevertheless possible.

ROBOCOPY exits with a value in range 0 to 7 on no error occurred on copying the files. The ROBOCOPY exit code is 8 or greater if there was an error on copying.

The last line in the batch file executed as post build step could be also one of the following two lines:

if not errorlevel 8 (exit /B 0) else exit /B 1
if not errorlevel 2 exit /B 0

The first command line exits batch file processing with exit code 0 on ROBOCOPY exiting with a value in range 0 to 7 and otherwise with exit code 1.

The second command line exits batch file processing with exit code 0 on ROBOCOPY exiting with a value 0 or 1 and otherwise with exit code of ROBOCOPY not modified by command IF.

Please run in a command prompt window if /? for help on command IF and the syntax required for an IF ... ELSE condition as well as which syntax is recommended to evaluate the exit code of previous command/executable assigned to dynamic variable ERRORLEVEL which is not an environment variable although its value can be accessed using the syntax for referencing the value of environment variable.

The command or command line(s) to execute on condition being true must be enclosed in round brackets as otherwise the command IF interprets everything after the condition as argument to execute including the ELSE statement.

if %errorlevel% leq 1 exit 0 else exit %errorlevel%

This condition results in execution of command EXIT with value 0 on ROBOCOPY exits with 0 or 1. Otherwise the batch file processing is continued on next line if ROBOCOPY is greater 1 because of else is not recognized by if due to the missing round brackets. The command EXIT ignores everything after the exit value. So this command line could be also written as:

if %errorlevel% leq 1 exit 0 Batch processing exits with value 0 on condition is true.

See also:

Mofi
  • 46,139
  • 17
  • 80
  • 143