My project at work has historically been a small component in a much larger pile of APIs and applications all tied to the same build system, a Windows scheduled task and a pile of incoherent batch scripts. Since then, we have grown up into our own small pile of APIs and connected applications and the time to divorce the parent project has finally come.
In order to make the transition as clean as possible, I’ve been removing dependencies, reorganising the project structure and cleaning up our build scripts. Our Linux builds have always been isolated from the rest of the system so getting that to work in isolation was easy. The Windows builds on the other hand, took a lot more work.
I needed to write a new script to build the Visual Studio solutions on a new continuous integration server. This is largely because the server can’t really handle Visual Studio 2003 without a little help. The script started out simple enough, using the command line arguments to set a few variables, select a version of Visual Studio and a build configuration, then eventually calling devenv with the required parameters. The batch language is primitive and buggy so it took a few goes to get this right. Eventually, I got it working on my machine, so I commited the script and watched it die on the CI server.
The Visual Studio installation wasn’t found in Program Files. Well, this is a 64 bit server, so I queried
%PROCESSOR_ARCHITECTURE% and had an alternate path on the 64 bit machine. Searching around the topic, you will sometimes see people checking specifically for
%ProgramFiles(x86)% or even if the raw path
C:\Program Files (x86) exists. I did something like this:
rem select program files directory if /I "%PROCESSOR_ARCHITECTURE%" EQU "AMD64" ( echo building on 64-bit Windows set _VS7DIR_="C:\Program Files (x86)\Microsoft Visual Studio .NET 7.1\Common7\IDE" set _VS8DIR_="C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE" ) else ( echo building on 32-bit Windows set _VS7DIR_="C:\Program Files\Microsoft Visual Studio .NET 7.1\Common7\IDE" set _VS8DIR_="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE" )
This threw up errors in the script parser, saying things like
'\Microsoft' not expected at this time. After a bit of searching around, I found that the parser was interpreting the closing parenthesis in
Program Files (x86) as the end of the
IF statement. This behaviour is obviously crazy, but nobody is ever going to update such an ancient and terrible scripting language, so we just have to accept that nesting parentheses are never going to work properly in a batch file and work around it.
In the end, the mighty
goto solved the problem:
rem select program files directory if /I "%PROCESSOR_ARCHITECTURE%" EQU "AMD64" goto progfiles_win64 :progfiles_win32 echo building on 32-bit Windows set _VS7DIR_="C:\Program Files\Microsoft Visual Studio .NET 7.1\Common7\IDE" set _VS8DIR_="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE" goto progfiles_done :progfiles_win64 echo building on 64-bit Windows set _VS7DIR_="C:\Program Files (x86)\Microsoft Visual Studio .NET 7.1\Common7\IDE" set _VS8DIR_="C:\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE" goto progfiles_done :progfiles_done
Parsing command line options to correctly handle VC7 and VC8 was much the same, but more complicated:
rem establish paths and defaults set _VS7DIR_=%_VS7DIR_:"="""% set _VS8DIR_=%_VS8DIR_:"="""% set _VSDIR_=%_VS7DIR_% set _VC7SLN_=MyProject_71.sln set _VC8SLN_=MyProject_80.sln set _SLN_=%_VC7SLN_% set _OPTS_=/Build set _MODE_=Debug rem command line option parsing :begin_opts if /I "%1" EQU "" goto done_opts if /I "%1" EQU "clean" (set _OPTS_=/Clean) && goto next_opts if /I "%1" EQU "build" (set _OPTS_=/Build) && goto next_opts if /I "%1" EQU "rebuild" (set _OPTS_=/Rebuild) && goto next_opts if /I "%1" EQU "release" (set _MODE_=Release) && goto next_opts if /I "%1" EQU "debug" (set _MODE_=Debug) && goto next_opts rem find the visual studio installation rem forgive the spaghetti, Windows cannot parse parentheses in IF, FOR, etc. rem when we might have (x86) in the path if /I "%1" EQU "VC7" goto vc7_opts if /I "%1" EQU "VC8" goto vc8_opts rem catch-all for unknown options echo unknown option '%1' - clean, build, release or debug expected >&2 goto :failed_opts :next_opts shift goto begin_opts :vc7_opts echo VC7 selected set _SLN_=%_VC7SLN_% if exist %_VS7DIR_% goto vc7_exist_opts goto vc7_failed_opts :vc7_exist_opts echo using Visual Studio from %_VS7DIR_:"""='% set _VSDIR_=%_VS7DIR_% goto next_opts :vc8_opts echo VC8 selected set _SLN_=%_VC8SLN_% if exist %_VS8DIR_% goto vc8_exist_opts goto vc8_failed_opts :vc8_exist_opts echo using Visual Studio from %_VS8DIR_:"""='% set _VSDIR_=%_VS8DIR_% goto next_opts :done_opts :vc7_failed_opts echo Visual Studio 7 not found >&2 goto failed_opts :vc8_failed_opts echo Visual Studio 8 not found >&2 goto failed_opts :failed_opts echo option parsing failed >&2 goto 2>nul:
I hope this is useful to anyone suffering under the tyranny of the Windows batch scripting language. Where possible it is probably better to use Powershell or a third party scripting language like Python, Ruby, Perl, etc. In my situation I had to hit the lowest common denominator across various editions of Windows.