开发者

Making pre- and post-build event scripts pretty?

I have some moderately hefty pre- and post-build event scripts for my Visual Studio 2008 projects (actually it's mainly post-build event scripts). They work OK in that they function correctly and when I exit 0 the build succeeds and when I exit 1 the build fails with an error. However, that error is enormous, and goes something like this:

The command "if Release == Debug goto Foo
if Release == Release goto Bar
exit 0

:Foo
mkdir "abc"
copy "$(TargetDir)file.dll" "abc"
[...]
" exited with code 1.

You get the idea. The entire script is always dumped out as part of the error description. The entire script is also dumped out in the Output window while the build is happening, too. So, why have I seen various references on the web to using echo in these scripts? For example, here's part of an example on one particular site:

:B开发者_StackOverflowuildEventFailed
echo POSTBUILDSTEP for $(ProjectName) FAILED
exit 1
:BuildEventOK
echo POSTBUILDSTEP for $(ProjectName) COMPLETED OK

Is there a way to get Visual Studio to suppress all script output apart from what is echoed (and therefore using echo only to output what you want would make sense), or are these examples just misguided and they don't realize that the whole script is always dumped out?


OK - the core of the issue seems to be that the Visual Studio C++ and C# build engines are quite different.

The C++ build engine executes the batch code specified in the project's pre- or post-build event 'Command Line' without dumping the actual code to the Output window; it just dumps out what is echoed by the code, as well as what is sent out by commands the code executes (such as the copy command's '2 file(s) copied', etc.) The examples I've seen around the web are presumably intended for Visual Studio's C++ build engine, because there is no need really to echo anything with the C# build engine if you're putting your C# pre- or post-build batch code in the C# project's 'event command line' box.

This is because the C# build engine, which I was using, does dump all the code in that box to the Output window. Whatsmore, if the code fails, it includes the entire code block in that box in the error message that will appear in 'Error List' - the C++ build engine doesn't (more on that later).

The best solution I've found, therefore, is to minimize the amount of code you put in a C# project's pre- or post-build event command line box. Everything you put in there WILL be dumped to the Output window as it is executed. The best way to minimize the code there is to make it just execute a batch file, and pass the necessary arguments to the batch file. The contents of the batch file will NOT be dumped to the Output window, but (like with code in the 'Command Line' for the C++ build engine) echo output and output from commands the batch file code executes will be; this is a good way to control the output of a C# pre- or post-build script. The C++ build engine, then, is treating the code specified in the 'Command Line' box in the same way as the C# engine treats code in a batch file; it doesn't dump out the code itself, just the code's output.

So basically, if you're compiling a C++ project in Visual Studio, you can just put all the batch script in the 'Command Line' box and it won't all be dumped to the Output window. If compiling a C# project, though, I recommend putting your batch script in a separate .bat file, and just calling that file with the appropriate arguments from the pre- or post-build event command line box. I ended up with my C# post-build event command line box looking like this:

..\..\BuildScripts\PostBuild.bat $(ConfigurationName) $(ProjectName) "$(TargetDir)" "$(ProjectDir)"

... and my PostBuild.bat file looking like this:

@REM Store args...
set _configName=%1%
set _projectName=%2%

@REM Remove quotes from remaining args...
set _targetDir=###%3%###
set _targetDir=%_targetDir:"###=%
set _targetDir=%_targetDir:###"=%
set _targetDir=%_targetDir:###=%

set _projectDir=###%4%###
set _projectDir=%_projectDir:"###=%
set _projectDir=%_projectDir:###"=%
set _projectDir=%_projectDir:###=%

if %_configName% == Release goto StartProcessing
echo No post-build processing required.
exit 0

@REM Start post-build processing
:StartProcessing
echo Starting post-build processing.

@REM use input args to do batch script work, such as:
copy "%_targetDir%*.dll" "%_projectDir%..\..\..\..\..\CommonAssemblies"
if errorlevel 1 goto CopyFailure

@REM etc.

goto PostBuildSuccess

@REM Failure labels
:CopyFailure
echo Post-build processing for %_projectName% FAILED: Failed to copy file(s) to common assemblies directory!
exit 1

@REM Post-build success
:PostBuildSuccess
echo Post-build processing for %_projectName% completed OK.
exit 0

This controls the output much more neatly, and only things echoed in this batch script will be output to the Output window.

Finally, as mentioned above, the C# and C++ build engines also output different things in the error message that appears in 'Error List', when post-build processing fails (ie. the batch code exits with a code other than 0). The C++ build engine always seems to just say:

Error result 1 returned from 'C:\Windows\system32\cmd.exe'.

The C# build engine, however, will include the whole contents of the pre- or post-build event command line box (whichever failed) in the error message, eg.:

The command "..\..\BuildScripts\PostBuild.bat Release MyProject "C:\Development\MyProject\bin\" "C:\Development\MyProject\"" exited with code 1.

Still a bit of a mouthful for a single error message, but much more manageable than having all of the code we moved into the batch file included in the error message!

Even so, it would be nice to be able to customize this error message that appears in the Error List to display something I define. I doubt it's possible, but if anybody knows of a way to do this, please don't hesitate to post it as a comment!


If you put your scripts in a batch file, VS is pretty silent about it. When you need access to $(projectName) etc you will have to pass them as parameters to the batch file though. I looked for settings that might affect the output but couldn't find any.

Tested with this batch file (d:\test.bat); the @echo off is not even needed:

@echo off
echo "I'm Gonna Fail"
exit 1

Set Pre-Build Event->Command Line to d:\test.bat, leave Description empty (description is what is shown instead of Performing Pre-Build Event.... In VS2008, I get this in the build output window for a C++ project:

Performing Pre-Build Event...
"I'm Gonna Fail"
Project : error PRJ0002 : Error result 1 returned from 'C:\Windows\system32\cmd.exe'.

For a C# project, this is shown instead (with MSBuild verbosity set to normal):

Target PreBuildEvent:
  d:\test.bat
  "I'm gonna fail"
  Microsoft.Common.targets(895,9) error MSB3073: The command "d:\test.bat" exited with code 1.


@Jez your answer is great, but i would improve the Batch file as follow: By using %~1 it will remove quotes from args more easily:

@echo off
if "%~1"=="" (set ErrorMessage=Parameter 1 missing. It has to be the Config name. & GOTO :EndError)
if "%~2"=="" (set ErrorMessage=Parameter 2 missing. It has to be the Project name. & GOTO :EndError)
if "%~3"=="" (set ErrorMessage=Parameter 3 missing. It has to be the Targetdir. & GOTO :EndError)
if "%~4"=="" (set ErrorMessage=Parameter 4 missing. It has to be the Projectdir. & GOTO :EndError)

cd /D "%~dp0"  &:: setting current directory to there where the batchfile is.

:: With ~ the quotes are removed
set _configName=%~1 
set _projectName=%~2
set _targetDir=%~3
set _projectDir=%~4
set _outDir=%~5

rd Plugins\%_projectName% /S /Q
if errorlevel 1 (set ErrorMessage=remove directory failed!!. & GOTO :EndError)
md Plugins\%_projectName%
if errorlevel 1 (set ErrorMessage=make directory failed!!. & GOTO :EndError)
xcopy %_targetDir%* Plugins\%_projectName% /E /Q
if errorlevel 1 (set ErrorMessage=Copy failed!!. & GOTO :EndError)

exit 0

:EndError
echo --! Error in %~nx0 !-- 
echo %ErrorMessage%
pause
exit 1
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜