At work and also on my private computer I am maintaining multiple Delphi projects that are (currently) managed in subversion repositories. They have a general structure like this:
projectname \- buildtools (-> svn:external) \- src (project sources) \- libs \- dzlib (-> svn:external) \- some other libraries, all svn:external
Being a lazy bastard (deutsch: faule Socke) I don’t want to start Delphi just to compile the projects or check whether they still compile but have a build script to do that. Also, I don’t want to right click and svn update all the time (and build after that) so I have got an autobuildcheck script. And last but not least, since on my computer all Delphi versions since version 6 are installed, I need a method to automatically launch the correct IDE for each of the projects.
First, auto_build_check.cmd:
That one is simple. It needs to
- svn update
- call the build script
- revert the build number increment which the build script automatically made
- if no error occurred, just exit, otherwise pause to let me examine the error message
rem Update sources from repository rem Build them rem in case of error(s): Stop and show the error rem revert the automatic build number incrementation svn update if errorlevel 1 goto Error set BatchBuild=1 call _BuildProject.cmd if not exist src\*_version.ini goto :eof svn revert src\*_version.ini if errorlevel 1 goto error goto :eof :error echo ***** ERROR ***** pause
Next, _buildproject.cmd:
It needs to
- determine the project name from the current directory (I don’t want to write different build script for each project, lazy bastard again)
- determine the Delphi version to use for building (this one only supports Delphi 2007 and later)
- call msbuild
- if an error occurs, show that error
- pause, to let me examine the results
rem @echo off REM - assumes that the parent directory is also the project name, extracts it REM - and calls msbuild with %project%.dproj setlocal rem Has the project been passed as parameter? set project=%1 if not "%project%"=="" goto projgiven rem Is there a __SetProjectName.cmd which supposedly sets %project%? if exist __SetProjectName.cmd call __SetProjectName.cmd if not "%project%"=="" goto projgiven rem Still no project, use the parent directory name to get it call :GetLastDir %0 set project=%result% :projgiven call buildtools\delphiversions.cmd set DelphiVersion=Delphi 2007 set DelphiDir=%Delphi2007Dir% if exist __SetDelphiVersion.cmd call __SetDelphiVersion.cmd set OldPath=%PATH% echo building project %project%.dproj using %DelphiVersion% call "%DelphiDir%\bin\rsvars.bat" if NOT "%DelphiVersion%"=="Delphi 2007" goto no2007 rem Delphi 2007 sets the FrameworkDir incorrectly on 64 bit Windows, so we fix this here SET FrameworkDir=%SystemRoot%\Microsoft.NET\Framework\ SET PATH=%FrameworkDir%%FrameworkVersion%;%FrameworkSDKDir%;%OldPath% :no2007 pushd src msbuild %project%.dproj | ..\buildtools\msbuildfilter ..\errors.txt popd if errorlevel 1 goto error endlocal if "%BatchBuild%"=="1" goto nopause pause :nopause goto :eof :error echo ************************************ echo ***** Error building %project% ***** echo ************************************ if "%MAILSend%"=="" goto nomail %MAILSend% -sub "Error building %project%" -M "Build Error" -attach errors.txt,text/plain,a endlocal goto :eof :nomail endlocal pause goto :eof :GetLastDir rem extract path set directory=%~p1% rem remove backslash (=last character) set directory=%directory:~0,-1% rem extract "filename" (= last directory of path) call :LastItem %directory% goto :eof :LastItem set result=%~n1% goto :eof
Actually, this script does some more things:
- It allows me to pass the project name as a parameter.
- If no parameter is passed, it looks for a file called __SetProjectName in the current directory, which can set %project%.
- If that doesn’t exist either, it calls the GetLastDir subroutine to get the last part of the current directory and uses that as the project.
- next, it calls buildtools\delphiversions.cmd which is a script that knows where all my Delphi versions are installed and sets the base directories for them as Delphi2007Dir, Delphi2009Dir etc. environment variables.
- Determine wichh Delphi version it should use. It assumes Delphi 2007 (which I still use for the majority of my projects), looks for __SetDelphiVersion.cmd in the current directory, which is supposed to set %DelphiVersion% and %DelphiDir% if a different version is to be used.
- If Delphi 2007 is to be used, it implements a workaround to fix a problem this version has with setting %FrameworkDir% on 64 bit Windows.
- It calls rsvars.bat (which is a script installed by Delphi)
- It switches to the src subdir
- It calls msbuild for %project%.dproj (msbuildfilter is an internal tool that filters the output and counts hints, warnings and errors)
- If there is no error it either pauses, or if in batchbuild mode, it just exists.
- If there is an error, it checks whether it should send an e-mail and does that (I’ll come back to that in a future blog post.).
- If it sent an e-mail, it exists, otherwise it pauses to let me examine the error.
Last, _OpenInIde.cmd
It needs to:
- Determine the project name (see above)
- Determine the Delphi version to use (again: see above)
- Call the IDE of that version and pass it the appropriate .dproj file
@echo off REM - assumes that the parent directory is also the project name, extracts it REM - and calls the IDE with %project%.dproj setlocal rem Has the project been passed as parameter? set project=%1 if not "%project%"=="" goto projgiven rem Is there a __SetProjectName.cmd which supposedly sets %project%? if exist __SetProjectName.cmd call __SetProjectName.cmd if not "%project%"=="" goto projgiven rem Still no project, use the parent directory name to get it call :GetLastDir %0 set project=%result% :projgiven call buildtools\delphiversions.cmd set DelphiVersion=Delphi 2007 set DelphiDir=%Delphi2007Dir% if exist __SetDelphiVersion.cmd call __SetDelphiVersion.cmd start "%DelphiVerson%""%DelphiDir%\bin\bds.exe" -pDelphi src\%project%.dproj endlocal goto :eof :GetLastDir rem extract path set directory=%~p1% rem remove backslash (=last character) set directory=%directory:~0,-1% rem extract "filename" (= last directory of path) call :LastItem %directory% goto :eof :LastItem set result=%~n1% goto :eof
So, am I lazy? I don’t really know. Writing the scripts described above was quite a lot of work. On the other hand, that was interesting work and it keeps me from making stupid errors again and again, like opening a project with the wrong Delphi version and having to revert the changes done by that version, or forgetting to do svn update before checking if the project still compiles. Also, they allow me to run a continuous build server to automatically find compile errors caused by incompatible changes to one of the libraries used, before I or my coworkers forget the reason for that change.
But why have copies of these scripts in all my projects rather than having them in buildtools and only small stubs in the project, that call these scripts? So that’s the next step.
If you want to use these scripts in your own projects or roll your own based on them, feel free to do so. You can find them in my buildtools repository.