diff --git a/SConstruct b/SConstruct
index f9aef938632ca5faa87680b0e18d7bda5ab295b4..0950fa05735dc8ce882c09428efbd1f25aa71669 100644
--- a/SConstruct
+++ b/SConstruct
@@ -4,9 +4,13 @@ import os.path
 import platform
 import sys
 
+default_install_dir="/usr/local"
+if platform.system() == 'Windows':
+    default_install_dir = "build" # no obvious place for installation on Windows
+
 vars = Variables(None, ARGUMENTS)
 vars.Add(PathVariable('DESTDIR', "Root directory to install in (useful for packaging scripts)", None, PathVariable.PathIsDirCreate))
-vars.Add(PathVariable('prefix', "Where to install in the FHS", "/usr/local", PathVariable.PathAccept))
+vars.Add(PathVariable('prefix', "Where to install in the FHS", default_install_dir, PathVariable.PathAccept))
 vars.Add(ListVariable('bindings', 'Language bindings to build', 'none', ['cpp', 'dotnet', 'perl', 'php', 'python', 'ruby']))
 
 tools = ['default', 'scanreplace']
@@ -16,6 +20,9 @@ if 'dotnet' in ARGUMENTS.get('bindings', []):
 envvars = {'PATH' : os.environ['PATH']}
 if 'PKG_CONFIG_PATH' in os.environ:
     envvars['PKG_CONFIG_PATH'] = os.environ['PKG_CONFIG_PATH']
+if platform.system() == 'Windows':
+    # from the scons FAQ (keywords: LNK1104 TEMPFILE), needed by link.exe
+    envvars['TMP'] = os.environ['TMP']
 
 env = Environment(ENV = envvars,
                   variables = vars,
@@ -68,6 +75,12 @@ AddOption("--in-place",
           action="store_true",
           help="Build in-place, rather than in the build/<variant> tree")
 
+AddOption("--tests",
+          dest="with_tests",
+          default=env['PLATFORM'] != 'win32',
+          action="store_true",
+          help="Build tests")
+
 env["CC"] = os.getenv("CC") or env["CC"]
 env["CXX"] = os.getenv("CXX") or env["CXX"]
 
@@ -76,13 +89,29 @@ if os.getenv("CC") == "clang" or env['PLATFORM'] == 'darwin':
                 CXX="clang++")
 
 # Language standard and warnings
-env.MergeFlags("-std=gnu99 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-attributes -Wno-unused-variable")
+if env["CC"] == "cl":
+    env.MergeFlags("-W3 -WX")
+    env.Append(
+        CPPDEFINES=[
+            "_CRT_SECURE_NO_WARNINGS" # allow uses of sprintf
+        ],
+        CFLAGS=[
+            "-wd4018", # 'expression' : signed/unsigned mismatch
+            "-wd4244", # 'argument' : conversion from 'type1' to 'type2', possible loss of data
+            "-wd4267", # 'var' : conversion from 'size_t' to 'type', possible loss of data
+        ]
+    )
+else:
+    env.MergeFlags("-std=gnu99 -Wall -Wextra -Werror -Wno-unused-parameter -Wno-attributes -Wno-unused-variable")
 
 # Linker options
 if env['PLATFORM'] == 'darwin':
     env.Append(SHLINKFLAGS = '-install_name ' + env["libpath"] + '/${TARGET.file}')
 elif platform.system() == "OpenBSD":
     pass
+elif env['PLATFORM'] == 'win32':
+    # no extra lib needed
+    pass
 else:
     env.MergeFlags("-lrt")
 
@@ -96,10 +125,16 @@ if GetOption("coverage"):
         env.ParseConfig('llvm-config --ldflags')
 
 dbg = env.Clone(VARIANT='debug')
-dbg.Append(CCFLAGS=['-g'])
+if env["CC"] == "cl":
+    dbg.Append(CCFLAGS=["/Z7"])
+else:
+    dbg.Append(CCFLAGS=['-g'])
 
 opt = env.Clone(VARIANT='opt')
-opt.Append(CCFLAGS=["-O3"])
+if env["CC"] == "cl":
+    opt.Append(CCFLAGS=["/O2"])
+else:
+    opt.Append(CCFLAGS=["-O3"])
 
 if GetOption("variant") == 'debug':
     env = dbg
diff --git a/src/SConscript b/src/SConscript
index 7a1b9d495b1e7d3fe018ad4342da18eae4a4f9e6..414f9f46fc3e3344627139a4e0900b76177f5914 100644
--- a/src/SConscript
+++ b/src/SConscript
@@ -1,5 +1,6 @@
 # -*- python -*-
 import os.path
+
 Import('env testruns')
 
 dist_headers = [
@@ -48,7 +49,7 @@ parsers = ['parsers/%s.c'%s for s in
             'unimplemented',
             'whitespace',
             'xor',
-            'value']] 
+            'value']]
 
 backends = ['backends/%s.c' % s for s in
             ['packrat', 'llk', 'regex', 'glr', 'lalr', 'lr', 'lr0']]
@@ -63,11 +64,18 @@ misc_hammer_parts = [
     'desugar.c',
     'glue.c',
     'hammer.c',
-    'platform_bsdlike.c',
     'pprint.c',
     'registry.c',
     'system_allocator.c']
 
+if env['PLATFORM'] == 'win32':
+    misc_hammer_parts += [
+        'platform_win32.c',
+        'tsearch.c',
+    ]
+else:
+    misc_hammer_parts += ['platform_bsdlike.c']
+
 ctests = ['t_benchmark.c',
           't_bitreader.c',
           't_bitwriter.c',
@@ -76,24 +84,36 @@ ctests = ['t_benchmark.c',
           't_misc.c',
 	  't_regression.c']
 
+
+static_library_name = 'hammer'
+build_shared_library=True
+if env['PLATFORM'] == 'win32':
+    build_shared_library=False # symbols in hammer are not exported yet, this shared lib would be useless
+    static_library_name = 'hammer_s' # prevent collision between .lib from dll and .lib for static lib
+
 libhammer_shared = env.SharedLibrary('hammer', parsers + backends + misc_hammer_parts)
-libhammer_static = env.StaticLibrary('hammer', parsers + backends + misc_hammer_parts)
-Default(libhammer_shared, libhammer_static)
+libhammer_static = env.StaticLibrary(static_library_name, parsers + backends + misc_hammer_parts)
+if build_shared_library:
+    Default(libhammer_shared, libhammer_static)
+    env.Install("$libpath", [libhammer_static, libhammer_shared])
+else:
+    Default(libhammer_static)
+    env.Install("$libpath", [libhammer_static])
 
-env.Install("$libpath", [libhammer_static, libhammer_shared])
 env.Install("$incpath", dist_headers)
 env.Install("$parsersincpath", parsers_headers)
 env.Install("$backendsincpath", backends_headers)
 env.Install("$pkgconfigpath", "../../../libhammer.pc")
 
-testenv = env.Clone()
-testenv.ParseConfig('pkg-config --cflags --libs glib-2.0')
-testenv.Append(LIBS=['hammer'])
-testenv.Prepend(LIBPATH=['.'])
-ctestexec = testenv.Program('test_suite', ctests + ['test_suite.c'], LINKFLAGS="--coverage" if testenv.GetOption("coverage") else None)
-ctest = Alias('testc', [ctestexec], "".join(["env LD_LIBRARY_PATH=", os.path.dirname(ctestexec[0].path), " ", ctestexec[0].path]))
-AlwaysBuild(ctest)
-testruns.append(ctest)
+if GetOption("with_tests"):
+    testenv = env.Clone()
+    testenv.ParseConfig('pkg-config --cflags --libs glib-2.0')
+    testenv.Append(LIBS=['hammer'])
+    testenv.Prepend(LIBPATH=['.'])
+    ctestexec = testenv.Program('test_suite', ctests + ['test_suite.c'], LINKFLAGS="--coverage" if testenv.GetOption("coverage") else None)
+    ctest = Alias('testc', [ctestexec], "".join(["env LD_LIBRARY_PATH=", os.path.dirname(ctestexec[0].path), " ", ctestexec[0].path]))
+    AlwaysBuild(ctest)
+    testruns.append(ctest)
 
 Export("libhammer_static libhammer_shared")
 
diff --git a/tools/windows/build.bat b/tools/windows/build.bat
index 20f878acab296420cb8b29fa36df74aad26eeb44..2bf3901648adfabddbfa33e76bc3d0dc1bcf53a1 100644
--- a/tools/windows/build.bat
+++ b/tools/windows/build.bat
@@ -24,8 +24,8 @@ cl.exe -nologo -FC -EHsc -Z7 -Oi -GR- -Gm- %CLFLAGS% -c ^
        -Fo%BUILD%\obj\
 if %errorlevel% neq 0 goto err
 
-lib.exe %BUILD%\obj\*.obj -OUT:%BUILD%\hammer.lib
-echo STATIC_LIBRARY %BUILD%\hammer.lib
+lib.exe %BUILD%\obj\*.obj -OUT:%BUILD%\lib\hammer_s.lib
+echo STATIC_LIBRARY %BUILD%\lib\hammer_s.lib
 if %errorlevel% neq 0 goto err
 popd
 
diff --git a/tools/windows/build_examples.bat b/tools/windows/build_examples.bat
index c431faebcd29d7b1a1aaeaa77558b948fc3454f0..b6f82487b1ec47d59eb1c5cc653be3961516342a 100644
--- a/tools/windows/build_examples.bat
+++ b/tools/windows/build_examples.bat
@@ -15,7 +15,7 @@ call %HEREPATH%\clvars.bat
 echo SRC=%SRC%, BUILD=%BUILD%
 echo CLFLAGS=%CLFLAGS%
 
-set HAMMERLIB=%BUILD%\hammer.lib
+set HAMMERLIB=%BUILD%\lib\hammer_s.lib
 
 REM Now let's build some example programs