Simple Lightweight Build scripts covering .Net, VB6 and C++ projects

Some time ago we put together a lightweight build process utilising DOS batch files with a configurable Console application that could run as a scheduled nightly task on the build server if required but that could also be used in an ad-hoc fashion. The ultimate aim was to be able to divide our projects into separate branches and have them build successfully during the course of the project, and then when the branch representing the project was merged into the main trunk, we were able to perform the build again on the build server, produce packaged software and then have those “packages” deployed to target servers (usually test environments, the staging environment and then live).

This post covers the build process we employed. At the time it would have been possible to use “nAnt” but there was no obvious way to include a large number of VB6 projects that also needed building. So without much additional investigation we embarked on putting the following build scripts together.

Folder structure

We are very organised about how a solution and project are set-up. We employ the following simple folder structure for all of our projects broken into appropriate sub-systems where the solution and project will reside:-

__Scripts__
__Strong Name Keys__
Products <1 for each sub-system name of which “Products” is an example>
         Build
         Package
         Deploy
         Stage
         VisualBasic6
                 sample.vbg
                 Proj1
                     Proj1.vbp
                 Proj2
                     Proj2.vbp
         VSNet2003
                 sample.sln
                 Proj1
                      sample.csproj
                 Proj2
                      sample.vbproj
         VSNet2005
         etc

The function of each folder is as follows:-

  • “__Scripts__” contains script files (.vbs and .bat) that provide the main build functionality
  • “__Strong Name Keys__” contains pretty much what it says, strong name key files generated with sn.exe tool. Each assembly file referencing a key will do so using its relative path ONLY
  • “Build” contains a simple “startup” script that references the _Scripts__ folder to carry out the main build functionality
  • “Package” contains scripts to take the build output and form it in a way that the autodeployment utility can deploy it
  • “Deploy” contains the auto deployment XML for each environment to be deployed along with the script to start the deployment process on the target server. This would typically take care of Windows Services, web services and sites, Com+ components, registry entries, COM DLL registrations, etc and also have the ability to run pre and post deployment script steps
  • “Stage” typically contains the pre-compled build output for .Net 2.0 web sites and web services (covered in a previous post)

“Build” sub-folder

The build sub-folder contains a single batch file that is responsible for getting from TFS and building the contents of the sub-system in whch the “build” sub-folder exists. The build script almost exclusively makes use of a number of supporting script files in the “__Scripts__” folder as follows:-

@echo off
cls

ECHO *********************************
ECHO *SUB-SYSTEM *
ECHO *********************************

set bldscripts=..\..\__Scripts__
call %bldscripts%\GetLatestVB6
call %bldscripts%\GetLatestVSNet2003
call %bldscripts%\GetLatestVSNet2005
call %bldscripts%\BuildVB6
call %bldscripts%\CheckVB6Build
call %bldscripts%\BuildVSNet2003
call %bldscripts%\CheckVSNet2003Build
call %bldscripts%\BuildVSNet2005
call %bldscripts%\CheckVSNet2005Build
call %bldscripts%\PreCompileVSNet2005
echo.

“GetLatestVB6″ script

The script “GetLatestVB6″ in the “__Scripts__” folder is responsible for getting the Visual Basic 6 technology folder  within the specific sub-system that it resides in. The implementation is given below:-

REM ‘ GetLatest Script for VB6
REM ‘ Inputs:
REM ‘ %1 = Subsystem name
REM ‘ %2 = Label or null for get latest version
REM ‘ ————————————————————————-
@echo off
set path=%path%;C:\Program Files\Microsoft Visual Studio 8\Common7\IDE
if exist GetLatest_Out.txt del GetLatest_Out.txt
echo.
echo Deleting VB6 Folder…
if exist ..\VisualBasic6\*.* rmdir ..\VisualBasic6 /s /q
mkdir ..\VisualBasic6
if “%2″==”" goto getlatest

:getlabelled
echo Getting Labelled Version  (Label=%2) …
tf get ..\VisualBasic6 /version:%2 /all /recursive > GetLatestVB6_Out.txt
echo Get Labelled Version Complete.
goto done

:getlatest
echo Getting Latest Version …
tf get ..\VisualBasic6 /version:T /all /recursive > GetLatestVB6_Out.txt
echo Get Latest Version Complete.
goto done

:done
echo.

The script must be called with at least the name of the sub-system and optionally can also take a second argument of a label to get from. We tend to use Get latest and label ourselves manually. Fairly simple so far. The “get latest” for VS 2003 and VS 2005 is exactly the same but the folders are different as is the output text files for the results of the get in TFS. A simple refactor that could be employed here would be to parameterise the technology type (VisualBasic6, VSNet2003, etc) in the above “Getlatest” script to avoid having several jobs that effectively do the same thing for different technology types.

“BuildVB6″ script

This script is relatively straightforward and simply sets up any C++ variables defined in VC98 folder along with the path to the VB98 folder and then invokes the vbc compiler as given below:-

REM ‘ ————————————————————————-
REM ‘ Build Script for VB6
REM ‘ Inputs %1 = name of VBG file to build
REM ‘ ————————————————————————-
@echo off
REM Initialize VB6 Environment
call “C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT” > nul
set path=%path%;C:\Program Files\Microsoft Visual Studio\VB98

echo.
echo Building VB6 components …
if exist BuildVB6_out.txt del BuildVB6_out.txt
cd ..\VisualBasic6
vb6 /out ..\Build\BuildVB6_Out.txt /make %1.vbg
cd ..\Build

“BuildVSNet2003″ script

This script is more complicated than the VB6 build because there are issues with web sites that have to be dealt with for instance.  The highlights for the BuildVSNet2003 script are as follows:-

  • Sets up required environment variables for .net compiler
  • Invokes “ResetVRoots.bat” script to clear (if present) and create the VRoot for the site pointing the content location to where the build files reside instead of c:\inetpub\wwwroot
  • Clears VSWebCache folder for the security context of the user running the build
  • Rebuilds the entire solution (.sln) based on the relative location of VSNet2003 folder within the sub-system. Again a refactor here would be to provide a further optional parameter to allow “debug” or “release” build accordingly

The implementation of the script is given below:-

REM ‘ ————————————————————————-
REM ‘ Build Script for Visual Studio 2003
REM ‘ Inputs %1 = name of VBG file to build
REM ‘ ————————————————————————-

@echo off
REM Initialize VS.Net 2003 Environment
call “C:\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools\vsvars32.bat” > nul
echo.
echo Building VS.Net 2003 components …
if exist BuildVS2003_out.txt del BuildVS2003_out.txt
cd ..\VSNet2003
if exist ResetVROOTs.bat call “ResetVROOTs.bat” > nul
set wc1=c:\Documents and Settings\
set wc2=%USERNAME%
set wc3=\VSWebCache
echo Deleting %wc1%%wc2%%wc3%
del “%wc1%%wc2%%wc3%” /Q /S /F > nul
devenv /rebuild debug %1.sln >..\build\BuildVS2003_out.txt
cd ..\Build

“ResetVRoots.bat (+.vbs)” script

This small collection of scripts essentially sorts out the virtual directories (delete and recreate) for the sub-system concerned on the build server. The batch file does little more than call the associated .vbs file, both of which are listed here:-

REM ‘ ————————————————————————-
REM ‘
REM ‘ ResetVRoots.bat
REM ‘
REM ‘ ————————————————————————-

@echo off

cls
set bldscripts=..\..\__Scripts__
cscript.exe %bldscripts%\ResetVROOTs.vbs
echo.

REM ‘ ————————————————————————-
REM ‘
REM ‘ ResetVRoots.vbs
REM ‘
REM ‘ ————————————————————————-
 

Option Explicit

Dim objFSO

Dim objParentFolder

Dim objSubFolder

Dim sPath Dim SNameIncludeFile “.\VROOTUtils.vbs”

Set objFSO = CreateObject(“Scripting.FileSystemObject”)

Set objParentFolder = objFSO.GetFolder(“.”)

For Each objSubFolder in objParentFolder.SubFolders

‘ Check for presense of web.config

If IsWebFolder(objSubFolder) Then

WScript.Echo “”

WScript.Echo “Folder : ” & objSubFolder.Name

If RemoveVROOT(objSubFolder.Name) Then

WScript.Echo ” Removed VROOT”

Else

WScript.Echo ” No Existing VROOT”

End If

sPath = objSubFolder.Path

sName = objSubFolder.Name

If CreateVROOT(sName,sPath) Then

WScript.Echo ” VROOT Created”

Else

WScript.Echo ” VROOT Create FAILED”

End If

End If

Next

‘——————————————————-
‘ Include a script by a relative file name
‘ Add this code to each VBS file that needs to
‘ use ‘included’ script files, then call

‘ IncludeFile .vbs

‘ to include external scripts
‘ note: is relative to this script file location
‘——————————————————–

Private Sub IncludeFile(ByVal strIncludeFile)

Const cForReading = 1

Dim fso
Dim strScriptDir
Dim strFileName
Dim file
Dim strScript
Set fso = CreateObject(“Scripting.FileSystemObject”)
strScriptDir = fso.GetParentFolderName(WScript.ScriptFullName)
strFileName = fso.BuildPath(strScriptDir,strIncludeFile)
Set file = fso.OpenTextFile(strFileName,cForReading)
strScript = file.ReadAll()
ExecuteGlobal strScript
file.Close
End Sub

‘————————————————————————–

‘ VRootUtils.vbs

‘————————————————————————–

Public Function RemoveVROOT(sVDIR)

Dim objIIS
Dim objVdirRoot
RemoveVROOT = False

‘Get Default Web Site Object
Set objIIS = GetObject(“IIS://localhost/W3svc/1″)
‘Get root of Default Web Site
Set objVdirRoot = objIIS.GetObject(“IIsWebVirtualDir”, “Root”)

If IsVROOT(objVdirRoot, SVDIR) Then
objVdirRoot.Delete “IIsWebVirtualDir”,sVDIR
RemoveVROOT = True
Else
RemoveVROOT = False
End If

End Function

Function IsVROOT(objVdirRoot, sVDIR)

Dim objVdir
IsVROOT = False
For Each objVdir in objVdirRoot
If UCase(objVdir.Name) = UCase(sVDIR) AND objVDir.Class=”IIsWebVirtualDir” Then
IsVROOT = True
End If
Next

End Function

Function IsWebFolder(objFolder)

Dim objFile
IsWebFolder = False
For Each objFile in objFolder.Files
If Instr(1,objFile.Name,”web.config”,1) > 0 Or _
Instr(1,objFile.Name,”asp”,1) > 0 Then
IsWebFolder = True
End If
Next

End Function

Function CreateVROOT(sVDIR,sPath)

Dim vDir
Dim objIIS
Dim objVdirRoot

CreateVROOT = False
‘Get Default Web Site Object
Set objIIS = GetObject(“IIS://localhost/W3svc/1″)
‘Get root of Default Web Site
Set objVdirRoot = objIIS.GetObject(“IIsWebVirtualDir”, “Root”)

‘Create Virtual Directory
Set vDir = objVdirRoot.Create(“IIsWebVirtualDir”,sVDIR)
vDir.AccessRead = true
vDir.Path = sPath
vDir.SetInfo
CreateVROOT = True
End Function

“CheckVB6Build.bat (+.vbs)” script

This small collection of scripts ensures that the user / log file output records the success (or failure) of the various parts of the build. Again, the decision was taken to split off separate jobs to check that VB6, .Net 1.1 and .Net 2.0 all built correctly.  The batch file does little more than call the associated .vbs file, both of which are listed here:-

‘———————————————————-

CheckVB6Build.bat

‘———————————————————-
cscript /NoLogo %bldscripts%\CheckVB6Build.vbs BuildVB6_out.txt

if errorlevel 2 goto lblErrors

if errorlevel 1 goto lblWarnings

if errorlevel 0 goto lblGood

:lblErrors
echo Finished (with Errors)
Goto lblEnd

:lblWarnings
echo Finished (with Warnings)
Goto lblEnd

:lblGood
echo Finished (SUCCESS)
Goto lblEnd

:lblEnd
echo Building Visual Basic 6 components complete.
echo.

I hope these posts are in some way useful and explain what is going on. However, if you have any feedback regarding what is covered here, please feel free to drop me a line. In my next post I will talk about the “packaging” phase and may also cover the auto deployment phase time permitting.

One Response

  1. Hi! I was surfing and found your blog post… nice! I love your blog. :) Cheers! Sandra. R.

Leave a Reply