Batch file to precompile VS2005 web projects for deployment

A bit of a different subject this one. I am working for a client at the moment that utilises a simple yet effective batch file system for automated builds, packaging and deployment of Visual Basic 6, C++, VS2003 and VS2005 projects of all varieties. Since I had added a new web service using VS2005 I preceded to run the build and package scripts. As it turns out, the “devenv.exe” command when used in conjunction with a web project be it web site or web service will do no more than compile the solution to ensure that it is syntactically correct.

This leaves us in a precarious place. I do not want to deploy the source code to the target server since this has inherent vulnerabilities. I also do not want to suffer the penalty of dynamic compilation when the web page is first requested. I also do not want to publish the site using Visual Studio 2005 IDE as I do not necessarily have valid credentials to be able to “see” the target server for deployment. Despite best practice suggestions to use this method I would prefer to have the site “pre-compiled” so that it can be deployed using scripts by someone else who does not necessarily code in .net or have familiarity with Visual Studio.

This brings us onto the “pre-compilation” batch file. This was written using MS-DOS command language (many will know it well!) and runs on the build server. Here is the script for a typical folder structure of:-

__Scripts
Build
Package
Deploy
Stage
Other
VSNet2003
VSNet2005
etc

The VSNet2005 folder contains the .sln and project sub-folders, e.g.:-

webService.sln
Proj1
Proj2
etc

The “Stage” folder is an output folder for pre-compiled web-sites (as a result of running the script below) and typically has one site for each project folder found to be a web-site.

The pre-compilation batch file to pre-compile Visual Studio 2005 web projects resides in the “Build” directory and is given below:-

REM '
REM ‘ This script precompiles Visual Studio 2005 web sites and services to a
REM ‘ staging location under the directory called “Stage”. for the appropriate sub-system.
REM ‘
REM ‘ It doesn’t require any inputs. Aspnet_compiler will rebuild the entire site
REM ‘ as a release build. This script looks for .aspx or .asmx files anywhere
REM ' below each web root and treats their presence as defining that
REM ‘ project as a web project.
REM ‘ ————————————————————————- 
REM Initialize VS.Net 2005 Environment           

call “C:Program FilesMicrosoft Visual Studio 8Common7Toolsvsvars32.bat” > nul
echo.
echo Precompiling VS.Net 2005 Web Projects…
echo  

if exist PrecompVS2005_out.txt del PrecompVS2005_out.txt       

cd ..VSNet2005          

FOR /F “delims=” %%g IN (’DIR /ad/b’) DO CALL :process “%%g”
pause
GOTO :eof        

:process
DIR %1 /S /b | FIND /I “.asmx” > NUL
IF ERRORLEVEL 1 GOTO :Next
call :precomp %1
GOTO :endproc       

:Next
DIR %1 /S /b | FIND /I “.aspx” > NUL
IF NOT ERRORLEVEL 1 call :precomp %1
GOTO :endproc       

:precomp
rem Remove quotes around long directory names, can be added back by quoting the variable if required - see below
set str=%1
set str=%str:~1,-1%       

aspnet_compiler -p “%str%” -fixednames -v -u -f “..Stage%str%” > ..buildPrecompVS2005_out.txt       

:endproc

Batch files can seem a bit stange if you’re not used to them so this is how it works.

1. The “vsvars32.bat” file is run to provide access to the .Net 2.0 tools folders, etc. The output is directed to “nul” which prevents messages appearing on the screen

2. Change directory to position in the folder where the .Net 2.0 solution to build is located, in this case “VSNet2005″ sub-directory

3. A FOR loop is set up to process each directory name from the DIR /ad/b command that lists just directory names in bare format. Each file (/F) is then procesed. The “delims=” is important as this sets up the terminating character for each iteration of the loop against the DIR command output (the default is <tab><space> if not explicitly stated). Also note the use of %%g which is a replaceable parameter for each iteration of the loop and how it is enclosed in quotes to cope with long directory names containing spaces. Each iteration allows the result to be fed to the DO statement which makes an in-line call to a subroutine called “:process”

4.When the FOR loop is finished, there is a pause and then a GOTO end of script block

5. Inside the “:process” routine (called for each iteration), a DIRectory command is performed recursively (/s) in bare format (/b) which is piped (|) to the FIND command to perform a case insensitive match of the text to the “.aspx” string that is used to detect that this current directory is a web project

6. The FIND command returns an ERRORLEVEL of 1 if the FIND DID NOT result in a match, so the next match pattern of “.asmx” is tried

7. If a match is found the :precomp subroutine is called whereupon the quote characters enclosing the (potentialy) long filename are removed prior to passing to the aspnet_compiler utility to precompile the site to the “staging” sub-folder (in this case)

8. The site is made “updatebale” (-u) so that web.config can be changed without causing a dynamic recompile. Also note that both -v and -p need to be specified even though we are supplying a path to the web directory

I’ll leave it to the reader to explore the remaining aspnet_compiler options they might require.

I hope you may find this solution useful in some way.

2 Responses

  1. I’m looking to try automating our deploys. I have just about everything except for making Visual studios publish the way the GUI does.

    Basically my company has a lot of security measures in place and a lot of the developers do not have access to production environments. This leaves a lot of the code deploys to the administrators (ie me). I’m not familiar with Visual Studios 2005, at all. The steps of our deployment are as follows:

    Download the script from our version control software (Vault) to a folder called 1.2.3.4.
    Open folder 1.2.3.4 and open the 1.2.3.4\blah.sln

    In VisualStudios make sure the configuration is set to Debug – right click the top Main Solution and click Clean Solution.

    Go down to the Project (called web) right click and hit Publish.

    It prompts me saying ‘Where do you want to publish “Services” which is another Project (Directly above Web) and I give it a folder to publish to, then it asks me where to publish ‘Web’ which is the project I originally selected on.

    It then publishes the files to the selected local folders that I can use my batch scripts to copy to the proper servers.

    I’m having problems with the publishing of the Projects to the folders so that I can have my batch script copy to the remote locations.

    Any ideas?

  2. Hi ray,

    I am presuming that your solutions and projects have been converted to VS2005?

    What do you think is your problem when publishing, do you get an error message or does it appear to publish but give incorrect results?

    The method I outline in the above post addresses your particular problem to some degree in that, as an admin / solution deployer it dispenses with the need to use Visual Studio GUI at all for publishing web projects.

    Place the above batch script in a new folder called “Build” at the same level as your 1.2.3.4 folder and also add a new folder called “Stage” at the same level as “Build” also.

    So then from your example above you will have:-

    ___Stage
    ___Build
    ___1.2.3.4
    ______blah.sln

    Within that solution, you ideally want folders for web projects existing at the same level on disk as the blah.sln, so on disk you *SHOULD* see the following:-

    ___Stage
    ___Build
    ___1.2.3.4
    ______blah.sln
    ______Services
    ______Web

    I assume here that “Services” and “Web” are 2 independant web projects. That is not to say that each web project cannot contain sub folders of the site in the usual way because that is absolutely fine. The thing to get right here is the structure of the projects within the .sln file as shown above. This is possibly where your problems lay.

    Then you would typically change directory to the “Build” folder and run the script from there.
    To get the script to work change the “cd VSNet2005″ to “cd 1.2.3.4″ or use a batch file parameter to pass in the folder to prcompile. See above post for what happens next. You should end up with all precompiled content in the “Stage” folder like so:-

    ___Stage
    ______Services
    ______Web

    You can then copy each of the project folders to your target location. There is one big drawback here though that you did not seem to mention. Copying the project folders to the target server(s) WILL NOT create web sites/virtual roots for you. I have written another tool for this same organisation that takes the output from the precompiled Stage folder and then using XML configuration files maintained by the team will deploy the precompiled web folders to the target server creating the virtual roots, application pools, etc along the way. Without the final part you will end up having to maintain the sites in IIS manually.

    There are other methods of course. As an admin Microsoft suggest that you use their built in “Publish” option within the GUI to the target servers. If you persue this method, you want to ensure that you deploy a “Release” build. If you want the debug symbols then select these in the solution file options, I think it is the “pdb-only” option. Publishing directly will create the web sites/virtual roots that you require although I still think you will end up having to “fine tune” application pools, etc (which is why we elected to write tools to do it all across all .Net versions and VB6!).

    You can use a Web Setup project of course for each site. The develpers usually prepare these as part of the development stage. Underneath Web Setup is MSI so you have an Uninstall option on the target server as well.

    You could use the Web Copy tool in the Visual Studio GUI but this requires Front Page Server extensions on the target server and I doubt you will have that if your production environment is locked down as you say.

    There is also a “deployer” utility offered by Microsoft and there are build tasks that can be added to solutions to address builds (and depoys).

    Unless your final release scripts take care of the “deployment” steps involving configuring the web sites and virtual directories, setting up applications pools and accounts, etc you will still have to do this some other way.

    Hope this helps you and good luck.

    Si

Leave a Reply