Index: pjproject/trunk/tests/cdash/builder.py
===================================================================
--- pjproject/trunk/tests/cdash/builder.py	(revision 2403)
+++ pjproject/trunk/tests/cdash/builder.py	(revision 2404)
@@ -70,13 +70,13 @@
 #
 std_test_ops= [
-    Operation(Operation.TEST, "./pjlib-test-$SUFFIX", name="pjlib test",
+    Operation(Operation.TEST, "./pjlib-test$SUFFIX", name="pjlib test",
               wdir="pjlib/bin"),
-    Operation(Operation.TEST, "./pjlib-util-test-$SUFFIX", 
+    Operation(Operation.TEST, "./pjlib-util-test$SUFFIX", 
               name="pjlib-util test", wdir="pjlib-util/bin"),
-    Operation(Operation.TEST, "./pjnath-test-$SUFFIX", name="pjnath test",
+    Operation(Operation.TEST, "./pjnath-test$SUFFIX", name="pjnath test",
               wdir="pjnath/bin"),
-    Operation(Operation.TEST, "./pjmedia-test-$SUFFIX", name="pjmedia test",
+    Operation(Operation.TEST, "./pjmedia-test$SUFFIX", name="pjmedia test",
               wdir="pjmedia/bin"),
-    Operation(Operation.TEST, "./pjsip-test-$SUFFIX", name="pjsip test",
+    Operation(Operation.TEST, "./pjsip-test$SUFFIX", name="pjsip test",
               wdir="pjsip/bin")
 ]
@@ -86,6 +86,6 @@
 #
 gnu_build_ops = [
-    Operation(Operation.CONFIGURE, "./configure"),
-    Operation(Operation.BUILD, "make distclean; make dep && make; cd pjsip-apps/src/python; python setup.py clean build"),
+    Operation(Operation.CONFIGURE, "sh ./configure"),
+    Operation(Operation.BUILD, "sh -c 'make distclean && make dep && make && cd pjsip-apps/src/python && python setup.py clean build'"),
     #Operation(Operation.BUILD, "python setup.py clean build",
     #          wdir="pjsip-apps/src/python")
@@ -95,6 +95,10 @@
 # These are pjsua Python based unit test operations
 #
-def build_pjsua_test_ops():
+def build_pjsua_test_ops(pjsua_exe=""):
     ops = []
+    if pjsua_exe:
+        exe = " -e ../../pjsip-apps/bin/" + pjsua_exe
+    else:
+        exe = ""
     cwd = os.getcwd()
     os.chdir("../pjsua")
@@ -106,6 +110,8 @@
         name = mod[4:mod.find(".py")] + "_" + \
                param[param.find("/")+1:param.find(".py")]
-        ops.append(Operation(Operation.TEST, "python run.py " + e, name=name,
-                   wdir="tests/pjsua"))
+        ops.append(Operation(Operation.TEST, "python run.py" + exe + " " + \
+                             e, name=name, wdir="tests/pjsua"))
+    f.close()
+    os.remove("list") 
     os.chdir(cwd)
     return ops
@@ -127,4 +133,34 @@
     proc.wait()
     return "gcc-" + ver
+
+#
+# Get Visual Studio version
+#
+def vs_get_version():
+    proc = subprocess.Popen("cl", stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT)
+    while True:
+        s = proc.stdout.readline()
+        if s=="":
+            break
+        pos = s.find("Version")
+        if pos > 0:
+            proc.wait()
+            s = s[pos+8:]
+            ver = s.split(None, 1)[0]
+            major = ver[0:2]
+            if major=="12":
+                return "vs6"
+            elif major=="13":
+                return "vs2003"
+            elif major=="14":
+                return "vs2005"
+            elif major=="15":
+                return "vs2008"
+            else:
+                return "vs-" + major
+    proc.wait()
+    return "vs-unknown"
+    
 
 #
@@ -184,18 +220,12 @@
         # Restore user.mak
         name = self.config.base_dir + "/user.mak"
-        if self.saved_user_mak:
-            f = open(name, "wt")
-            f.write(self.saved_user_mak)
-            f.close()
-        else:
-            os.remove(name)
+        f = open(name, "wt")
+        f.write(self.saved_user_mak)
+        f.close()
         # Restore config_site.h
         name = self.config.base_dir + "/pjlib/include/pj/config_site.h"
-        if self.saved_config_site:
-            f = open(name, "wt")
-            f.write(self.saved_config_site)
-            f.close()
-        else:
-            os.remove(name)
+        f = open(name, "wt")
+        f.write(self.saved_config_site)
+        f.close()
 
     def build_tests(self):
@@ -223,10 +253,9 @@
                         break
             if excluded and not included:
-                print "Skipping test '%s'.." % (fullcmd)
+                if len(fullcmd)>60:
+                    fullcmd = fullcmd[0:60] + ".."
+                print "Skipping '%s'" % (fullcmd)
                 continue
 
-            #a.extend(["-o", "/tmp/xx" + a[0] + ".xml"])
-            #print a
-            #a = ["ccdash.py"].extend(a)
             b = ["ccdash.py"]
             b.extend(a)
@@ -242,6 +271,26 @@
 #
 class GNUTestBuilder(TestBuilder):
+    """\
+    This class creates list of tests suitable for GNU targets.
+
+    """
     def __init__(self, config, build_config_name="", user_mak="", \
                  config_site="", cross_compile="", exclude=[], not_exclude=[]):
+        """\
+        Parameters:
+        config              - BaseConfig instance
+        build_config_name   - Optional name to be added as suffix to the build
+                              name. Sample: "min-size", "O4", "TLS", etc.
+        user_mak            - Contents to be put on user.mak
+        config_site         - Contents to be put on config_site.h
+        cross_compile       - Optional cross-compile prefix. Must include the
+                              trailing dash, e.g. "arm-unknown-linux-"
+        exclude             - List of regular expression patterns for tests
+                              that will be excluded from the run
+        not_exclude         - List of regular expression patterns for tests
+                              that will be run regardless of whether they
+                              match the excluded pattern.
+
+        """
         TestBuilder.__init__(self, config, build_config_name=build_config_name,
                              user_mak=user_mak, config_site=config_site,
@@ -253,11 +302,13 @@
     def build_tests(self):
         if self.cross_compile:
-            suffix = self.cross_compile
-            build_name =  suffix + gcc_version(self.cross_compile + "gcc")
+            suffix = "-" + self.cross_compile[0:-1]
+            build_name =  self.cross_compile + \
+                          gcc_version(self.cross_compile + "gcc")
         else:
-            proc = subprocess.Popen(self.config.base_dir+"/config.guess",
-                                    stdout=subprocess.PIPE)
-            suffix = proc.stdout.readline().rstrip(" \r\n")
-            build_name =  suffix+"-"+gcc_version(self.cross_compile + "gcc")
+            proc = subprocess.Popen("sh "+self.config.base_dir+"/config.guess",
+                                    shell=True, stdout=subprocess.PIPE)
+            sys = proc.stdout.readline().rstrip(" \r\n")
+            build_name =  sys + "-"+gcc_version(self.cross_compile + "gcc")
+            suffix = "-" + sys
 
         if self.build_config_name:
@@ -280,3 +331,68 @@
             self.ccdash_args.append(args)
 
-
+#
+# MSVC test configurator
+#
+class MSVCTestBuilder(TestBuilder):
+    """\
+    This class creates list of tests suitable for Visual Studio builds.
+
+    """
+    def __init__(self, config, vs_config="Release|Win32", build_config_name="", 
+                 config_site="", exclude=[], not_exclude=[]):
+        """\
+        Parameters:
+        config              - BaseConfig instance
+        vs_config           - Visual Studio build configuration to build.
+                              Sample: "Debug|Win32", "Release|Win32".
+        build_config_name   - Optional name to be added as suffix to the build
+                              name. Sample: "Debug", "Release", "IPv6", etc.
+        config_site         - Contents to be put on config_site.h
+        exclude             - List of regular expression patterns for tests
+                              that will be excluded from the run
+        not_exclude         - List of regular expression patterns for tests
+                              that will be run regardless of whether they
+                              match the excluded pattern.
+
+        """
+        TestBuilder.__init__(self, config, build_config_name=build_config_name,
+                             config_site=config_site, exclude=exclude, 
+                             not_exclude=not_exclude)
+        self.vs_config = vs_config.lower()
+
+    def build_tests(self):
+       
+        (vsbuild,sys) = self.vs_config.split("|",2)
+        
+        build_name = sys + "-" + vs_get_version() + "-" + vsbuild
+
+        if self.build_config_name:
+            build_name = build_name + "-" + self.build_config_name
+
+        vccmd = "vcbuild.exe /nologo /nohtmllog /nocolor /rebuild " + \
+                "pjproject-vs8.sln " + " \"" + self.vs_config + "\""
+        
+        suffix = "-i386-win32-vc8-" + vsbuild
+        pjsua = "pjsua_vc8"
+        if vsbuild=="debug":
+            pjsua = pjsua + "d"
+        
+        cmds = []
+        cmds.extend(update_ops)
+        cmds.extend([Operation(Operation.BUILD, vccmd)])
+        cmds.extend(std_test_ops)
+        cmds.extend(build_pjsua_test_ops(pjsua))
+
+        self.ccdash_args = []
+        for c in cmds:
+            c.cmdline = c.cmdline.replace("$SUFFIX", suffix)
+            args = c.encode(self.config.base_dir)
+            args.extend(["-U", self.config.url, 
+                         "-S", self.config.site, 
+                         "-T", self.stamp(), 
+                         "-B", build_name, 
+                         "-G", self.config.group])
+            args.extend(self.config.options)
+            self.ccdash_args.append(args)
+
+
Index: pjproject/trunk/tests/cdash/cfg_msvc.py
===================================================================
--- pjproject/trunk/tests/cdash/cfg_msvc.py	(revision 2404)
+++ pjproject/trunk/tests/cdash/cfg_msvc.py	(revision 2404)
@@ -0,0 +1,69 @@
+#
+# cfg_msvc.py - MSVC/Visual Studio target configurator
+#
+# Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+import builder
+import os
+import sys
+
+# Each configurator must export this function
+def create_builder(args):
+    # (optional) args format:
+    #   [cfg_site] [--vs-config VSCFG]
+    #
+    #   cfg_site:   site configuration module. If not specified, "cfg_site" 
+    #               is implied
+    #   VSCFG:      Visual Studio build configuration to build. Sample values:
+    #               "Debug|Win32", "Release|Win32". If not specified then 
+    #               "Release|Win32" is assumed
+
+    cfg_site = "cfg_site"
+    vs_cfg = "Release|Win32"
+    in_option = ""
+    
+    for arg in args:
+        if in_option=="--vs-config":
+            vs_cfg = arg
+            in_option = ""
+        elif arg=="--vs-config":
+            in_option = arg
+        else:
+            cfg_site = arg
+        
+    if os.access(cfg_site+".py", os.F_OK) == False:
+        print "Error: file '%s.py' doesn't exist." % (cfg_site)
+        sys.exit(1)
+
+    cfg_site = __import__(cfg_site)
+    test_cfg = builder.BaseConfig(cfg_site.BASE_DIR, \
+                                  cfg_site.URL, \
+                                  cfg_site.SITE_NAME, \
+                                  cfg_site.GROUP, \
+                                  cfg_site.OPTIONS)
+
+    builders = [
+        builder.MSVCTestBuilder(test_cfg, 
+                                vs_config=vs_cfg,
+                                build_config_name="default",
+                                config_site="#define PJ_TODO(x)\n",
+                                exclude=cfg_site.EXCLUDE,
+                                not_exclude=cfg_site.NOT_EXCLUDE)
+        ]
+
+    return builders
+
