v1.5.3
diff --git a/.travis.yml b/.travis.yml
index d7bee6f..544db6f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,4 @@
 compiler:
   - gcc
   - clang
-before_install:
-  - sudo apt-get update -qq
-  - sudo apt-get install libgtest-dev
-script: ./bootstrap.py && ./configure.py --with-gtest=/usr/src/gtest && ./ninja ninja_test && ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots
+script: ./configure.py --bootstrap && ./ninja ninja_test && ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots && ./misc/ninja_syntax_test.py
diff --git a/HACKING.md b/HACKING.md
index 8e1696a..059a424 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -7,10 +7,6 @@
 Ninja your changes should be testable so it's more useful to build
 and run `ninja_test` when developing.
 
-(`./bootstrap.py` creates a bootstrap `ninja` and runs the above
-process; it's only necessary to run if you don't have a copy of
-`ninja` to build with.)
-
 ### Adjusting build flags
 
 Build in "debug" mode while developing (disables optimizations and builds
@@ -50,26 +46,6 @@
 
 ## Testing
 
-### Installing gtest
-
-The `ninja_test` binary, containing all the tests, depends on the
-googletest (gtest) library.
-
-* On older Ubuntus it'll install as libraries into `/usr/lib`:
-
-        apt-get install libgtest
-
-* On newer Ubuntus it's only distributed as source
-
-        apt-get install libgtest-dev
-        ./configure.py --with-gtest=/usr/src/gtest
-
-* Otherwise you need to download it, unpack it, and pass
-  `--with-gtest` to `configure.py`.  Get it from [its downloads
-  page](http://code.google.com/p/googletest/downloads/list); [this
-  direct download link might work
-  too](http://googletest.googlecode.com/files/gtest-1.6.0.zip).
-
 ### Test-driven development
 
 Set your build command to
@@ -146,14 +122,15 @@
 
 * Install Visual Studio (Express is fine), [Python for Windows][],
   and (if making changes) googletest (see above instructions)
-* In a Visual Studio command prompt: `python bootstrap.py`
+* In a Visual Studio command prompt: `python configure.py --bootstrap`
 
 [Python for Windows]: http://www.python.org/getit/windows/
 
 ### Via mingw on Windows (not well supported)
 
 * Install mingw, msys, and python
-* In the mingw shell, put Python in your path, and `python bootstrap.py`
+* In the mingw shell, put Python in your path, and
+  `python configure.py --bootstrap`
 * To reconfigure, run `python configure.py`
 * Remember to strip the resulting executable if size matters to you
 
@@ -167,6 +144,12 @@
 * `sudo apt-get install gcc-mingw-w64-i686 g++-mingw-w64-i686 wine`
 * `export CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ AR=i686-w64-mingw32-ar`
 
+Setup on Arch:
+* Uncomment the `[multilib]` section of `/etc/pacman.conf` and `sudo pacman -Sy`.
+* `sudo pacman -S mingw-w64-gcc wine`
+* `export CC=x86_64-w64-mingw32-cc CXX=x86_64-w64-mingw32-c++ AR=x86_64-w64-mingw32-ar`
+* `export CFLAGS=-I/usr/x86_64-w64-mingw32/include`
+
 Then run:
 * `./configure.py --platform=mingw --host=linux`
 * Build `ninja.exe` using a Linux ninja binary: `/path/to/linux/ninja`
diff --git a/README b/README
index 733ccb3..41ecdda 100644
--- a/README
+++ b/README
@@ -5,12 +5,16 @@
 doc/manual.asciidoc included in the distribution -- for background
 and more details.
 
-To build, run ./bootstrap.py.  It first blindly compiles all non-test
+To build, run ./configure.py --bootstrap.  It first compiles all non-test
 source files together, then re-builds Ninja using itself.  You should
-end up with a 'ninja' binary in the source root.  Run './ninja -h' for
-help.
+end up with a 'ninja' binary in the source root.
 
-There is no installation step.  The only file of interest to a user
-is the resulting ninja binary.
+Run './configure.py --help' for more configuration options.
+Run './ninja -h' for Ninja help.
+
+Installation is not necessary because the only required file is is the
+resulting ninja binary. However, to enable features like Bash
+completion and Emacs and Vim editing modes, some files in misc/ must be
+copied to appropriate locations.
 
 If you're interested in making changes to Ninja, read HACKING.md first.
diff --git a/bootstrap.py b/bootstrap.py
index 026396b..56eab64 100755
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -15,152 +15,9 @@
 
 from __future__ import print_function
 
-from optparse import OptionParser
-import sys
-import os
-import glob
-import errno
-import shlex
-import shutil
 import subprocess
-import platform_helper
+import sys
 
-os.chdir(os.path.dirname(os.path.abspath(__file__)))
-
-parser = OptionParser()
-
-parser.add_option('--verbose', action='store_true',
-                  help='enable verbose build',)
-parser.add_option('--x64', action='store_true',
-                  help='force 64-bit build (Windows)',)
-parser.add_option('--platform',
-                  help='target platform (' +
-                       '/'.join(platform_helper.platforms()) + ')',
-                  choices=platform_helper.platforms())
-parser.add_option('--force-pselect', action='store_true',
-                  help='ppoll() is used by default where available, '
-                       'but some platforms might need to use pselect instead',)
-(options, conf_args) = parser.parse_args()
-
-
-platform = platform_helper.Platform(options.platform)
-conf_args.append("--platform=" + platform.platform())
-
-def run(*args, **kwargs):
-    returncode = subprocess.call(*args, **kwargs)
-    if returncode != 0:
-        sys.exit(returncode)
-
-# Compute system-specific CFLAGS/LDFLAGS as used in both in the below
-# g++ call as well as in the later configure.py.
-cflags = os.environ.get('CFLAGS', '').split()
-ldflags = os.environ.get('LDFLAGS', '').split()
-if platform.is_freebsd() or platform.is_openbsd() or platform.is_bitrig():
-    cflags.append('-I/usr/local/include')
-    ldflags.append('-L/usr/local/lib')
-
-print('Building ninja manually...')
-
-try:
-    os.mkdir('build')
-except OSError:
-    e = sys.exc_info()[1]
-    if e.errno != errno.EEXIST:
-        raise
-
-sources = []
-for src in glob.glob('src/*.cc'):
-    if src.endswith('test.cc') or src.endswith('.in.cc'):
-        continue
-    if src.endswith('bench.cc'):
-        continue
-
-    filename = os.path.basename(src)
-    if filename == 'browse.cc':  # Depends on generated header.
-        continue
-
-    if platform.is_windows():
-        if src.endswith('-posix.cc'):
-            continue
-    else:
-        if src.endswith('-win32.cc'):
-            continue
-
-    sources.append(src)
-
-if platform.is_windows():
-    sources.append('src/getopt.c')
-
-if platform.is_msvc():
-    cl = 'cl'
-    vcdir = os.environ.get('VCINSTALLDIR')
-    if vcdir:
-        if options.x64:
-            cl = os.path.join(vcdir, 'bin', 'x86_amd64', 'cl.exe')
-            if not os.path.exists(cl):
-                cl = os.path.join(vcdir, 'bin', 'amd64', 'cl.exe')
-        else:
-            cl = os.path.join(vcdir, 'bin', 'cl.exe')
-    args = [cl, '/nologo', '/EHsc', '/DNOMINMAX']
-else:
-    args = shlex.split(os.environ.get('CXX', 'g++'))
-    cflags.extend(['-Wno-deprecated',
-                   '-DNINJA_PYTHON="' + sys.executable + '"',
-                   '-DNINJA_BOOTSTRAP'])
-    if platform.is_windows():
-        cflags.append('-D_WIN32_WINNT=0x0501')
-    if options.x64:
-        cflags.append('-m64')
-if (platform.is_linux() or platform.is_openbsd() or platform.is_bitrig()) and \
-        not options.force_pselect:
-    cflags.append('-DUSE_PPOLL')
-if options.force_pselect:
-    conf_args.append("--force-pselect")
-args.extend(cflags)
-args.extend(ldflags)
-binary = 'ninja.bootstrap'
-if platform.is_windows():
-    binary = 'ninja.bootstrap.exe'
-args.extend(sources)
-if platform.is_msvc():
-    args.extend(['/link', '/out:' + binary])
-else:
-    args.extend(['-o', binary])
-
-if options.verbose:
-    print(' '.join(args))
-
-try:
-    run(args)
-except:
-    print('Failure running:', args)
-    raise
-
-verbose = []
-if options.verbose:
-    verbose = ['-v']
-
-if platform.is_windows():
-    print('Building ninja using itself...')
-    run([sys.executable, 'configure.py'] + conf_args)
-    run(['./' + binary] + verbose)
-
-    # Copy the new executable over the bootstrap one.
-    shutil.copyfile('ninja.exe', binary)
-
-    # Clean up.
-    for obj in glob.glob('*.obj'):
-        os.unlink(obj)
-
-    print("""
-Done!
-
-Note: to work around Windows file locking, where you can't rebuild an
-in-use binary, to run ninja after making any changes to build ninja
-itself you should run ninja.bootstrap instead.""")
-else:
-    print('Building ninja using itself...')
-    run([sys.executable, 'configure.py'] + conf_args)
-    run(['./' + binary] + verbose)
-    os.unlink(binary)
-    print('Done!')
+print('DEPRECATED: this script will be deleted.')
+print('use "configure.py --bootstrap" instead.')
+subprocess.check_call([sys.executable, 'configure.py', '--bootstrap'])
diff --git a/configure.py b/configure.py
index 64123a0..aac4ad4 100755
--- a/configure.py
+++ b/configure.py
@@ -23,29 +23,170 @@
 
 from optparse import OptionParser
 import os
+import pipes
+import string
+import subprocess
 import sys
-import platform_helper
-sys.path.insert(0, 'misc')
 
+sys.path.insert(0, 'misc')
 import ninja_syntax
 
+
+class Platform(object):
+    """Represents a host/target platform and its specific build attributes."""
+    def __init__(self, platform):
+        self._platform = platform
+        if self._platform is not None:
+            return
+        self._platform = sys.platform
+        if self._platform.startswith('linux'):
+            self._platform = 'linux'
+        elif self._platform.startswith('freebsd'):
+            self._platform = 'freebsd'
+        elif self._platform.startswith('gnukfreebsd'):
+            self._platform = 'freebsd'
+        elif self._platform.startswith('openbsd'):
+            self._platform = 'openbsd'
+        elif self._platform.startswith('solaris') or self._platform == 'sunos5':
+            self._platform = 'solaris'
+        elif self._platform.startswith('mingw'):
+            self._platform = 'mingw'
+        elif self._platform.startswith('win'):
+            self._platform = 'msvc'
+        elif self._platform.startswith('bitrig'):
+            self._platform = 'bitrig'
+        elif self._platform.startswith('netbsd'):
+            self._platform = 'netbsd'
+
+    @staticmethod
+    def known_platforms():
+      return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5',
+              'mingw', 'msvc', 'gnukfreebsd', 'bitrig', 'netbsd']
+
+    def platform(self):
+        return self._platform
+
+    def is_linux(self):
+        return self._platform == 'linux'
+
+    def is_mingw(self):
+        return self._platform == 'mingw'
+
+    def is_msvc(self):
+        return self._platform == 'msvc'
+
+    def msvc_needs_fs(self):
+        popen = subprocess.Popen(['cl', '/nologo', '/?'],
+                                 stdout=subprocess.PIPE,
+                                 stderr=subprocess.PIPE)
+        out, err = popen.communicate()
+        return '/FS ' in str(out)
+
+    def is_windows(self):
+        return self.is_mingw() or self.is_msvc()
+
+    def is_solaris(self):
+        return self._platform == 'solaris'
+
+    def uses_usr_local(self):
+        return self._platform in ('freebsd', 'openbsd', 'bitrig')
+
+    def supports_ppoll(self):
+        return self._platform in ('linux', 'openbsd', 'bitrig')
+
+    def supports_ninja_browse(self):
+        return not self.is_windows() and not self.is_solaris()
+
+
+class Bootstrap:
+    """API shim for ninja_syntax.Writer that instead runs the commands.
+
+    Used to bootstrap Ninja from scratch.  In --bootstrap mode this
+    class is used to execute all the commands to build an executable.
+    It also proxies all calls to an underlying ninja_syntax.Writer, to
+    behave like non-bootstrap mode.
+    """
+    def __init__(self, writer):
+        self.writer = writer
+        # Map of variable name => expanded variable value.
+        self.vars = {}
+        # Map of rule name => dict of rule attributes.
+        self.rules = {
+            'phony': {}
+        }
+
+    def comment(self, text):
+        return self.writer.comment(text)
+
+    def newline(self):
+        return self.writer.newline()
+
+    def variable(self, key, val):
+        self.vars[key] = self._expand(val)
+        return self.writer.variable(key, val)
+
+    def rule(self, name, **kwargs):
+        self.rules[name] = kwargs
+        return self.writer.rule(name, **kwargs)
+
+    def build(self, outputs, rule, inputs=None, **kwargs):
+        ruleattr = self.rules[rule]
+        cmd = ruleattr.get('command')
+        if cmd is None:  # A phony rule, for example.
+            return
+
+        # Implement just enough of Ninja variable expansion etc. to
+        # make the bootstrap build work.
+        local_vars = {
+            'in': self._expand_paths(inputs),
+            'out': self._expand_paths(outputs)
+        }
+        for key, val in kwargs.get('variables', []):
+            local_vars[key] = ' '.join(ninja_syntax.as_list(val))
+
+        self._run_command(self._expand(cmd, local_vars))
+
+        return self.writer.build(outputs, rule, inputs, **kwargs)
+
+    def default(self, paths):
+        return self.writer.default(paths)
+
+    def _expand_paths(self, paths):
+        """Expand $vars in an array of paths, e.g. from a 'build' block."""
+        paths = ninja_syntax.as_list(paths)
+        return ' '.join(map(self._expand, paths))
+
+    def _expand(self, str, local_vars={}):
+        """Expand $vars in a string."""
+        return ninja_syntax.expand(str, self.vars, local_vars)
+
+    def _run_command(self, cmdline):
+        """Run a subcommand, quietly.  Prints the full command on error."""
+        try:
+            subprocess.check_call(cmdline, shell=True)
+        except subprocess.CalledProcessError, e:
+            print('when running: ', cmdline)
+            raise
+
+
 parser = OptionParser()
 profilers = ['gmon', 'pprof']
+parser.add_option('--bootstrap', action='store_true',
+                  help='bootstrap a ninja binary from nothing')
 parser.add_option('--platform',
                   help='target platform (' +
-                       '/'.join(platform_helper.platforms()) + ')',
-                  choices=platform_helper.platforms())
+                       '/'.join(Platform.known_platforms()) + ')',
+                  choices=Platform.known_platforms())
 parser.add_option('--host',
                   help='host platform (' +
-                       '/'.join(platform_helper.platforms()) + ')',
-                  choices=platform_helper.platforms())
+                       '/'.join(Platform.known_platforms()) + ')',
+                  choices=Platform.known_platforms())
 parser.add_option('--debug', action='store_true',
                   help='enable debugging extras',)
 parser.add_option('--profile', metavar='TYPE',
                   choices=profilers,
                   help='enable profiling (' + '/'.join(profilers) + ')',)
-parser.add_option('--with-gtest', metavar='PATH',
-                  help='use gtest unpacked in directory PATH')
+parser.add_option('--with-gtest', metavar='PATH', help='ignored')
 parser.add_option('--with-python', metavar='EXE',
                   help='use EXE as the Python interpreter',
                   default=os.path.basename(sys.executable))
@@ -57,15 +198,27 @@
     print('ERROR: extra unparsed command-line arguments:', args)
     sys.exit(1)
 
-platform = platform_helper.Platform(options.platform)
+platform = Platform(options.platform)
 if options.host:
-    host = platform_helper.Platform(options.host)
+    host = Platform(options.host)
 else:
     host = platform
 
 BUILD_FILENAME = 'build.ninja'
-buildfile = open(BUILD_FILENAME, 'w')
-n = ninja_syntax.Writer(buildfile)
+ninja_writer = ninja_syntax.Writer(open(BUILD_FILENAME, 'w'))
+n = ninja_writer
+
+if options.bootstrap:
+    # Make the build directory.
+    try:
+        os.mkdir('build')
+    except OSError:
+        pass
+    # Wrap ninja_writer with the Bootstrapper, which also executes the
+    # commands.
+    print('bootstrapping ninja...')
+    n = Bootstrap(n)
+
 n.comment('This file is used to build ninja itself.')
 n.comment('It is generated by ' + os.path.basename(__file__) + '.')
 n.newline()
@@ -74,11 +227,15 @@
 n.newline()
 
 n.comment('The arguments passed to configure.py, for rerunning it.')
-n.variable('configure_args', ' '.join(sys.argv[1:]))
+configure_args = sys.argv[1:]
+if '--bootstrap' in configure_args:
+    configure_args.remove('--bootstrap')
+n.variable('configure_args', ' '.join(configure_args))
 env_keys = set(['CXX', 'AR', 'CFLAGS', 'LDFLAGS'])
 configure_env = dict((k, os.environ[k]) for k in os.environ if k in env_keys)
 if configure_env:
-    config_str = ' '.join([k + '=' + configure_env[k] for k in configure_env])
+    config_str = ' '.join([k + '=' + pipes.quote(configure_env[k])
+                           for k in configure_env])
     n.variable('configure_env', config_str + '$ ')
 n.newline()
 
@@ -113,7 +270,8 @@
     n.variable('ar', configure_env.get('AR', 'ar'))
 
 if platform.is_msvc():
-    cflags = ['/nologo',  # Don't print startup banner.
+    cflags = ['/showIncludes',
+              '/nologo',  # Don't print startup banner.
               '/Zi',  # Create pdb with debug info.
               '/W4',  # Highest warning level.
               '/WX',  # Warnings as errors.
@@ -128,6 +286,10 @@
               '/DNOMINMAX', '/D_CRT_SECURE_NO_WARNINGS',
               '/D_VARIADIC_MAX=10',
               '/DNINJA_PYTHON="%s"' % options.with_python]
+    if options.bootstrap:
+        # In bootstrap mode, we have no ninja process to catch /showIncludes
+        # output.
+        cflags.remove('/showIncludes')
     if platform.msvc_needs_fs():
         cflags.append('/FS')
     ldflags = ['/DEBUG', '/libpath:$builddir']
@@ -153,12 +315,16 @@
     if platform.is_mingw():
         cflags += ['-D_WIN32_WINNT=0x0501']
     ldflags = ['-L$builddir']
+    if platform.uses_usr_local():
+        cflags.append('-I/usr/local/include')
+        ldflags.append('-L/usr/local/lib')
+
 libs = []
 
 if platform.is_mingw():
     cflags.remove('-fvisibility=hidden');
     ldflags.append('-static')
-elif platform.is_sunos5():
+elif platform.is_solaris():
     cflags.remove('-fvisibility=hidden')
 elif platform.is_msvc():
     pass
@@ -170,9 +336,10 @@
         cflags.append('-fno-omit-frame-pointer')
         libs.extend(['-Wl,--no-as-needed', '-lprofiler'])
 
-if (platform.is_linux() or platform.is_openbsd() or platform.is_bitrig()) and \
-        not options.force_pselect:
+if platform.supports_ppoll() and not options.force_pselect:
     cflags.append('-DUSE_PPOLL')
+if platform.supports_ninja_browse():
+    cflags.append('-DNINJA_HAVE_BROWSE')
 
 def shell_escape(str):
     """Escape str such that it's interpreted as a single argument by
@@ -195,9 +362,10 @@
 
 if platform.is_msvc():
     n.rule('cxx',
-        command='$cxx /showIncludes $cflags -c $in /Fo$out',
+        command='$cxx $cflags -c $in /Fo$out',
         description='CXX $out',
-        deps='msvc')
+        deps='msvc'  # /showIncludes is included in $cflags.
+    )
 else:
     n.rule('cxx',
         command='$cxx -MMD -MT $out -MF $out.d $cflags -c $in -o $out',
@@ -232,7 +400,7 @@
 
 objs = []
 
-if not platform.is_windows() and not platform.is_solaris():
+if platform.supports_ninja_browse():
     n.comment('browse_py.h is used to inline browse.py.')
     n.rule('inline',
            command='src/inline.sh $varname < $in > $out',
@@ -247,7 +415,6 @@
 
 n.comment('the depfile parser and ninja lexers are generated using re2c.')
 def has_re2c():
-    import subprocess
     try:
         proc = subprocess.Popen(['re2c', '-V'], stdout=subprocess.PIPE)
         return int(proc.communicate()[0], 10) >= 1103
@@ -316,37 +483,16 @@
 n.newline()
 all_targets += ninja
 
+if options.bootstrap:
+    # We've built the ninja binary.  Don't run any more commands
+    # through the bootstrap executor, but continue writing the
+    # build.ninja file.
+    n = ninja_writer
+
 n.comment('Tests all build into ninja_test executable.')
 
-variables = []
-test_cflags = cflags + ['-DGTEST_HAS_RTTI=0']
-test_ldflags = None
-test_libs = libs
 objs = []
-if options.with_gtest:
-    path = options.with_gtest
 
-    gtest_all_incs = '-I%s -I%s' % (path, os.path.join(path, 'include'))
-    if platform.is_msvc():
-        gtest_cflags = '/nologo /EHsc /Zi /D_VARIADIC_MAX=10 '
-        if platform.msvc_needs_fs():
-          gtest_cflags += '/FS '
-        gtest_cflags += gtest_all_incs
-    else:
-        gtest_cflags = '-fvisibility=hidden ' + gtest_all_incs
-    objs += n.build(built('gtest-all' + objext), 'cxx',
-                    os.path.join(path, 'src', 'gtest-all.cc'),
-                    variables=[('cflags', gtest_cflags)])
-
-    test_cflags.append('-I%s' % os.path.join(path, 'include'))
-else:
-    # Use gtest from system.
-    if platform.is_msvc():
-        test_libs.extend(['gtest_main.lib', 'gtest.lib'])
-    else:
-        test_libs.extend(['-lgtest_main', '-lgtest'])
-
-n.variable('test_cflags', test_cflags)
 for name in ['build_log_test',
              'build_test',
              'clean_test',
@@ -362,16 +508,13 @@
              'subprocess_test',
              'test',
              'util_test']:
-    objs += cxx(name, variables=[('cflags', '$test_cflags')])
+    objs += cxx(name)
 if platform.is_windows():
     for name in ['includes_normalize_test', 'msvc_helper_test']:
-        objs += cxx(name, variables=[('cflags', '$test_cflags')])
+        objs += cxx(name)
 
-if not platform.is_windows():
-    test_libs.append('-lpthread')
 ninja_test = n.build(binary('ninja_test'), 'link', objs, implicit=ninja_lib,
-                     variables=[('ldflags', test_ldflags),
-                                ('libs', test_libs)])
+                     variables=[('libs', libs)])
 n.newline()
 all_targets += ninja_test
 
@@ -456,4 +599,16 @@
 
 n.build('all', 'phony', all_targets)
 
+n.close()
 print('wrote %s.' % BUILD_FILENAME)
+
+if options.bootstrap:
+    print('bootstrap complete.  rebuilding...')
+    if platform.is_windows():
+        bootstrap_exe = 'ninja.bootstrap.exe'
+        if os.path.exists(bootstrap_exe):
+            os.unlink(bootstrap_exe)
+        os.rename('ninja.exe', bootstrap_exe)
+        subprocess.check_call('ninja.bootstrap.exe', shell=True)
+    else:
+        subprocess.check_call('./ninja', shell=True)
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 28fd9d3..21f4e42 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -1,7 +1,7 @@
 Ninja
 =====
 Evan Martin <martine@danga.com>
-v1.5.1, June 2014
+v1.5.3, Nov 2014
 
 
 Introduction
@@ -697,9 +697,7 @@
 
 Ninja is mostly encoding agnostic, as long as the bytes Ninja cares
 about (like slashes in paths) are ASCII.  This means e.g. UTF-8 or
-ISO-8859-1 input files ought to work.  (To simplify some code, tabs
-and carriage returns are currently disallowed; this could be fixed if
-it really mattered to you.)
+ISO-8859-1 input files ought to work.
 
 Comments begin with `#` and extend to the end of the line.
 
diff --git a/misc/bash-completion b/misc/bash-completion
index 6edf4df..0536760 100644
--- a/misc/bash-completion
+++ b/misc/bash-completion
@@ -50,7 +50,7 @@
 	    esac
 	done;
 	targets_command="eval ninja -C \"${dir}\" -t targets all"
-	targets=$((${targets_command} 2>/dev/null) | awk -F: '{print $1}')
+	targets=$(${targets_command} 2>/dev/null | awk -F: '{print $1}')
 	COMPREPLY=($(compgen -W "$targets" -- "$cur"))
     fi
     return
diff --git a/misc/ninja-mode.el b/misc/ninja-mode.el
index 36ada6f..71825d5 100644
--- a/misc/ninja-mode.el
+++ b/misc/ninja-mode.el
@@ -1,4 +1,6 @@
-;;; ninja-mode.el --- Major mode for editing .ninja files
+;;; ninja-mode.el --- Major mode for editing .ninja files -*- lexical-binding: t -*-
+
+;; Package-Requires: ((emacs "24"))
 
 ;; Copyright 2011 Google Inc. All Rights Reserved.
 ;;
@@ -22,26 +24,58 @@
 ;;; Code:
 
 (defvar ninja-keywords
-      (list
-       '("^#.*" . font-lock-comment-face)
-       (cons (concat "^" (regexp-opt '("rule" "build" "subninja" "include"
-                                       "pool" "default")
-                                     'words))
-             font-lock-keyword-face)
-       '("\\([[:alnum:]_]+\\) =" . (1 font-lock-variable-name-face))
-       ;; Variable expansion.
-       '("\\($[[:alnum:]_]+\\)" . (1 font-lock-variable-name-face))
-       ;; Rule names
-       '("rule \\([[:alnum:]_-]+\\)" . (1 font-lock-function-name-face))
-       ))
+  `((,(concat "^" (regexp-opt '("rule" "build" "subninja" "include"
+                                "pool" "default")
+                              'words))
+     . font-lock-keyword-face)
+    ("\\([[:alnum:]_]+\\) =" 1 font-lock-variable-name-face)
+    ;; Variable expansion.
+    ("$[[:alnum:]_]+" . font-lock-variable-name-face)
+    ("${[[:alnum:]._]+}" . font-lock-variable-name-face)
+    ;; Rule names
+    ("rule +\\([[:alnum:]_.-]+\\)" 1 font-lock-function-name-face)
+    ;; Build Statement - highlight the rule used,
+    ;; allow for escaped $,: in outputs.
+    ("build +\\(?:[^:$\n]\\|$[:$]\\)+ *: *\\([[:alnum:]_.-]+\\)"
+     1 font-lock-function-name-face)))
 
-;;;###autoload       
-(define-derived-mode ninja-mode fundamental-mode "ninja"
-  (setq comment-start "#")
-  ; Pass extra "t" to turn off syntax-based fontification -- we don't want
-  ; quoted strings highlighted.
-  (setq font-lock-defaults '(ninja-keywords t))
-  )
+(defvar ninja-mode-syntax-table
+  (let ((table (make-syntax-table)))
+    (modify-syntax-entry ?\" "." table)
+    table)
+  "Syntax table used in `ninja-mode'.")
+
+(defun ninja-syntax-propertize (start end)
+  (save-match-data
+    (save-excursion
+      (goto-char start)
+      (while (search-forward "#" end t)
+        (let ((match-pos (match-beginning 0)))
+          (when (and
+                 ;; Is it the first non-white character on the line?
+                 (eq match-pos (save-excursion (back-to-indentation) (point)))
+                 (save-excursion
+                   (goto-char (line-end-position 0))
+                   (or
+                    ;; If we're continuting the previous line, it's not a
+                    ;; comment.
+                    (not (eq ?$ (char-before)))
+                    ;; Except if the previous line is a comment as well, as the
+                    ;; continuation dollar is ignored then.
+                    (nth 4 (syntax-ppss)))))
+            (put-text-property match-pos (1+ match-pos) 'syntax-table '(11))
+            (let ((line-end (line-end-position)))
+              ;; Avoid putting properties past the end of the buffer.
+              ;; Otherwise we get an `args-out-of-range' error.
+              (unless (= line-end (1+ (buffer-size)))
+                (put-text-property line-end (1+ line-end) 'syntax-table '(12))))))))))
+
+;;;###autoload
+(define-derived-mode ninja-mode prog-mode "ninja"
+  (set (make-local-variable 'comment-start) "#")
+  (set (make-local-variable 'parse-sexp-lookup-properties) t)
+  (set (make-local-variable 'syntax-propertize-function) #'ninja-syntax-propertize)
+  (setq font-lock-defaults '(ninja-keywords)))
 
 ;; Run ninja-mode for files ending in .ninja.
 ;;;###autoload
diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py
index 14b932f..8673518 100644
--- a/misc/ninja_syntax.py
+++ b/misc/ninja_syntax.py
@@ -7,6 +7,7 @@
 use Python.
 """
 
+import re
 import textwrap
 
 def escape_path(word):
@@ -59,16 +60,16 @@
 
     def build(self, outputs, rule, inputs=None, implicit=None, order_only=None,
               variables=None):
-        outputs = self._as_list(outputs)
+        outputs = as_list(outputs)
         out_outputs = [escape_path(x) for x in outputs]
-        all_inputs = [escape_path(x) for x in self._as_list(inputs)]
+        all_inputs = [escape_path(x) for x in as_list(inputs)]
 
         if implicit:
-            implicit = [escape_path(x) for x in self._as_list(implicit)]
+            implicit = [escape_path(x) for x in as_list(implicit)]
             all_inputs.append('|')
             all_inputs.extend(implicit)
         if order_only:
-            order_only = [escape_path(x) for x in self._as_list(order_only)]
+            order_only = [escape_path(x) for x in as_list(order_only)]
             all_inputs.append('||')
             all_inputs.extend(order_only)
 
@@ -93,7 +94,7 @@
         self._line('subninja %s' % path)
 
     def default(self, paths):
-        self._line('default %s' % ' '.join(self._as_list(paths)))
+        self._line('default %s' % ' '.join(as_list(paths)))
 
     def _count_dollars_before_index(self, s, i):
         """Returns the number of '$' characters right in front of s[i]."""
@@ -140,12 +141,16 @@
 
         self.output.write(leading_space + text + '\n')
 
-    def _as_list(self, input):
-        if input is None:
-            return []
-        if isinstance(input, list):
-            return input
-        return [input]
+    def close(self):
+        self.output.close()
+
+
+def as_list(input):
+    if input is None:
+        return []
+    if isinstance(input, list):
+        return input
+    return [input]
 
 
 def escape(string):
@@ -154,3 +159,17 @@
     assert '\n' not in string, 'Ninja syntax does not allow newlines'
     # We only have one special metacharacter: '$'.
     return string.replace('$', '$$')
+
+
+def expand(string, vars, local_vars={}):
+    """Expand a string containing $vars as Ninja would.
+
+    Note: doesn't handle the full Ninja variable syntax, but it's enough
+    to make configure.py's use of it work.
+    """
+    def exp(m):
+        var = m.group(1)
+        if var == '$':
+            return '$'
+        return local_vars.get(var, vars.get(var, ''))
+    return re.sub(r'\$(\$|\w*)', exp, string)
diff --git a/misc/ninja_syntax_test.py b/misc/ninja_syntax_test.py
index 2aef7ff..36b2e7b 100755
--- a/misc/ninja_syntax_test.py
+++ b/misc/ninja_syntax_test.py
@@ -148,5 +148,31 @@
 ''',
                          self.out.getvalue())
 
+class TestExpand(unittest.TestCase):
+    def test_basic(self):
+        vars = {'x': 'X'}
+        self.assertEqual('foo', ninja_syntax.expand('foo', vars))
+
+    def test_var(self):
+        vars = {'xyz': 'XYZ'}
+        self.assertEqual('fooXYZ', ninja_syntax.expand('foo$xyz', vars))
+
+    def test_vars(self):
+        vars = {'x': 'X', 'y': 'YYY'}
+        self.assertEqual('XYYY', ninja_syntax.expand('$x$y', vars))
+
+    def test_space(self):
+        vars = {}
+        self.assertEqual('x y z', ninja_syntax.expand('x$ y$ z', vars))
+
+    def test_locals(self):
+        vars = {'x': 'a'}
+        local_vars = {'x': 'b'}
+        self.assertEqual('a', ninja_syntax.expand('$x', vars))
+        self.assertEqual('b', ninja_syntax.expand('$x', vars, local_vars))
+
+    def test_double(self):
+        self.assertEqual('a b$c', ninja_syntax.expand('a$ b$$c', {}))
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/misc/packaging/ninja.spec b/misc/packaging/ninja.spec
index f0c46fe..fa244a6 100644
--- a/misc/packaging/ninja.spec
+++ b/misc/packaging/ninja.spec
@@ -23,7 +23,7 @@
 
 %build
 echo Building..
-./bootstrap.py
+./configure.py --bootstrap
 ./ninja manual
 
 %install
diff --git a/misc/write_fake_manifests.py b/misc/write_fake_manifests.py
index 837007e..ca49535 100644
--- a/misc/write_fake_manifests.py
+++ b/misc/write_fake_manifests.py
@@ -182,7 +182,7 @@
 
 
 def random_targets():
-    num_targets = 800
+    num_targets = 1500
     gen = GenRandom()
 
     # N-1 static libraries, and 1 executable depending on all of them.
diff --git a/misc/zsh-completion b/misc/zsh-completion
index 2fe16fb..af24f89 100644
--- a/misc/zsh-completion
+++ b/misc/zsh-completion
@@ -17,7 +17,14 @@
 #   . path/to/ninja/misc/zsh-completion
 
 __get_targets() {
-  ninja -t targets 2>/dev/null | while read -r a b; do echo $a | cut -d ':' -f1; done;
+  dir="."
+  if [ -n "${opt_args[-C]}" ];
+  then
+    eval dir="${opt_args[-C]}"
+  fi
+  targets_command="ninja -C \"${dir}\" -t targets"
+  eval ${targets_command} 2>/dev/null | while read -r a b; do echo $a | cut -d ':' -f1; done;
+
 }
 
 __get_tools() {
diff --git a/platform_helper.py b/platform_helper.py
deleted file mode 100644
index bc3a125..0000000
--- a/platform_helper.py
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/usr/bin/env python
-# Copyright 2011 Google Inc.
-# Copyright 2013 Patrick von Reth <vonreth@kde.org>
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import sys
-
-def platforms():
-    return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5',
-            'mingw', 'msvc', 'gnukfreebsd', 'bitrig']
-
-class Platform(object):
-    def __init__(self, platform):
-        self._platform = platform
-        if not self._platform is None:
-            return
-        self._platform = sys.platform
-        if self._platform.startswith('linux'):
-            self._platform = 'linux'
-        elif self._platform.startswith('freebsd'):
-            self._platform = 'freebsd'
-        elif self._platform.startswith('gnukfreebsd'):
-            self._platform = 'freebsd'
-        elif self._platform.startswith('openbsd'):
-            self._platform = 'openbsd'
-        elif self._platform.startswith('solaris'):
-            self._platform = 'solaris'
-        elif self._platform.startswith('mingw'):
-            self._platform = 'mingw'
-        elif self._platform.startswith('win'):
-            self._platform = 'msvc'
-        elif self._platform.startswith('bitrig'):
-            self._platform = 'bitrig'
-
-    def platform(self):
-        return self._platform
-
-    def is_linux(self):
-        return self._platform == 'linux'
-
-    def is_mingw(self):
-        return self._platform == 'mingw'
-
-    def is_msvc(self):
-        return self._platform == 'msvc'
-
-    def msvc_needs_fs(self):
-        import subprocess
-        popen = subprocess.Popen(['cl', '/nologo', '/?'],
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.PIPE)
-        out, err = popen.communicate()
-        return '/FS ' in str(out)
-
-    def is_windows(self):
-        return self.is_mingw() or self.is_msvc()
-
-    def is_solaris(self):
-        return self._platform == 'solaris'
-
-    def is_freebsd(self):
-        return self._platform == 'freebsd'
-
-    def is_openbsd(self):
-        return self._platform == 'openbsd'
-
-    def is_sunos5(self):
-        return self._platform == 'sunos5'
-
-    def is_bitrig(self):
-        return self._platform == 'bitrig'
diff --git a/src/build.cc b/src/build.cc
index 64bcea3..3e74131 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -488,7 +488,9 @@
 }
 
 bool RealCommandRunner::CanRunMore() {
-  return ((int)subprocs_.running_.size()) < config_.parallelism
+  size_t subproc_number =
+      subprocs_.running_.size() + subprocs_.finished_.size();
+  return (int)subproc_number < config_.parallelism
     && ((subprocs_.running_.empty() || config_.max_load_average <= 0.0f)
         || GetLoadAverage() < config_.max_load_average);
 }
@@ -823,7 +825,11 @@
     result->output = parser.Parse(result->output, deps_prefix);
     for (set<string>::iterator i = parser.includes_.begin();
          i != parser.includes_.end(); ++i) {
-      deps_nodes->push_back(state_->GetNode(*i));
+      // ~0 is assuming that with MSVC-parsed headers, it's ok to always make
+      // all backslashes (as some of the slashes will certainly be backslashes
+      // anyway). This could be fixed if necessary with some additional
+      // complexity in IncludesNormalize::Relativize.
+      deps_nodes->push_back(state_->GetNode(*i, ~0u));
     }
   } else
 #endif
@@ -848,9 +854,11 @@
     deps_nodes->reserve(deps.ins_.size());
     for (vector<StringPiece>::iterator i = deps.ins_.begin();
          i != deps.ins_.end(); ++i) {
-      if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, err))
+      unsigned int slash_bits;
+      if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits,
+                            err))
         return false;
-      deps_nodes->push_back(state_->GetNode(*i));
+      deps_nodes->push_back(state_->GetNode(*i, slash_bits));
     }
 
     if (disk_interface_->RemoveFile(depfile) < 0) {
diff --git a/src/build_log.cc b/src/build_log.cc
index 3f24c16..b6f9874 100644
--- a/src/build_log.cc
+++ b/src/build_log.cc
@@ -102,7 +102,7 @@
 {}
 
 BuildLog::BuildLog()
-  : log_file_(NULL), needs_recompaction_(false) {}
+  : log_file_(NULL), needs_recompaction_(false), quiet_(false) {}
 
 BuildLog::~BuildLog() {
   Close();
@@ -354,7 +354,8 @@
 bool BuildLog::Recompact(const string& path, const BuildLogUser& user,
                          string* err) {
   METRIC_RECORD(".ninja_log recompact");
-  printf("Recompacting log...\n");
+  if (!quiet_)
+    printf("Recompacting log...\n");
 
   Close();
   string temp_path = path + ".recompact";
diff --git a/src/build_log.h b/src/build_log.h
index fe81a85..db0cfe0 100644
--- a/src/build_log.h
+++ b/src/build_log.h
@@ -80,6 +80,7 @@
 
   /// Rewrite the known log entries, throwing away old data.
   bool Recompact(const string& path, const BuildLogUser& user, string* err);
+  void set_quiet(bool quiet) { quiet_ = quiet; }
 
   typedef ExternalStringHashMap<LogEntry*>::Type Entries;
   const Entries& entries() const { return entries_; }
@@ -88,6 +89,7 @@
   Entries entries_;
   FILE* log_file_;
   bool needs_recompaction_;
+  bool quiet_;
 };
 
 #endif // NINJA_BUILD_LOG_H_
diff --git a/src/build_log_test.cc b/src/build_log_test.cc
index 6738c7b..7ea2117 100644
--- a/src/build_log_test.cc
+++ b/src/build_log_test.cc
@@ -17,12 +17,12 @@
 #include "util.h"
 #include "test.h"
 
+#include <sys/stat.h>
 #ifdef _WIN32
 #include <fcntl.h>
 #include <share.h>
 #else
 #include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h>
 #endif
 
@@ -290,6 +290,7 @@
   ASSERT_TRUE(log2.LookupByOutput("out"));
   ASSERT_TRUE(log2.LookupByOutput("out2"));
   // ...and force a recompaction.
+  log2.set_quiet(true);
   EXPECT_TRUE(log2.OpenForWrite(kTestFilename, *this, &err));
   log2.Close();
 
diff --git a/src/build_test.cc b/src/build_test.cc
index dad69dc..bd1cd30 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -14,6 +14,8 @@
 
 #include "build.h"
 
+#include <assert.h>
+
 #include "build_log.h"
 #include "deps_log.h"
 #include "graph.h"
@@ -506,7 +508,7 @@
   builder.command_runner_.reset(&command_runner_);
   if (!builder.AlreadyUpToDate()) {
     bool build_res = builder.Build(&err);
-    EXPECT_TRUE(build_res) << "builder.Build(&err)";
+    EXPECT_TRUE(build_res);
   }
   builder.command_runner_.release();
 }
@@ -753,23 +755,18 @@
 #ifdef _WIN32
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
                                       "build subdir\\dir2\\file: cat in1\n"));
-  EXPECT_TRUE(builder_.AddTarget("subdir\\dir2\\file", &err));
 #else
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
                                       "build subdir/dir2/file: cat in1\n"));
-  EXPECT_TRUE(builder_.AddTarget("subdir/dir2/file", &err));
 #endif
+  EXPECT_TRUE(builder_.AddTarget("subdir/dir2/file", &err));
 
   EXPECT_EQ("", err);
   EXPECT_TRUE(builder_.Build(&err));
   ASSERT_EQ("", err);
   ASSERT_EQ(2u, fs_.directories_made_.size());
   EXPECT_EQ("subdir", fs_.directories_made_[0]);
-#ifdef _WIN32
-  EXPECT_EQ("subdir\\dir2", fs_.directories_made_[1]);
-#else
   EXPECT_EQ("subdir/dir2", fs_.directories_made_[1]);
-#endif
 }
 
 TEST_F(BuildTest, DepFileMissing) {
@@ -940,6 +937,38 @@
   ASSERT_EQ("cc oo.h.in", command_runner_.commands_ran_[0]);
 }
 
+#ifdef _WIN32
+TEST_F(BuildTest, DepFileCanonicalize) {
+  string err;
+  int orig_edges = state_.edges_.size();
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule cc\n  command = cc $in\n  depfile = $out.d\n"
+"build gen/stuff\\things/foo.o: cc x\\y/z\\foo.c\n"));
+  Edge* edge = state_.edges_.back();
+
+  fs_.Create("x/y/z/foo.c", "");
+  GetNode("bar.h")->MarkDirty();  // Mark bar.h as missing.
+  // Note, different slashes from manifest.
+  fs_.Create("gen/stuff\\things/foo.o.d",
+             "gen\\stuff\\things\\foo.o: blah.h bar.h\n");
+  EXPECT_TRUE(builder_.AddTarget("gen/stuff/things/foo.o", &err));
+  ASSERT_EQ("", err);
+  ASSERT_EQ(1u, fs_.files_read_.size());
+  // The depfile path does not get Canonicalize as it seems unnecessary.
+  EXPECT_EQ("gen/stuff\\things/foo.o.d", fs_.files_read_[0]);
+
+  // Expect three new edges: one generating foo.o, and two more from
+  // loading the depfile.
+  ASSERT_EQ(orig_edges + 3, (int)state_.edges_.size());
+  // Expect our edge to now have three inputs: foo.c and two headers.
+  ASSERT_EQ(3u, edge->inputs_.size());
+
+  // Expect the command line we generate to only use the original input, and
+  // using the slashes from the manifest.
+  ASSERT_EQ("cc x\\y/z\\foo.c", edge->EvaluateCommand());
+}
+#endif
+
 TEST_F(BuildTest, Phony) {
   string err;
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
@@ -1854,7 +1883,7 @@
 
     Edge* edge = state.edges_.back();
 
-    state.GetNode("bar.h")->MarkDirty();  // Mark bar.h as missing.
+    state.GetNode("bar.h", 0)->MarkDirty();  // Mark bar.h as missing.
     EXPECT_TRUE(builder.AddTarget("fo o.o", &err));
     ASSERT_EQ("", err);
 
@@ -1872,6 +1901,72 @@
   }
 }
 
+#ifdef _WIN32
+TEST_F(BuildWithDepsLogTest, DepFileDepsLogCanonicalize) {
+  string err;
+  const char* manifest =
+      "rule cc\n  command = cc $in\n  depfile = $out.d\n  deps = gcc\n"
+      "build a/b\\c\\d/e/fo$ o.o: cc x\\y/z\\foo.c\n";
+
+  fs_.Create("x/y/z/foo.c", "");
+
+  {
+    State state;
+    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));
+
+    // Run the build once, everything should be ok.
+    DepsLog deps_log;
+    ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err));
+    ASSERT_EQ("", err);
+
+    Builder builder(&state, config_, NULL, &deps_log, &fs_);
+    builder.command_runner_.reset(&command_runner_);
+    EXPECT_TRUE(builder.AddTarget("a/b/c/d/e/fo o.o", &err));
+    ASSERT_EQ("", err);
+    // Note, different slashes from manifest.
+    fs_.Create("a/b\\c\\d/e/fo o.o.d",
+               "a\\b\\c\\d\\e\\fo\\ o.o: blah.h bar.h\n");
+    EXPECT_TRUE(builder.Build(&err));
+    EXPECT_EQ("", err);
+
+    deps_log.Close();
+    builder.command_runner_.release();
+  }
+
+  {
+    State state;
+    ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));
+
+    DepsLog deps_log;
+    ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err));
+    ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", &err));
+    ASSERT_EQ("", err);
+
+    Builder builder(&state, config_, NULL, &deps_log, &fs_);
+    builder.command_runner_.reset(&command_runner_);
+
+    Edge* edge = state.edges_.back();
+
+    state.GetNode("bar.h", 0)->MarkDirty();  // Mark bar.h as missing.
+    EXPECT_TRUE(builder.AddTarget("a/b/c/d/e/fo o.o", &err));
+    ASSERT_EQ("", err);
+
+    // Expect three new edges: one generating fo o.o, and two more from
+    // loading the depfile.
+    ASSERT_EQ(3u, state.edges_.size());
+    // Expect our edge to now have three inputs: foo.c and two headers.
+    ASSERT_EQ(3u, edge->inputs_.size());
+
+    // Expect the command line we generate to only use the original input.
+    // Note, slashes from manifest, not .d.
+    ASSERT_EQ("cc x\\y/z\\foo.c", edge->EvaluateCommand());
+
+    deps_log.Close();
+    builder.command_runner_.release();
+  }
+}
+#endif
+
 /// Check that a restat rule doesn't clear an edge if the depfile is missing.
 /// Follows from: https://github.com/martine/ninja/issues/603
 TEST_F(BuildTest, RestatMissingDepfile) {
diff --git a/src/canon_perftest.cc b/src/canon_perftest.cc
index 59bd18f..389ac24 100644
--- a/src/canon_perftest.cc
+++ b/src/canon_perftest.cc
@@ -33,8 +33,9 @@
   for (int j = 0; j < 5; ++j) {
     const int kNumRepetitions = 2000000;
     int64_t start = GetTimeMillis();
+    unsigned int slash_bits;
     for (int i = 0; i < kNumRepetitions; ++i) {
-      CanonicalizePath(buf, &len, &err);
+      CanonicalizePath(buf, &len, &slash_bits, &err);
     }
     int delta = (int)(GetTimeMillis() - start);
     times.push_back(delta);
diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc
index a5f3321..e67ef79 100644
--- a/src/depfile_parser_test.cc
+++ b/src/depfile_parser_test.cc
@@ -14,7 +14,7 @@
 
 #include "depfile_parser.h"
 
-#include <gtest/gtest.h>
+#include "test.h"
 
 struct DepfileParserTest : public testing::Test {
   bool Parse(const char* input, string* err);
diff --git a/src/deps_log.cc b/src/deps_log.cc
index 61df387..fc46497 100644
--- a/src/deps_log.cc
+++ b/src/deps_log.cc
@@ -240,7 +240,12 @@
       if (buf[path_size - 1] == '\0') --path_size;
       if (buf[path_size - 1] == '\0') --path_size;
       StringPiece path(buf, path_size);
-      Node* node = state->GetNode(path);
+      // It is not necessary to pass in a correct slash_bits here. It will
+      // either be a Node that's in the manifest (in which case it will already
+      // have a correct slash_bits that GetNode will look up), or it is an
+      // implicit dependency from a .d which does not affect the build command
+      // (and so need not have its slashes maintained).
+      Node* node = state->GetNode(path, 0);
 
       // Check that the expected index matches the actual index. This can only
       // happen if two ninja processes write to the same deps log concurrently.
@@ -302,7 +307,8 @@
 
 bool DepsLog::Recompact(const string& path, string* err) {
   METRIC_RECORD(".ninja_deps recompact");
-  printf("Recompacting deps...\n");
+  if (!quiet_)
+    printf("Recompacting deps...\n");
 
   Close();
   string temp_path = path + ".recompact";
diff --git a/src/deps_log.h b/src/deps_log.h
index cec0257..9b81bc1 100644
--- a/src/deps_log.h
+++ b/src/deps_log.h
@@ -64,7 +64,7 @@
 /// wins, allowing updates to just be appended to the file.  A separate
 /// repacking step can run occasionally to remove dead records.
 struct DepsLog {
-  DepsLog() : needs_recompaction_(false), file_(NULL) {}
+  DepsLog() : needs_recompaction_(false), quiet_(false), file_(NULL) {}
   ~DepsLog();
 
   // Writing (build-time) interface.
@@ -87,6 +87,7 @@
 
   /// Rewrite the known log entries, throwing away old data.
   bool Recompact(const string& path, string* err);
+  void set_quiet(bool quiet) { quiet_ = quiet; }
 
   /// Returns if the deps entry for a node is still reachable from the manifest.
   ///
@@ -108,6 +109,7 @@
   bool RecordId(Node* node);
 
   bool needs_recompaction_;
+  bool quiet_;
   FILE* file_;
 
   /// Maps id -> Node.
diff --git a/src/deps_log_test.cc b/src/deps_log_test.cc
index e8e5138..ac2b315 100644
--- a/src/deps_log_test.cc
+++ b/src/deps_log_test.cc
@@ -14,6 +14,11 @@
 
 #include "deps_log.h"
 
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
 #include "graph.h"
 #include "util.h"
 #include "test.h"
@@ -41,16 +46,16 @@
 
   {
     vector<Node*> deps;
-    deps.push_back(state1.GetNode("foo.h"));
-    deps.push_back(state1.GetNode("bar.h"));
-    log1.RecordDeps(state1.GetNode("out.o"), 1, deps);
+    deps.push_back(state1.GetNode("foo.h", 0));
+    deps.push_back(state1.GetNode("bar.h", 0));
+    log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps);
 
     deps.clear();
-    deps.push_back(state1.GetNode("foo.h"));
-    deps.push_back(state1.GetNode("bar2.h"));
-    log1.RecordDeps(state1.GetNode("out2.o"), 2, deps);
+    deps.push_back(state1.GetNode("foo.h", 0));
+    deps.push_back(state1.GetNode("bar2.h", 0));
+    log1.RecordDeps(state1.GetNode("out2.o", 0), 2, deps);
 
-    DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o"));
+    DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0));
     ASSERT_TRUE(log_deps);
     ASSERT_EQ(1, log_deps->mtime);
     ASSERT_EQ(2, log_deps->node_count);
@@ -74,7 +79,7 @@
   }
 
   // Spot-check the entries in log2.
-  DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out2.o"));
+  DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out2.o", 0));
   ASSERT_TRUE(log_deps);
   ASSERT_EQ(2, log_deps->mtime);
   ASSERT_EQ(2, log_deps->node_count);
@@ -96,11 +101,11 @@
     for (int i = 0; i < kNumDeps; ++i) {
       char buf[32];
       sprintf(buf, "file%d.h", i);
-      deps.push_back(state1.GetNode(buf));
+      deps.push_back(state1.GetNode(buf, 0));
     }
-    log1.RecordDeps(state1.GetNode("out.o"), 1, deps);
+    log1.RecordDeps(state1.GetNode("out.o", 0), 1, deps);
 
-    DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o"));
+    DepsLog::Deps* log_deps = log1.GetDeps(state1.GetNode("out.o", 0));
     ASSERT_EQ(kNumDeps, log_deps->node_count);
   }
 
@@ -111,7 +116,7 @@
   EXPECT_TRUE(log2.Load(kTestFilename, &state2, &err));
   ASSERT_EQ("", err);
 
-  DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out.o"));
+  DepsLog::Deps* log_deps = log2.GetDeps(state2.GetNode("out.o", 0));
   ASSERT_EQ(kNumDeps, log_deps->node_count);
 }
 
@@ -127,9 +132,9 @@
     ASSERT_EQ("", err);
 
     vector<Node*> deps;
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar.h"));
-    log.RecordDeps(state.GetNode("out.o"), 1, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar.h", 0));
+    log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
     log.Close();
 
     struct stat st;
@@ -149,9 +154,9 @@
     ASSERT_EQ("", err);
 
     vector<Node*> deps;
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar.h"));
-    log.RecordDeps(state.GetNode("out.o"), 1, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar.h", 0));
+    log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
     log.Close();
 
     struct stat st;
@@ -181,14 +186,14 @@
     ASSERT_EQ("", err);
 
     vector<Node*> deps;
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar.h"));
-    log.RecordDeps(state.GetNode("out.o"), 1, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar.h", 0));
+    log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
 
     deps.clear();
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("baz.h"));
-    log.RecordDeps(state.GetNode("other_out.o"), 1, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("baz.h", 0));
+    log.RecordDeps(state.GetNode("other_out.o", 0), 1, deps);
 
     log.Close();
 
@@ -211,8 +216,8 @@
     ASSERT_EQ("", err);
 
     vector<Node*> deps;
-    deps.push_back(state.GetNode("foo.h"));
-    log.RecordDeps(state.GetNode("out.o"), 1, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
     log.Close();
 
     struct stat st;
@@ -232,14 +237,14 @@
     string err;
     ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
 
-    Node* out = state.GetNode("out.o");
+    Node* out = state.GetNode("out.o", 0);
     DepsLog::Deps* deps = log.GetDeps(out);
     ASSERT_TRUE(deps);
     ASSERT_EQ(1, deps->mtime);
     ASSERT_EQ(1, deps->node_count);
     ASSERT_EQ("foo.h", deps->nodes[0]->path());
 
-    Node* other_out = state.GetNode("other_out.o");
+    Node* other_out = state.GetNode("other_out.o", 0);
     deps = log.GetDeps(other_out);
     ASSERT_TRUE(deps);
     ASSERT_EQ(1, deps->mtime);
@@ -247,6 +252,7 @@
     ASSERT_EQ("foo.h", deps->nodes[0]->path());
     ASSERT_EQ("baz.h", deps->nodes[1]->path());
 
+    log.set_quiet(true);
     ASSERT_TRUE(log.Recompact(kTestFilename, &err));
 
     // The in-memory deps graph should still be valid after recompaction.
@@ -281,14 +287,14 @@
     string err;
     ASSERT_TRUE(log.Load(kTestFilename, &state, &err));
 
-    Node* out = state.GetNode("out.o");
+    Node* out = state.GetNode("out.o", 0);
     DepsLog::Deps* deps = log.GetDeps(out);
     ASSERT_TRUE(deps);
     ASSERT_EQ(1, deps->mtime);
     ASSERT_EQ(1, deps->node_count);
     ASSERT_EQ("foo.h", deps->nodes[0]->path());
 
-    Node* other_out = state.GetNode("other_out.o");
+    Node* other_out = state.GetNode("other_out.o", 0);
     deps = log.GetDeps(other_out);
     ASSERT_TRUE(deps);
     ASSERT_EQ(1, deps->mtime);
@@ -296,6 +302,7 @@
     ASSERT_EQ("foo.h", deps->nodes[0]->path());
     ASSERT_EQ("baz.h", deps->nodes[1]->path());
 
+    log.set_quiet(true);
     ASSERT_TRUE(log.Recompact(kTestFilename, &err));
 
     // The previous entries should have been removed.
@@ -354,14 +361,14 @@
     ASSERT_EQ("", err);
 
     vector<Node*> deps;
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar.h"));
-    log.RecordDeps(state.GetNode("out.o"), 1, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar.h", 0));
+    log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
 
     deps.clear();
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar2.h"));
-    log.RecordDeps(state.GetNode("out2.o"), 2, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar2.h", 0));
+    log.RecordDeps(state.GetNode("out2.o", 0), 2, deps);
 
     log.Close();
   }
@@ -413,14 +420,14 @@
     ASSERT_EQ("", err);
 
     vector<Node*> deps;
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar.h"));
-    log.RecordDeps(state.GetNode("out.o"), 1, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar.h", 0));
+    log.RecordDeps(state.GetNode("out.o", 0), 1, deps);
 
     deps.clear();
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar2.h"));
-    log.RecordDeps(state.GetNode("out2.o"), 2, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar2.h", 0));
+    log.RecordDeps(state.GetNode("out2.o", 0), 2, deps);
 
     log.Close();
   }
@@ -441,16 +448,16 @@
     err.clear();
 
     // The truncated entry should've been discarded.
-    EXPECT_EQ(NULL, log.GetDeps(state.GetNode("out2.o")));
+    EXPECT_EQ(NULL, log.GetDeps(state.GetNode("out2.o", 0)));
 
     EXPECT_TRUE(log.OpenForWrite(kTestFilename, &err));
     ASSERT_EQ("", err);
 
     // Add a new entry.
     vector<Node*> deps;
-    deps.push_back(state.GetNode("foo.h"));
-    deps.push_back(state.GetNode("bar2.h"));
-    log.RecordDeps(state.GetNode("out2.o"), 3, deps);
+    deps.push_back(state.GetNode("foo.h", 0));
+    deps.push_back(state.GetNode("bar2.h", 0));
+    log.RecordDeps(state.GetNode("out2.o", 0), 3, deps);
 
     log.Close();
   }
@@ -464,7 +471,7 @@
     EXPECT_TRUE(log.Load(kTestFilename, &state, &err));
 
     // The truncated entry should exist.
-    DepsLog::Deps* deps = log.GetDeps(state.GetNode("out2.o"));
+    DepsLog::Deps* deps = log.GetDeps(state.GetNode("out2.o", 0));
     ASSERT_TRUE(deps);
   }
 }
diff --git a/src/disk_interface.cc b/src/disk_interface.cc
index b170f63..9810708 100644
--- a/src/disk_interface.cc
+++ b/src/disk_interface.cc
@@ -82,16 +82,20 @@
   return TimeStampFromFileTime(attrs.ftLastWriteTime);
 }
 
+#ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable: 4996)  // GetVersionExA is deprecated post SDK 8.1.
+#endif
 bool IsWindows7OrLater() {
   OSVERSIONINFO version_info = { sizeof(version_info) };
   if (!GetVersionEx(&version_info))
     Fatal("GetVersionEx: %s", GetLastErrorString().c_str());
   return version_info.dwMajorVersion > 6 ||
-         version_info.dwMajorVersion == 6 && version_info.dwMinorVersion >= 1;
+         (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion >= 1);
 }
+#ifdef _MSC_VER
 #pragma warning(pop)
+#endif
 
 bool StatAllFilesInDir(const string& dir, map<string, TimeStamp>* stamps,
                        bool quiet) {
diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc
index b2e8cb5..05d509c 100644
--- a/src/disk_interface_test.cc
+++ b/src/disk_interface_test.cc
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <gtest/gtest.h>
-
+#include <assert.h>
+#include <stdio.h>
 #ifdef _WIN32
 #include <io.h>
 #include <windows.h>
diff --git a/src/getopt.c b/src/getopt.c
index 75ef99c..3350fb9 100644
--- a/src/getopt.c
+++ b/src/getopt.c
@@ -299,7 +299,7 @@
           return (optopt = '?');
         }
       has_arg = ((cp[1] == ':')
-                 ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : no_argument);
+                 ? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument);
       possible_arg = argv[optind] + optwhere + 1;
       optopt = *cp;
     }
@@ -318,7 +318,7 @@
       else
         optarg = NULL;
       break;
-    case REQUIRED_ARG:
+    case required_argument:
       if (*possible_arg == '=')
         possible_arg++;
       if (*possible_arg != '\0')
diff --git a/src/getopt.h b/src/getopt.h
index ead9878..b4247fb 100644
--- a/src/getopt.h
+++ b/src/getopt.h
@@ -4,9 +4,9 @@
 /* include files needed by this include file */
 
 /* macros defined by this include file */
-#define no_argument     0
-#define REQUIRED_ARG    1
-#define OPTIONAL_ARG    2
+#define no_argument       0
+#define required_argument 1
+#define OPTIONAL_ARG      2
 
 /* types defined by this include file */
 
diff --git a/src/graph.cc b/src/graph.cc
index aa9c0e8..2829669 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -131,7 +131,7 @@
 }
 
 bool DependencyScan::RecomputeOutputsDirty(Edge* edge,
-                                           Node* most_recent_input) {   
+                                           Node* most_recent_input) {
   string command = edge->EvaluateCommand(true);
   for (vector<Node*>::iterator i = edge->outputs_.begin();
        i != edge->outputs_.end(); ++i) {
@@ -256,7 +256,7 @@
   for (vector<Node*>::iterator i = begin; i != end; ++i) {
     if (!result.empty())
       result.push_back(sep);
-    const string& path = (*i)->path();
+    const string& path = (*i)->PathDecanonicalized();
     if (escape_in_out_ == kShellEscape) {
 #if _WIN32
       GetWin32EscapedString(path, &result);
@@ -328,6 +328,20 @@
   return pool() == &State::kConsolePool;
 }
 
+string Node::PathDecanonicalized() const {
+  string result = path_;
+#ifdef _WIN32
+  unsigned int mask = 1;
+  for (char* c = &result[0]; (c = strchr(c, '/')) != NULL;) {
+    if (slash_bits_ & mask)
+      *c = '\\';
+    c++;
+    mask <<= 1;
+  }
+#endif
+  return result;
+}
+
 void Node::Dump(const char* prefix) const {
   printf("%s <%s 0x%p> mtime: %d%s, (:%s), ",
          prefix, path().c_str(), this,
@@ -379,6 +393,11 @@
     return false;
   }
 
+  unsigned int unused;
+  if (!CanonicalizePath(const_cast<char*>(depfile.out_.str_),
+                        &depfile.out_.len_, &unused, err))
+    return false;
+
   // Check that this depfile matches the edge's output.
   Node* first_output = edge->outputs_[0];
   StringPiece opath = StringPiece(first_output->path());
@@ -395,10 +414,12 @@
   // Add all its in-edges.
   for (vector<StringPiece>::iterator i = depfile.ins_.begin();
        i != depfile.ins_.end(); ++i, ++implicit_dep) {
-    if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, err))
+    unsigned int slash_bits;
+    if (!CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits,
+                          err))
       return false;
 
-    Node* node = state_->GetNode(*i);
+    Node* node = state_->GetNode(*i, slash_bits);
     *implicit_dep = node;
     node->AddOutEdge(edge);
     CreatePhonyInEdge(node);
diff --git a/src/graph.h b/src/graph.h
index 66e31b5..9aada38 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -33,8 +33,9 @@
 /// Information about a node in the dependency graph: the file, whether
 /// it's dirty, mtime, etc.
 struct Node {
-  explicit Node(const string& path)
+  Node(const string& path, unsigned int slash_bits)
       : path_(path),
+        slash_bits_(slash_bits),
         mtime_(-1),
         dirty_(false),
         in_edge_(NULL),
@@ -71,6 +72,9 @@
   }
 
   const string& path() const { return path_; }
+  /// Get |path()| but use slash_bits to convert back to original slash styles.
+  string PathDecanonicalized() const;
+  unsigned int slash_bits() const { return slash_bits_; }
   TimeStamp mtime() const { return mtime_; }
 
   bool dirty() const { return dirty_; }
@@ -90,6 +94,11 @@
 
 private:
   string path_;
+
+  /// Set bits starting from lowest for backslashes that were normalized to
+  /// forward slashes by CanonicalizePath. See |PathDecanonicalized|.
+  unsigned int slash_bits_;
+
   /// Possible values of mtime_:
   ///   -1: file hasn't been examined
   ///   0:  we looked, and file doesn't exist
diff --git a/src/graph_test.cc b/src/graph_test.cc
index 14dc678..382d352 100644
--- a/src/graph_test.cc
+++ b/src/graph_test.cc
@@ -251,3 +251,24 @@
   EXPECT_EQ(0, plan_.command_edge_count());
   ASSERT_FALSE(plan_.more_to_do());
 }
+
+#ifdef _WIN32
+TEST_F(GraphTest, Decanonicalize) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build out\\out1: cat src\\in1\n"
+"build out\\out2/out3\\out4: cat mid1\n"
+"build out3 out4\\foo: cat mid1\n"));
+
+  string err;
+  vector<Node*> root_nodes = state_.RootNodes(&err);
+  EXPECT_EQ(4u, root_nodes.size());
+  EXPECT_EQ(root_nodes[0]->path(), "out/out1");
+  EXPECT_EQ(root_nodes[1]->path(), "out/out2/out3/out4");
+  EXPECT_EQ(root_nodes[2]->path(), "out3");
+  EXPECT_EQ(root_nodes[3]->path(), "out4/foo");
+  EXPECT_EQ(root_nodes[0]->PathDecanonicalized(), "out\\out1");
+  EXPECT_EQ(root_nodes[1]->PathDecanonicalized(), "out\\out2/out3\\out4");
+  EXPECT_EQ(root_nodes[2]->PathDecanonicalized(), "out3");
+  EXPECT_EQ(root_nodes[3]->PathDecanonicalized(), "out4\\foo");
+}
+#endif
diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc
index 05ce75d..1e88a0a 100644
--- a/src/includes_normalize-win32.cc
+++ b/src/includes_normalize-win32.cc
@@ -68,12 +68,15 @@
 string IncludesNormalize::AbsPath(StringPiece s) {
   char result[_MAX_PATH];
   GetFullPathName(s.AsString().c_str(), sizeof(result), result, NULL);
+  for (char* c = result; *c; ++c)
+    if (*c == '\\')
+      *c = '/';
   return result;
 }
 
 string IncludesNormalize::Relativize(StringPiece path, const string& start) {
-  vector<string> start_list = Split(AbsPath(start), '\\');
-  vector<string> path_list = Split(AbsPath(path), '\\');
+  vector<string> start_list = Split(AbsPath(start), '/');
+  vector<string> path_list = Split(AbsPath(path), '/');
   int i;
   for (i = 0; i < static_cast<int>(min(start_list.size(), path_list.size()));
        ++i) {
@@ -88,7 +91,7 @@
     rel_list.push_back(path_list[j]);
   if (rel_list.size() == 0)
     return ".";
-  return Join(rel_list, '\\');
+  return Join(rel_list, '/');
 }
 
 string IncludesNormalize::Normalize(const string& input,
@@ -96,19 +99,17 @@
   char copy[_MAX_PATH];
   size_t len = input.size();
   strncpy(copy, input.c_str(), input.size() + 1);
-  for (size_t j = 0; j < len; ++j)
-    if (copy[j] == '/')
-      copy[j] = '\\';
   string err;
-  if (!CanonicalizePath(copy, &len, &err)) {
-    Warning("couldn't canonicalize '%s: %s\n", input.c_str(), err.c_str());
-  }
+  unsigned int slash_bits;
+  if (!CanonicalizePath(copy, &len, &slash_bits, &err))
+    Warning("couldn't canonicalize '%s': %s\n", input.c_str(), err.c_str());
+  StringPiece partially_fixed(copy, len);
+
   string curdir;
   if (!relative_to) {
     curdir = AbsPath(".");
     relative_to = curdir.c_str();
   }
-  StringPiece partially_fixed(copy, len);
   if (!SameDrive(partially_fixed, relative_to))
     return partially_fixed.AsString();
   return Relativize(partially_fixed, relative_to);
diff --git a/src/includes_normalize.h b/src/includes_normalize.h
index 43527af..634fef3 100644
--- a/src/includes_normalize.h
+++ b/src/includes_normalize.h
@@ -29,7 +29,6 @@
   static string Relativize(StringPiece path, const string& start);
 
   /// Normalize by fixing slashes style, fixing redundant .. and . and makes the
-  /// path relative to |relative_to|. Case is normalized to lowercase on
-  /// Windows too.
+  /// path relative to |relative_to|.
   static string Normalize(const string& input, const char* relative_to);
 };
diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc
index 419996f..c4c2476 100644
--- a/src/includes_normalize_test.cc
+++ b/src/includes_normalize_test.cc
@@ -14,7 +14,7 @@
 
 #include "includes_normalize.h"
 
-#include <gtest/gtest.h>
+#include <direct.h>
 
 #include "test.h"
 #include "util.h"
@@ -22,8 +22,8 @@
 TEST(IncludesNormalize, Simple) {
   EXPECT_EQ("b", IncludesNormalize::Normalize("a\\..\\b", NULL));
   EXPECT_EQ("b", IncludesNormalize::Normalize("a\\../b", NULL));
-  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("a\\.\\b", NULL));
-  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("a\\./b", NULL));
+  EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\.\\b", NULL));
+  EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\./b", NULL));
 }
 
 namespace {
@@ -42,21 +42,21 @@
   EXPECT_EQ("c", IncludesNormalize::Normalize("a/b/c", "a/b"));
   EXPECT_EQ("a", IncludesNormalize::Normalize(IncludesNormalize::AbsPath("a"),
                                               NULL));
-  EXPECT_EQ(string("..\\") + currentdir + string("\\a"),
+  EXPECT_EQ(string("../") + currentdir + string("/a"),
             IncludesNormalize::Normalize("a", "../b"));
-  EXPECT_EQ(string("..\\") + currentdir + string("\\a\\b"),
+  EXPECT_EQ(string("../") + currentdir + string("/a/b"),
             IncludesNormalize::Normalize("a/b", "../c"));
-  EXPECT_EQ("..\\..\\a", IncludesNormalize::Normalize("a", "b/c"));
+  EXPECT_EQ("../../a", IncludesNormalize::Normalize("a", "b/c"));
   EXPECT_EQ(".", IncludesNormalize::Normalize("a", "a"));
 }
 
 TEST(IncludesNormalize, Case) {
   EXPECT_EQ("b", IncludesNormalize::Normalize("Abc\\..\\b", NULL));
   EXPECT_EQ("BdEf", IncludesNormalize::Normalize("Abc\\..\\BdEf", NULL));
-  EXPECT_EQ("A\\b", IncludesNormalize::Normalize("A\\.\\b", NULL));
-  EXPECT_EQ("a\\b", IncludesNormalize::Normalize("a\\./b", NULL));
-  EXPECT_EQ("A\\B", IncludesNormalize::Normalize("A\\.\\B", NULL));
-  EXPECT_EQ("A\\B", IncludesNormalize::Normalize("A\\./B", NULL));
+  EXPECT_EQ("A/b", IncludesNormalize::Normalize("A\\.\\b", NULL));
+  EXPECT_EQ("a/b", IncludesNormalize::Normalize("a\\./b", NULL));
+  EXPECT_EQ("A/B", IncludesNormalize::Normalize("A\\.\\B", NULL));
+  EXPECT_EQ("A/B", IncludesNormalize::Normalize("A\\./B", NULL));
 }
 
 TEST(IncludesNormalize, Join) {
@@ -92,13 +92,13 @@
       IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "p:\\vs08"));
   EXPECT_EQ("stuff.h",
       IncludesNormalize::Normalize("P:\\Vs08\\stuff.h", "p:\\vs08"));
-  EXPECT_EQ("p:\\vs08\\stuff.h",
+  EXPECT_EQ("p:/vs08/stuff.h",
       IncludesNormalize::Normalize("p:\\vs08\\stuff.h", "c:\\vs08"));
-  EXPECT_EQ("P:\\vs08\\stufF.h",
+  EXPECT_EQ("P:/vs08/stufF.h",
       IncludesNormalize::Normalize("P:\\vs08\\stufF.h", "D:\\stuff/things"));
-  EXPECT_EQ("P:\\vs08\\stuff.h",
+  EXPECT_EQ("P:/vs08/stuff.h",
       IncludesNormalize::Normalize("P:/vs08\\stuff.h", "D:\\stuff/things"));
-  // TODO: this fails; fix it.
-  //EXPECT_EQ("P:\\wee\\stuff.h",
-  //    IncludesNormalize::Normalize("P:/vs08\\../wee\\stuff.h", "D:\\stuff/things"));
+  EXPECT_EQ("P:/wee/stuff.h",
+            IncludesNormalize::Normalize("P:/vs08\\../wee\\stuff.h",
+                                         "D:\\stuff/things"));
 }
diff --git a/src/lexer.cc b/src/lexer.cc
index 685fe81..37b8678 100644
--- a/src/lexer.cc
+++ b/src/lexer.cc
@@ -103,8 +103,6 @@
 string Lexer::DescribeLastError() {
   if (last_token_) {
     switch (last_token_[0]) {
-    case '\r':
-      return "carriage returns are not allowed, use newlines";
     case '\t':
       return "tabs are not allowed, use spaces";
     }
@@ -129,7 +127,7 @@
 	unsigned int yyaccept = 0;
 	static const unsigned char yybm[] = {
 		  0,  64,  64,  64,  64,  64,  64,  64, 
-		 64,  64,   0,  64,  64,   0,  64,  64, 
+		 64,  64,   0,  64,  64,  64,  64,  64, 
 		 64,  64,  64,  64,  64,  64,  64,  64, 
 		 64,  64,  64,  64,  64,  64,  64,  64, 
 		192,  64,  64,  64,  64,  64,  64,  64, 
@@ -163,56 +161,66 @@
 	};
 
 	yych = *p;
-	if (yych <= '^') {
-		if (yych <= ',') {
-			if (yych <= 0x1F) {
-				if (yych <= 0x00) goto yy22;
-				if (yych == '\n') goto yy6;
-				goto yy24;
+	if (yych <= 'Z') {
+		if (yych <= '#') {
+			if (yych <= '\f') {
+				if (yych <= 0x00) goto yy23;
+				if (yych == '\n') goto yy7;
+				goto yy25;
 			} else {
-				if (yych <= ' ') goto yy2;
-				if (yych == '#') goto yy4;
-				goto yy24;
+				if (yych <= 0x1F) {
+					if (yych <= '\r') goto yy6;
+					goto yy25;
+				} else {
+					if (yych <= ' ') goto yy2;
+					if (yych <= '"') goto yy25;
+					goto yy4;
+				}
 			}
 		} else {
-			if (yych <= ':') {
-				if (yych == '/') goto yy24;
-				if (yych <= '9') goto yy21;
-				goto yy15;
+			if (yych <= '9') {
+				if (yych <= ',') goto yy25;
+				if (yych == '/') goto yy25;
+				goto yy22;
 			} else {
-				if (yych <= '=') {
-					if (yych <= '<') goto yy24;
-					goto yy13;
+				if (yych <= '<') {
+					if (yych <= ':') goto yy16;
+					goto yy25;
 				} else {
-					if (yych <= '@') goto yy24;
-					if (yych <= 'Z') goto yy21;
-					goto yy24;
+					if (yych <= '=') goto yy14;
+					if (yych <= '@') goto yy25;
+					goto yy22;
 				}
 			}
 		}
 	} else {
 		if (yych <= 'i') {
-			if (yych <= 'b') {
-				if (yych == '`') goto yy24;
-				if (yych <= 'a') goto yy21;
-				goto yy8;
+			if (yych <= 'a') {
+				if (yych == '_') goto yy22;
+				if (yych <= '`') goto yy25;
+				goto yy22;
 			} else {
-				if (yych == 'd') goto yy12;
-				if (yych <= 'h') goto yy21;
-				goto yy19;
+				if (yych <= 'c') {
+					if (yych <= 'b') goto yy9;
+					goto yy22;
+				} else {
+					if (yych <= 'd') goto yy13;
+					if (yych <= 'h') goto yy22;
+					goto yy20;
+				}
 			}
 		} else {
 			if (yych <= 'r') {
-				if (yych == 'p') goto yy10;
-				if (yych <= 'q') goto yy21;
-				goto yy11;
+				if (yych == 'p') goto yy11;
+				if (yych <= 'q') goto yy22;
+				goto yy12;
 			} else {
 				if (yych <= 'z') {
-					if (yych <= 's') goto yy20;
-					goto yy21;
+					if (yych <= 's') goto yy21;
+					goto yy22;
 				} else {
-					if (yych == '|') goto yy17;
-					goto yy24;
+					if (yych == '|') goto yy18;
+					goto yy25;
 				}
 			}
 		}
@@ -220,192 +228,203 @@
 yy2:
 	yyaccept = 0;
 	yych = *(q = ++p);
-	goto yy70;
+	goto yy73;
 yy3:
 	{ token = INDENT;   break; }
 yy4:
 	yyaccept = 1;
 	yych = *(q = ++p);
-	if (yych <= 0x00) goto yy5;
-	if (yych != '\r') goto yy65;
+	if (yych >= 0x01) goto yy68;
 yy5:
 	{ token = ERROR;    break; }
 yy6:
-	++p;
-yy7:
-	{ token = NEWLINE;  break; }
-yy8:
-	++p;
-	if ((yych = *p) == 'u') goto yy59;
-	goto yy26;
-yy9:
-	{ token = IDENT;    break; }
-yy10:
 	yych = *++p;
-	if (yych == 'o') goto yy55;
-	goto yy26;
+	if (yych == '\n') goto yy65;
+	goto yy5;
+yy7:
+	++p;
+yy8:
+	{ token = NEWLINE;  break; }
+yy9:
+	++p;
+	if ((yych = *p) == 'u') goto yy60;
+	goto yy27;
+yy10:
+	{ token = IDENT;    break; }
 yy11:
 	yych = *++p;
-	if (yych == 'u') goto yy51;
-	goto yy26;
+	if (yych == 'o') goto yy56;
+	goto yy27;
 yy12:
 	yych = *++p;
-	if (yych == 'e') goto yy44;
-	goto yy26;
+	if (yych == 'u') goto yy52;
+	goto yy27;
 yy13:
+	yych = *++p;
+	if (yych == 'e') goto yy45;
+	goto yy27;
+yy14:
 	++p;
 	{ token = EQUALS;   break; }
-yy15:
+yy16:
 	++p;
 	{ token = COLON;    break; }
-yy17:
+yy18:
 	++p;
-	if ((yych = *p) == '|') goto yy42;
+	if ((yych = *p) == '|') goto yy43;
 	{ token = PIPE;     break; }
-yy19:
-	yych = *++p;
-	if (yych == 'n') goto yy35;
-	goto yy26;
 yy20:
 	yych = *++p;
-	if (yych == 'u') goto yy27;
-	goto yy26;
+	if (yych == 'n') goto yy36;
+	goto yy27;
 yy21:
 	yych = *++p;
-	goto yy26;
+	if (yych == 'u') goto yy28;
+	goto yy27;
 yy22:
+	yych = *++p;
+	goto yy27;
+yy23:
 	++p;
 	{ token = TEOF;     break; }
-yy24:
+yy25:
 	yych = *++p;
 	goto yy5;
-yy25:
+yy26:
 	++p;
 	yych = *p;
-yy26:
-	if (yybm[0+yych] & 32) {
-		goto yy25;
-	}
-	goto yy9;
 yy27:
+	if (yybm[0+yych] & 32) {
+		goto yy26;
+	}
+	goto yy10;
+yy28:
 	yych = *++p;
-	if (yych != 'b') goto yy26;
+	if (yych != 'b') goto yy27;
 	yych = *++p;
-	if (yych != 'n') goto yy26;
+	if (yych != 'n') goto yy27;
 	yych = *++p;
-	if (yych != 'i') goto yy26;
+	if (yych != 'i') goto yy27;
 	yych = *++p;
-	if (yych != 'n') goto yy26;
+	if (yych != 'n') goto yy27;
 	yych = *++p;
-	if (yych != 'j') goto yy26;
+	if (yych != 'j') goto yy27;
 	yych = *++p;
-	if (yych != 'a') goto yy26;
+	if (yych != 'a') goto yy27;
 	++p;
 	if (yybm[0+(yych = *p)] & 32) {
-		goto yy25;
+		goto yy26;
 	}
 	{ token = SUBNINJA; break; }
-yy35:
+yy36:
 	yych = *++p;
-	if (yych != 'c') goto yy26;
+	if (yych != 'c') goto yy27;
 	yych = *++p;
-	if (yych != 'l') goto yy26;
+	if (yych != 'l') goto yy27;
 	yych = *++p;
-	if (yych != 'u') goto yy26;
+	if (yych != 'u') goto yy27;
 	yych = *++p;
-	if (yych != 'd') goto yy26;
+	if (yych != 'd') goto yy27;
 	yych = *++p;
-	if (yych != 'e') goto yy26;
+	if (yych != 'e') goto yy27;
 	++p;
 	if (yybm[0+(yych = *p)] & 32) {
-		goto yy25;
+		goto yy26;
 	}
 	{ token = INCLUDE;  break; }
-yy42:
+yy43:
 	++p;
 	{ token = PIPE2;    break; }
-yy44:
+yy45:
 	yych = *++p;
-	if (yych != 'f') goto yy26;
+	if (yych != 'f') goto yy27;
 	yych = *++p;
-	if (yych != 'a') goto yy26;
+	if (yych != 'a') goto yy27;
 	yych = *++p;
-	if (yych != 'u') goto yy26;
+	if (yych != 'u') goto yy27;
 	yych = *++p;
-	if (yych != 'l') goto yy26;
+	if (yych != 'l') goto yy27;
 	yych = *++p;
-	if (yych != 't') goto yy26;
+	if (yych != 't') goto yy27;
 	++p;
 	if (yybm[0+(yych = *p)] & 32) {
-		goto yy25;
+		goto yy26;
 	}
 	{ token = DEFAULT;  break; }
-yy51:
+yy52:
 	yych = *++p;
-	if (yych != 'l') goto yy26;
+	if (yych != 'l') goto yy27;
 	yych = *++p;
-	if (yych != 'e') goto yy26;
+	if (yych != 'e') goto yy27;
 	++p;
 	if (yybm[0+(yych = *p)] & 32) {
-		goto yy25;
+		goto yy26;
 	}
 	{ token = RULE;     break; }
-yy55:
+yy56:
 	yych = *++p;
-	if (yych != 'o') goto yy26;
+	if (yych != 'o') goto yy27;
 	yych = *++p;
-	if (yych != 'l') goto yy26;
+	if (yych != 'l') goto yy27;
 	++p;
 	if (yybm[0+(yych = *p)] & 32) {
-		goto yy25;
+		goto yy26;
 	}
 	{ token = POOL;     break; }
-yy59:
+yy60:
 	yych = *++p;
-	if (yych != 'i') goto yy26;
+	if (yych != 'i') goto yy27;
 	yych = *++p;
-	if (yych != 'l') goto yy26;
+	if (yych != 'l') goto yy27;
 	yych = *++p;
-	if (yych != 'd') goto yy26;
+	if (yych != 'd') goto yy27;
 	++p;
 	if (yybm[0+(yych = *p)] & 32) {
-		goto yy25;
+		goto yy26;
 	}
 	{ token = BUILD;    break; }
-yy64:
+yy65:
+	++p;
+	{ token = NEWLINE;  break; }
+yy67:
 	++p;
 	yych = *p;
-yy65:
+yy68:
 	if (yybm[0+yych] & 64) {
-		goto yy64;
+		goto yy67;
 	}
-	if (yych <= 0x00) goto yy66;
-	if (yych <= '\f') goto yy67;
-yy66:
+	if (yych >= 0x01) goto yy70;
+yy69:
 	p = q;
 	if (yyaccept <= 0) {
 		goto yy3;
 	} else {
 		goto yy5;
 	}
-yy67:
+yy70:
 	++p;
 	{ continue; }
-yy69:
+yy72:
 	yyaccept = 0;
 	q = ++p;
 	yych = *p;
-yy70:
+yy73:
 	if (yybm[0+yych] & 128) {
-		goto yy69;
+		goto yy72;
 	}
-	if (yych == '\n') goto yy71;
-	if (yych == '#') goto yy64;
-	goto yy3;
-yy71:
+	if (yych <= '\f') {
+		if (yych != '\n') goto yy3;
+	} else {
+		if (yych <= '\r') goto yy75;
+		if (yych == '#') goto yy67;
+		goto yy3;
+	}
+	yych = *++p;
+	goto yy8;
+yy75:
 	++p;
-	yych = *p;
-	goto yy7;
+	if ((yych = *p) == '\n') goto yy65;
+	goto yy69;
 }
 
   }
@@ -427,6 +446,7 @@
 
 void Lexer::EatWhitespace() {
   const char* p = ofs_;
+  const char* q;
   for (;;) {
     ofs_ = p;
     
@@ -468,39 +488,48 @@
 	};
 	yych = *p;
 	if (yych <= ' ') {
-		if (yych <= 0x00) goto yy78;
-		if (yych <= 0x1F) goto yy80;
+		if (yych <= 0x00) goto yy82;
+		if (yych <= 0x1F) goto yy84;
 	} else {
-		if (yych == '$') goto yy76;
-		goto yy80;
+		if (yych == '$') goto yy80;
+		goto yy84;
 	}
 	++p;
 	yych = *p;
-	goto yy84;
-yy75:
+	goto yy92;
+yy79:
 	{ continue; }
-yy76:
-	++p;
-	if ((yych = *p) == '\n') goto yy81;
-yy77:
-	{ break; }
-yy78:
-	++p;
-	{ break; }
 yy80:
-	yych = *++p;
-	goto yy77;
+	yych = *(q = ++p);
+	if (yych == '\n') goto yy85;
+	if (yych == '\r') goto yy87;
 yy81:
+	{ break; }
+yy82:
+	++p;
+	{ break; }
+yy84:
+	yych = *++p;
+	goto yy81;
+yy85:
 	++p;
 	{ continue; }
-yy83:
+yy87:
+	yych = *++p;
+	if (yych == '\n') goto yy89;
+	p = q;
+	goto yy81;
+yy89:
+	++p;
+	{ continue; }
+yy91:
 	++p;
 	yych = *p;
-yy84:
+yy92:
 	if (yybm[0+yych] & 128) {
-		goto yy83;
+		goto yy91;
 	}
-	goto yy75;
+	goto yy79;
 }
 
   }
@@ -550,40 +579,40 @@
 	yych = *p;
 	if (yych <= '@') {
 		if (yych <= '.') {
-			if (yych <= ',') goto yy89;
+			if (yych <= ',') goto yy97;
 		} else {
-			if (yych <= '/') goto yy89;
-			if (yych >= ':') goto yy89;
+			if (yych <= '/') goto yy97;
+			if (yych >= ':') goto yy97;
 		}
 	} else {
 		if (yych <= '_') {
-			if (yych <= 'Z') goto yy87;
-			if (yych <= '^') goto yy89;
+			if (yych <= 'Z') goto yy95;
+			if (yych <= '^') goto yy97;
 		} else {
-			if (yych <= '`') goto yy89;
-			if (yych >= '{') goto yy89;
+			if (yych <= '`') goto yy97;
+			if (yych >= '{') goto yy97;
 		}
 	}
-yy87:
+yy95:
 	++p;
 	yych = *p;
-	goto yy92;
-yy88:
+	goto yy100;
+yy96:
 	{
       out->assign(start, p - start);
       break;
     }
-yy89:
+yy97:
 	++p;
 	{ return false; }
-yy91:
+yy99:
 	++p;
 	yych = *p;
-yy92:
+yy100:
 	if (yybm[0+yych] & 128) {
-		goto yy91;
+		goto yy99;
 	}
-	goto yy88;
+	goto yy96;
 }
 
   }
@@ -638,29 +667,36 @@
 	yych = *p;
 	if (yych <= ' ') {
 		if (yych <= '\n') {
-			if (yych <= 0x00) goto yy101;
-			if (yych >= '\n') goto yy97;
+			if (yych <= 0x00) goto yy110;
+			if (yych >= '\n') goto yy107;
 		} else {
-			if (yych == '\r') goto yy103;
-			if (yych >= ' ') goto yy97;
+			if (yych == '\r') goto yy105;
+			if (yych >= ' ') goto yy107;
 		}
 	} else {
 		if (yych <= '9') {
-			if (yych == '$') goto yy99;
+			if (yych == '$') goto yy109;
 		} else {
-			if (yych <= ':') goto yy97;
-			if (yych == '|') goto yy97;
+			if (yych <= ':') goto yy107;
+			if (yych == '|') goto yy107;
 		}
 	}
 	++p;
 	yych = *p;
-	goto yy126;
-yy96:
+	goto yy140;
+yy104:
 	{
       eval->AddText(StringPiece(start, p - start));
       continue;
     }
-yy97:
+yy105:
+	++p;
+	if ((yych = *p) == '\n') goto yy137;
+	{
+      last_token_ = start;
+      return Error(DescribeLastError(), err);
+    }
+yy107:
 	++p;
 	{
       if (path) {
@@ -673,137 +709,152 @@
         continue;
       }
     }
-yy99:
-	++p;
-	if ((yych = *p) <= '/') {
-		if (yych <= ' ') {
-			if (yych == '\n') goto yy115;
-			if (yych <= 0x1F) goto yy104;
-			goto yy106;
-		} else {
-			if (yych <= '$') {
-				if (yych <= '#') goto yy104;
-				goto yy108;
+yy109:
+	yych = *++p;
+	if (yych <= '-') {
+		if (yych <= 0x1F) {
+			if (yych <= '\n') {
+				if (yych <= '\t') goto yy112;
+				goto yy124;
 			} else {
-				if (yych == '-') goto yy110;
-				goto yy104;
+				if (yych == '\r') goto yy114;
+				goto yy112;
+			}
+		} else {
+			if (yych <= '#') {
+				if (yych <= ' ') goto yy115;
+				goto yy112;
+			} else {
+				if (yych <= '$') goto yy117;
+				if (yych <= ',') goto yy112;
+				goto yy119;
 			}
 		}
 	} else {
-		if (yych <= '^') {
-			if (yych <= ':') {
-				if (yych <= '9') goto yy110;
-				goto yy112;
+		if (yych <= 'Z') {
+			if (yych <= '9') {
+				if (yych <= '/') goto yy112;
+				goto yy119;
 			} else {
-				if (yych <= '@') goto yy104;
-				if (yych <= 'Z') goto yy110;
-				goto yy104;
+				if (yych <= ':') goto yy121;
+				if (yych <= '@') goto yy112;
+				goto yy119;
 			}
 		} else {
 			if (yych <= '`') {
-				if (yych <= '_') goto yy110;
-				goto yy104;
+				if (yych == '_') goto yy119;
+				goto yy112;
 			} else {
-				if (yych <= 'z') goto yy110;
-				if (yych <= '{') goto yy114;
-				goto yy104;
+				if (yych <= 'z') goto yy119;
+				if (yych <= '{') goto yy123;
+				goto yy112;
 			}
 		}
 	}
-yy100:
-	{
-      last_token_ = start;
-      return Error(DescribeLastError(), err);
-    }
-yy101:
+yy110:
 	++p;
 	{
       last_token_ = start;
       return Error("unexpected EOF", err);
     }
-yy103:
-	yych = *++p;
-	goto yy100;
-yy104:
+yy112:
 	++p;
-yy105:
+yy113:
 	{
       last_token_ = start;
       return Error("bad $-escape (literal $ must be written as $$)", err);
     }
-yy106:
+yy114:
+	yych = *++p;
+	if (yych == '\n') goto yy134;
+	goto yy113;
+yy115:
 	++p;
 	{
       eval->AddText(StringPiece(" ", 1));
       continue;
     }
-yy108:
+yy117:
 	++p;
 	{
       eval->AddText(StringPiece("$", 1));
       continue;
     }
-yy110:
+yy119:
 	++p;
 	yych = *p;
-	goto yy124;
-yy111:
+	goto yy133;
+yy120:
 	{
       eval->AddSpecial(StringPiece(start + 1, p - start - 1));
       continue;
     }
-yy112:
+yy121:
 	++p;
 	{
       eval->AddText(StringPiece(":", 1));
       continue;
     }
-yy114:
+yy123:
 	yych = *(q = ++p);
 	if (yybm[0+yych] & 32) {
-		goto yy118;
+		goto yy127;
 	}
-	goto yy105;
-yy115:
+	goto yy113;
+yy124:
 	++p;
 	yych = *p;
 	if (yybm[0+yych] & 16) {
-		goto yy115;
+		goto yy124;
 	}
 	{
       continue;
     }
-yy118:
+yy127:
 	++p;
 	yych = *p;
 	if (yybm[0+yych] & 32) {
-		goto yy118;
+		goto yy127;
 	}
-	if (yych == '}') goto yy121;
+	if (yych == '}') goto yy130;
 	p = q;
-	goto yy105;
-yy121:
+	goto yy113;
+yy130:
 	++p;
 	{
       eval->AddSpecial(StringPiece(start + 2, p - start - 3));
       continue;
     }
-yy123:
+yy132:
 	++p;
 	yych = *p;
-yy124:
+yy133:
 	if (yybm[0+yych] & 64) {
-		goto yy123;
+		goto yy132;
 	}
-	goto yy111;
-yy125:
+	goto yy120;
+yy134:
 	++p;
 	yych = *p;
-yy126:
+	if (yych == ' ') goto yy134;
+	{
+      continue;
+    }
+yy137:
+	++p;
+	{
+      if (path)
+        p = start;
+      break;
+    }
+yy139:
+	++p;
+	yych = *p;
+yy140:
 	if (yybm[0+yych] & 128) {
-		goto yy125;
+		goto yy139;
 	}
-	goto yy96;
+	goto yy104;
 }
 
   }
diff --git a/src/lexer.in.cc b/src/lexer.in.cc
index 93d5540..f861239 100644
--- a/src/lexer.in.cc
+++ b/src/lexer.in.cc
@@ -102,8 +102,6 @@
 string Lexer::DescribeLastError() {
   if (last_token_) {
     switch (last_token_[0]) {
-    case '\r':
-      return "carriage returns are not allowed, use newlines";
     case '\t':
       return "tabs are not allowed, use spaces";
     }
@@ -132,8 +130,9 @@
     simple_varname = [a-zA-Z0-9_-]+;
     varname = [a-zA-Z0-9_.-]+;
 
-    [ ]*"#"[^\000\r\n]*"\n" { continue; }
-    [ ]*[\n]   { token = NEWLINE;  break; }
+    [ ]*"#"[^\000\n]*"\n" { continue; }
+    [ ]*"\r\n" { token = NEWLINE;  break; }
+    [ ]*"\n"   { token = NEWLINE;  break; }
     [ ]+       { token = INDENT;   break; }
     "build"    { token = BUILD;    break; }
     "pool"     { token = POOL;     break; }
@@ -168,13 +167,15 @@
 
 void Lexer::EatWhitespace() {
   const char* p = ofs_;
+  const char* q;
   for (;;) {
     ofs_ = p;
     /*!re2c
-    [ ]+  { continue; }
-    "$\n" { continue; }
-    nul   { break; }
-    [^]   { break; }
+    [ ]+    { continue; }
+    "$\r\n" { continue; }
+    "$\n"   { continue; }
+    nul     { break; }
+    [^]     { break; }
     */
   }
 }
@@ -207,6 +208,11 @@
       eval->AddText(StringPiece(start, p - start));
       continue;
     }
+    "\r\n" {
+      if (path)
+        p = start;
+      break;
+    }
     [ :|\n] {
       if (path) {
         p = start;
@@ -226,6 +232,9 @@
       eval->AddText(StringPiece(" ", 1));
       continue;
     }
+    "$\r\n"[ ]* {
+      continue;
+    }
     "$\n"[ ]* {
       continue;
     }
diff --git a/src/lexer_test.cc b/src/lexer_test.cc
index e8a1642..331d8e1 100644
--- a/src/lexer_test.cc
+++ b/src/lexer_test.cc
@@ -14,9 +14,8 @@
 
 #include "lexer.h"
 
-#include <gtest/gtest.h>
-
 #include "eval_env.h"
+#include "test.h"
 
 TEST(Lexer, ReadVarValue) {
   Lexer lexer("plain text $var $VaR ${x}\n");
diff --git a/src/line_printer.cc b/src/line_printer.cc
index ef1609c..813f63e 100644
--- a/src/line_printer.cc
+++ b/src/line_printer.cc
@@ -21,6 +21,7 @@
 #else
 #include <unistd.h>
 #include <sys/ioctl.h>
+#include <termios.h>
 #include <sys/time.h>
 #endif
 
diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc
index 6fa4f7c..388b5bc 100644
--- a/src/manifest_parser.cc
+++ b/src/manifest_parser.cc
@@ -191,7 +191,7 @@
 
 bool ManifestParser::ParseLet(string* key, EvalString* value, string* err) {
   if (!lexer_.ReadIdent(key))
-    return false;
+    return lexer_.Error("expected variable name", err);
   if (!ExpectToken(Lexer::EQUALS, err))
     return false;
   if (!lexer_.ReadVarValue(value, err))
@@ -209,7 +209,8 @@
   do {
     string path = eval.Evaluate(env_);
     string path_err;
-    if (!CanonicalizePath(&path, &path_err))
+    unsigned int slash_bits;  // Unused because this only does lookup.
+    if (!CanonicalizePath(&path, &slash_bits, &path_err))
       return lexer_.Error(path_err, err);
     if (!state_->AddDefault(path, &path_err))
       return lexer_.Error(path_err, err);
@@ -323,16 +324,18 @@
   for (vector<EvalString>::iterator i = ins.begin(); i != ins.end(); ++i) {
     string path = i->Evaluate(env);
     string path_err;
-    if (!CanonicalizePath(&path, &path_err))
+    unsigned int slash_bits;
+    if (!CanonicalizePath(&path, &slash_bits, &path_err))
       return lexer_.Error(path_err, err);
-    state_->AddIn(edge, path);
+    state_->AddIn(edge, path, slash_bits);
   }
   for (vector<EvalString>::iterator i = outs.begin(); i != outs.end(); ++i) {
     string path = i->Evaluate(env);
     string path_err;
-    if (!CanonicalizePath(&path, &path_err))
+    unsigned int slash_bits;
+    if (!CanonicalizePath(&path, &slash_bits, &path_err))
       return lexer_.Error(path_err, err);
-    state_->AddOut(edge, path);
+    state_->AddOut(edge, path, slash_bits);
   }
   edge->implicit_deps_ = implicit;
   edge->order_only_deps_ = order_only;
diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index 152b965..6909ea9 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -17,17 +17,16 @@
 #include <map>
 #include <vector>
 
-#include <gtest/gtest.h>
-
 #include "graph.h"
 #include "state.h"
+#include "test.h"
 
 struct ParserTest : public testing::Test,
                     public ManifestParser::FileReader {
   void AssertParse(const char* input) {
     ManifestParser parser(&state, this);
     string err;
-    ASSERT_TRUE(parser.ParseTest(input, &err)) << err;
+    EXPECT_TRUE(parser.ParseTest(input, &err));
     ASSERT_EQ("", err);
   }
 
@@ -97,7 +96,7 @@
   ASSERT_EQ(2u, state.rules_.size());
   const Rule* rule = state.rules_.begin()->second;
   EXPECT_EQ("cat", rule->name());
-  Edge* edge = state.GetNode("result")->in_edge();
+  Edge* edge = state.GetNode("result", 0)->in_edge();
   EXPECT_TRUE(edge->GetBindingBool("restat"));
   EXPECT_FALSE(edge->GetBindingBool("generator"));
 }
@@ -270,6 +269,26 @@
   EXPECT_FALSE(state.LookupNode("in//2"));
 }
 
+#ifdef _WIN32
+TEST_F(ParserTest, CanonicalizeFileBackslashes) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(
+"rule cat\n"
+"  command = cat $in > $out\n"
+"build out: cat in\\1 in\\\\2\n"
+"build in\\1: cat\n"
+"build in\\2: cat\n"));
+
+  Node* node = state.LookupNode("in/1");;
+  EXPECT_TRUE(node);
+  EXPECT_EQ(1, node->slash_bits());
+  node = state.LookupNode("in/2");
+  EXPECT_TRUE(node);
+  EXPECT_EQ(1, node->slash_bits());
+  EXPECT_FALSE(state.LookupNode("in//1"));
+  EXPECT_FALSE(state.LookupNode("in//2"));
+}
+#endif
+
 TEST_F(ParserTest, PathVariables) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(
 "rule cat\n"
@@ -293,6 +312,34 @@
   EXPECT_TRUE(state.LookupNode("bar/foo.cc"));
 }
 
+#ifdef _WIN32
+TEST_F(ParserTest, CanonicalizePathsBackslashes) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(
+"rule cat\n"
+"  command = cat $in > $out\n"
+"build ./out.o: cat ./bar/baz/../foo.cc\n"
+"build .\\out2.o: cat .\\bar/baz\\..\\foo.cc\n"
+"build .\\out3.o: cat .\\bar\\baz\\..\\foo3.cc\n"
+));
+
+  EXPECT_FALSE(state.LookupNode("./out.o"));
+  EXPECT_FALSE(state.LookupNode(".\\out2.o"));
+  EXPECT_FALSE(state.LookupNode(".\\out3.o"));
+  EXPECT_TRUE(state.LookupNode("out.o"));
+  EXPECT_TRUE(state.LookupNode("out2.o"));
+  EXPECT_TRUE(state.LookupNode("out3.o"));
+  EXPECT_FALSE(state.LookupNode("./bar/baz/../foo.cc"));
+  EXPECT_FALSE(state.LookupNode(".\\bar/baz\\..\\foo.cc"));
+  EXPECT_FALSE(state.LookupNode(".\\bar/baz\\..\\foo3.cc"));
+  Node* node = state.LookupNode("bar/foo.cc");
+  EXPECT_TRUE(node);
+  EXPECT_EQ(0, node->slash_bits());
+  node = state.LookupNode("bar/foo3.cc");
+  EXPECT_TRUE(node);
+  EXPECT_EQ(1, node->slash_bits());
+}
+#endif
+
 TEST_F(ParserTest, ReservedWords) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(
 "rule build\n"
@@ -553,6 +600,15 @@
     State state;
     ManifestParser parser(&state, NULL);
     string err;
+    EXPECT_FALSE(parser.ParseTest("rule cc\n  command = foo\n  && bar",
+                                  &err));
+    EXPECT_EQ("input:3: expected variable name\n", err);
+  }
+
+  {
+    State state;
+    ManifestParser parser(&state, NULL);
+    string err;
     EXPECT_FALSE(parser.ParseTest("rule cc\n  command = foo\n"
                                   "build $: cc bar.cc\n",
                                   &err));
@@ -862,22 +918,18 @@
 "  description = compilaci\xC3\xB3\n"));
 }
 
-// We might want to eventually allow CRLF to be nice to Windows developers,
-// but for now just verify we error out with a nice message.
 TEST_F(ParserTest, CRLF) {
   State state;
   ManifestParser parser(&state, NULL);
   string err;
 
-  EXPECT_FALSE(parser.ParseTest("# comment with crlf\r\n",
-                                &err));
-  EXPECT_EQ("input:1: lexing error\n",
-            err);
-
-  EXPECT_FALSE(parser.ParseTest("foo = foo\nbar = bar\r\n",
-                                &err));
-  EXPECT_EQ("input:2: carriage returns are not allowed, use newlines\n"
-            "bar = bar\r\n"
-            "         ^ near here",
-            err);
+  EXPECT_TRUE(parser.ParseTest("# comment with crlf\r\n", &err));
+  EXPECT_TRUE(parser.ParseTest("foo = foo\nbar = bar\r\n", &err));
+  EXPECT_TRUE(parser.ParseTest(
+      "pool link_pool\r\n"
+      "  depth = 15\r\n\r\n"
+      "rule xyz\r\n"
+      "  command = something$expand \r\n"
+      "  description = YAY!\r\n",
+      &err));
 }
diff --git a/src/minidump-win32.cc b/src/minidump-win32.cc
index c79ec0e..c611919 100644
--- a/src/minidump-win32.cc
+++ b/src/minidump-win32.cc
@@ -12,12 +12,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef NINJA_BOOTSTRAP
+#ifdef _MSC_VER
 
 #include <windows.h>
 #include <DbgHelp.h>
 
-
 #include "util.h"
 
 typedef BOOL (WINAPI *MiniDumpWriteDumpFunc) (
@@ -85,4 +84,4 @@
   Warning("minidump created: %s", temp_file);
 }
 
-#endif  // NINJA_BOOTSTRAP
+#endif  // _MSC_VER
diff --git a/src/msvc_helper_test.cc b/src/msvc_helper_test.cc
index 391c045..29db650 100644
--- a/src/msvc_helper_test.cc
+++ b/src/msvc_helper_test.cc
@@ -14,8 +14,6 @@
 
 #include "msvc_helper.h"
 
-#include <gtest/gtest.h>
-
 #include "test.h"
 #include "util.h"
 
diff --git a/src/ninja.cc b/src/ninja.cc
index a381e83..2c890c2 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -163,7 +163,8 @@
 
   /// When to run the tool.
   enum {
-    /// Run after parsing the command-line flags (as early as possible).
+    /// Run after parsing the command-line flags and potentially changing
+    /// the current working directory (as early as possible).
     RUN_AFTER_FLAGS,
 
     /// Run after loading build.ninja.
@@ -192,9 +193,6 @@
 "\n"
 "  -j N     run N jobs in parallel [default=%d, derived from CPUs available]\n"
 "  -l N     do not start new jobs if the load average is greater than N\n"
-#ifdef _WIN32
-"           (not yet implemented on Windows)\n"
-#endif
 "  -k N     keep going until N jobs fail [default=1]\n"
 "  -n       dry run (don't run commands but act like they succeeded)\n"
 "  -v       show all command lines while building\n"
@@ -230,7 +228,8 @@
 /// Returns true if the manifest was rebuilt.
 bool NinjaMain::RebuildManifest(const char* input_file, string* err) {
   string path = input_file;
-  if (!CanonicalizePath(&path, err))
+  unsigned int slash_bits;  // Unused because this path is only used for lookup.
+  if (!CanonicalizePath(&path, &slash_bits, err))
     return false;
   Node* node = state_.LookupNode(path);
   if (!node)
@@ -252,7 +251,8 @@
 
 Node* NinjaMain::CollectTarget(const char* cpath, string* err) {
   string path = cpath;
-  if (!CanonicalizePath(&path, err))
+  unsigned int slash_bits;  // Unused because this path is only used for lookup.
+  if (!CanonicalizePath(&path, &slash_bits, err))
     return NULL;
 
   // Special syntax: "foo.cc^" means "the first output of foo.cc".
@@ -365,7 +365,7 @@
   return 0;
 }
 
-#if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
+#if defined(NINJA_HAVE_BROWSE)
 int NinjaMain::ToolBrowse(int argc, char* argv[]) {
   if (argc < 1) {
     Error("expected a target to browse");
@@ -698,7 +698,7 @@
 /// Returns a Tool, or NULL if Ninja should exit.
 const Tool* ChooseTool(const string& tool_name) {
   static const Tool kTools[] = {
-#if !defined(_WIN32) && !defined(NINJA_BOOTSTRAP)
+#if defined(NINJA_HAVE_BROWSE)
     { "browse", "browse dependency graph in a web browser",
       Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse },
 #endif
@@ -1042,13 +1042,6 @@
   if (exit_code >= 0)
     return exit_code;
 
-  if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) {
-    // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed
-    // by other tools.
-    NinjaMain ninja(ninja_command, config);
-    return (ninja.*options.tool->func)(argc, argv);
-  }
-
   if (options.working_dir) {
     // The formatting of this string, complete with funny quotes, is
     // so Emacs can properly identify that the cwd has changed for
@@ -1062,6 +1055,13 @@
     }
   }
 
+  if (options.tool && options.tool->when == Tool::RUN_AFTER_FLAGS) {
+    // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed
+    // by other tools.
+    NinjaMain ninja(ninja_command, config);
+    return (ninja.*options.tool->func)(argc, argv);
+  }
+
   // The build can take up to 2 passes: one to rebuild the manifest, then
   // another to build the desired target.
   for (int cycle = 0; cycle < 2; ++cycle) {
@@ -1111,7 +1111,7 @@
 }  // anonymous namespace
 
 int main(int argc, char** argv) {
-#if !defined(NINJA_BOOTSTRAP) && defined(_MSC_VER)
+#if defined(_MSC_VER)
   // Set a handler to catch crashes not caught by the __try..__except
   // block (e.g. an exception in a stack-unwind-block).
   set_terminate(TerminateHandler);
diff --git a/src/ninja_test.cc b/src/ninja_test.cc
index 989ea5c..54d8784 100644
--- a/src/ninja_test.cc
+++ b/src/ninja_test.cc
@@ -15,9 +15,35 @@
 #include <stdarg.h>
 #include <stdio.h>
 
-#include "gtest/gtest.h"
+#ifdef _WIN32
+#include "getopt.h"
+#else
+#include <getopt.h>
+#endif
+
+#include "test.h"
 #include "line_printer.h"
 
+struct RegisteredTest {
+  testing::Test* (*factory)();
+  const char *name;
+  bool should_run;
+};
+// This can't be a vector because tests call RegisterTest from static
+// initializers and the order static initializers run it isn't specified. So
+// the vector constructor isn't guaranteed to run before all of the
+// RegisterTest() calls.
+static RegisteredTest tests[10000];
+testing::Test* g_current_test;
+static int ntests;
+static LinePrinter printer;
+
+void RegisterTest(testing::Test* (*factory)(), const char* name) {
+  tests[ntests].factory = factory;
+  tests[ntests++].name = name;
+}
+
+namespace {
 string StringPrintf(const char* format, ...) {
   const int N = 1024;
   char buf[N];
@@ -30,59 +56,101 @@
   return buf;
 }
 
-/// A test result printer that's less wordy than gtest's default.
-struct LaconicPrinter : public testing::EmptyTestEventListener {
-  LaconicPrinter() : tests_started_(0), test_count_(0), iteration_(0) {}
-  virtual void OnTestProgramStart(const testing::UnitTest& unit_test) {
-    test_count_ = unit_test.test_to_run_count();
-  }
+void Usage() {
+  fprintf(stderr,
+"usage: ninja_tests [options]\n"
+"\n"
+"options:\n"
+"  --gtest_filter=POSTIVE_PATTERN[-NEGATIVE_PATTERN]\n"
+"      Run tests whose names match the positive but not the negative pattern.\n"
+"      '*' matches any substring. (gtest's ':', '?' are not implemented).\n");
+}
 
-  virtual void OnTestIterationStart(const testing::UnitTest& test_info,
-                                    int iteration) {
-    tests_started_ = 0;
-    iteration_ = iteration;
+bool PatternMatchesString(const char* pattern, const char* str) {
+  switch (*pattern) {
+    case '\0':
+    case '-': return *str == '\0';
+    case '*': return (*str != '\0' && PatternMatchesString(pattern, str + 1)) ||
+                     PatternMatchesString(pattern + 1, str);
+    default:  return *pattern == *str &&
+                     PatternMatchesString(pattern + 1, str + 1);
   }
+}
 
-  virtual void OnTestStart(const testing::TestInfo& test_info) {
-    ++tests_started_;
-    printer_.Print(
-        StringPrintf("[%d/%d%s] %s.%s",
-                     tests_started_,
-                     test_count_,
-                     iteration_ ? StringPrintf(" iter %d", iteration_).c_str()
-                                : "",
-                     test_info.test_case_name(),
-                     test_info.name()),
-        LinePrinter::ELIDE);
+bool TestMatchesFilter(const char* test, const char* filter) {
+  // Split --gtest_filter at '-' into positive and negative filters.
+  const char* const dash = strchr(filter, '-');
+  const char* pos = dash == filter ? "*" : filter; //Treat '-test1' as '*-test1'
+  const char* neg = dash ? dash + 1 : "";
+  return PatternMatchesString(pos, test) && !PatternMatchesString(neg, test);
+}
+
+bool ReadFlags(int* argc, char*** argv, const char** test_filter) {
+  enum { OPT_GTEST_FILTER = 1 };
+  const option kLongOptions[] = {
+    { "gtest_filter", required_argument, NULL, OPT_GTEST_FILTER },
+    { NULL, 0, NULL, 0 }
+  };
+
+  int opt;
+  while ((opt = getopt_long(*argc, *argv, "h", kLongOptions, NULL)) != -1) {
+    switch (opt) {
+    case OPT_GTEST_FILTER:
+      if (strchr(optarg, '?') == NULL && strchr(optarg, ':') == NULL) {
+        *test_filter = optarg;
+        break;
+      }  // else fall through.
+    default:
+      Usage();
+      return false;
+    }
   }
+  *argv += optind;
+  *argc -= optind;
+  return true;
+}
 
-  virtual void OnTestPartResult(
-      const testing::TestPartResult& test_part_result) {
-    if (!test_part_result.failed())
-      return;
-    printer_.PrintOnNewLine(StringPrintf(
-        "*** Failure in %s:%d\n%s\n", test_part_result.file_name(),
-        test_part_result.line_number(), test_part_result.summary()));
+}  // namespace
+
+bool testing::Test::Check(bool condition, const char* file, int line,
+                          const char* error) {
+  if (!condition) {
+    printer.PrintOnNewLine(
+        StringPrintf("*** Failure in %s:%d\n%s\n", file, line, error));
+    failed_ = true;
   }
-
-  virtual void OnTestProgramEnd(const testing::UnitTest& unit_test) {
-    printer_.PrintOnNewLine(unit_test.Passed() ? "passed\n" : "failed\n");
-  }
-
- private:
-  LinePrinter printer_;
-  int tests_started_;
-  int test_count_;
-  int iteration_;
-};
+  return condition;
+}
 
 int main(int argc, char **argv) {
-  testing::InitGoogleTest(&argc, argv);
+  int tests_started = 0;
 
-  testing::TestEventListeners& listeners =
-      testing::UnitTest::GetInstance()->listeners();
-  delete listeners.Release(listeners.default_result_printer());
-  listeners.Append(new LaconicPrinter);
+  const char* test_filter = "*";
+  if (!ReadFlags(&argc, &argv, &test_filter))
+    return 1;
 
-  return RUN_ALL_TESTS();
+  int nactivetests = 0;
+  for (int i = 0; i < ntests; i++)
+    if ((tests[i].should_run = TestMatchesFilter(tests[i].name, test_filter)))
+      ++nactivetests;
+
+  bool passed = true;
+  for (int i = 0; i < ntests; i++) {
+    if (!tests[i].should_run) continue;
+
+    ++tests_started;
+    testing::Test* test = tests[i].factory();
+    printer.Print(
+        StringPrintf("[%d/%d] %s", tests_started, nactivetests, tests[i].name),
+        LinePrinter::ELIDE);
+    test->SetUp();
+    test->Run();
+    test->TearDown();
+    if (test->Failed())
+      passed = false;
+    delete test;
+  }
+
+  printer.PrintOnNewLine(passed ? "passed\n" : "failed\n");
+  return passed ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/src/state.cc b/src/state.cc
index 7258272..6e3e10d 100644
--- a/src/state.cc
+++ b/src/state.cc
@@ -111,11 +111,11 @@
   return edge;
 }
 
-Node* State::GetNode(StringPiece path) {
+Node* State::GetNode(StringPiece path, unsigned int slash_bits) {
   Node* node = LookupNode(path);
   if (node)
     return node;
-  node = new Node(path.AsString());
+  node = new Node(path.AsString(), slash_bits);
   paths_[node->path()] = node;
   return node;
 }
@@ -145,14 +145,14 @@
   return result;
 }
 
-void State::AddIn(Edge* edge, StringPiece path) {
-  Node* node = GetNode(path);
+void State::AddIn(Edge* edge, StringPiece path, unsigned int slash_bits) {
+  Node* node = GetNode(path, slash_bits);
   edge->inputs_.push_back(node);
   node->AddOutEdge(edge);
 }
 
-void State::AddOut(Edge* edge, StringPiece path) {
-  Node* node = GetNode(path);
+void State::AddOut(Edge* edge, StringPiece path, unsigned int slash_bits) {
+  Node* node = GetNode(path, slash_bits);
   edge->outputs_.push_back(node);
   if (node->in_edge()) {
     Warning("multiple rules generate %s. "
diff --git a/src/state.h b/src/state.h
index c382dc0..8804532 100644
--- a/src/state.h
+++ b/src/state.h
@@ -95,12 +95,12 @@
 
   Edge* AddEdge(const Rule* rule);
 
-  Node* GetNode(StringPiece path);
+  Node* GetNode(StringPiece path, unsigned int slash_bits);
   Node* LookupNode(StringPiece path) const;
   Node* SpellcheckNode(const string& path);
 
-  void AddIn(Edge* edge, StringPiece path);
-  void AddOut(Edge* edge, StringPiece path);
+  void AddIn(Edge* edge, StringPiece path, unsigned int slash_bits);
+  void AddOut(Edge* edge, StringPiece path, unsigned int slash_bits);
   bool AddDefault(StringPiece path, string* error);
 
   /// Reset state.  Keeps all nodes and edges, but restores them to the
diff --git a/src/state_test.cc b/src/state_test.cc
index af2bff1..bd133b6 100644
--- a/src/state_test.cc
+++ b/src/state_test.cc
@@ -12,10 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <gtest/gtest.h>
-
 #include "graph.h"
 #include "state.h"
+#include "test.h"
 
 namespace {
 
@@ -33,15 +32,15 @@
   state.AddRule(rule);
 
   Edge* edge = state.AddEdge(rule);
-  state.AddIn(edge, "in1");
-  state.AddIn(edge, "in2");
-  state.AddOut(edge, "out");
+  state.AddIn(edge, "in1", 0);
+  state.AddIn(edge, "in2", 0);
+  state.AddOut(edge, "out", 0);
 
   EXPECT_EQ("cat in1 in2 > out", edge->EvaluateCommand());
 
-  EXPECT_FALSE(state.GetNode("in1")->dirty());
-  EXPECT_FALSE(state.GetNode("in2")->dirty());
-  EXPECT_FALSE(state.GetNode("out")->dirty());
+  EXPECT_FALSE(state.GetNode("in1", 0)->dirty());
+  EXPECT_FALSE(state.GetNode("in2", 0)->dirty());
+  EXPECT_FALSE(state.GetNode("out", 0)->dirty());
 }
 
 }  // namespace
diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
index 775a13a..8a0787c 100644
--- a/src/subprocess_test.cc
+++ b/src/subprocess_test.cc
@@ -18,6 +18,7 @@
 
 #ifndef _WIN32
 // SetWithLots need setrlimit.
+#include <stdio.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <unistd.h>
@@ -92,7 +93,7 @@
       return;
   }
 
-  ADD_FAILURE() << "We should have been interrupted";
+  ASSERT_FALSE("We should have been interrupted");
 }
 
 TEST_F(SubprocessTest, Console) {
@@ -171,14 +172,15 @@
 TEST_F(SubprocessTest, SetWithLots) {
   // Arbitrary big number; needs to be over 1024 to confirm we're no longer
   // hostage to pselect.
-  const size_t kNumProcs = 1025;
+  const unsigned kNumProcs = 1025;
 
   // Make sure [ulimit -n] isn't going to stop us from working.
   rlimit rlim;
   ASSERT_EQ(0, getrlimit(RLIMIT_NOFILE, &rlim));
-  ASSERT_GT(rlim.rlim_cur, kNumProcs)
-      << "Raise [ulimit -n] well above " << kNumProcs
-      << " to make this test go";
+  if (!EXPECT_GT(rlim.rlim_cur, kNumProcs)) {
+    printf("Raise [ulimit -n] well above %u to make this test go\n", kNumProcs);
+    return;
+  }
 
   vector<Subprocess*> procs;
   for (size_t i = 0; i < kNumProcs; ++i) {
diff --git a/src/test.cc b/src/test.cc
index 21015ed..f667fef 100644
--- a/src/test.cc
+++ b/src/test.cc
@@ -12,20 +12,25 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#ifdef _WIN32
+#include <direct.h>  // Has to be before util.h is included.
+#endif
+
 #include "test.h"
 
 #include <algorithm>
 
 #include <errno.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
 
 #include "build_log.h"
 #include "manifest_parser.h"
 #include "util.h"
 
-#ifdef _WIN32
-#include <windows.h>
-#endif
-
 namespace {
 
 #ifdef _WIN32
@@ -84,13 +89,14 @@
 }
 
 Node* StateTestWithBuiltinRules::GetNode(const string& path) {
-  return state_.GetNode(path);
+  EXPECT_FALSE(strpbrk(path.c_str(), "/\\"));
+  return state_.GetNode(path, 0);
 }
 
 void AssertParse(State* state, const char* input) {
   ManifestParser parser(state, NULL);
   string err;
-  ASSERT_TRUE(parser.ParseTest(input, &err)) << err;
+  EXPECT_TRUE(parser.ParseTest(input, &err));
   ASSERT_EQ("", err);
 }
 
diff --git a/src/test.h b/src/test.h
index f34b877..4c15203 100644
--- a/src/test.h
+++ b/src/test.h
@@ -15,12 +15,94 @@
 #ifndef NINJA_TEST_H_
 #define NINJA_TEST_H_
 
-#include <gtest/gtest.h>
-
 #include "disk_interface.h"
 #include "state.h"
 #include "util.h"
 
+// A tiny testing framework inspired by googletest, but much simpler and
+// faster to compile. It supports most things commonly used from googltest. The
+// most noticeable things missing: EXPECT_* and ASSERT_* don't support
+// streaming notes to them with operator<<, and for failing tests the lhs and
+// rhs are not printed. That's so that this header does not have to include
+// sstream, which slows down building ninja_test almost 20%.
+namespace testing {
+class Test {
+  bool failed_;
+  int assertion_failures_;
+ public:
+  Test() : failed_(false), assertion_failures_(0) {}
+  virtual ~Test() {}
+  virtual void SetUp() {}
+  virtual void TearDown() {}
+  virtual void Run() = 0;
+
+  bool Failed() const { return failed_; }
+  int AssertionFailures() const { return assertion_failures_; }
+  void AddAssertionFailure() { assertion_failures_++; }
+  bool Check(bool condition, const char* file, int line, const char* error);
+};
+}
+
+void RegisterTest(testing::Test* (*)(), const char*);
+
+extern testing::Test* g_current_test;
+#define TEST_F_(x, y, name)                                           \
+  struct y : public x {                                               \
+    static testing::Test* Create() { return g_current_test = new y; } \
+    virtual void Run();                                               \
+  };                                                                  \
+  struct Register##y {                                                \
+    Register##y() { RegisterTest(y::Create, name); }                  \
+  };                                                                  \
+  Register##y g_register_##y;                                         \
+  void y::Run()
+
+#define TEST_F(x, y) TEST_F_(x, x##y, #x "." #y)
+#define TEST(x, y) TEST_F_(testing::Test, x##y, #x "." #y)
+
+#define EXPECT_EQ(a, b) \
+  g_current_test->Check(a == b, __FILE__, __LINE__, #a " == " #b)
+#define EXPECT_NE(a, b) \
+  g_current_test->Check(a != b, __FILE__, __LINE__, #a " != " #b)
+#define EXPECT_GT(a, b) \
+  g_current_test->Check(a > b, __FILE__, __LINE__, #a " > " #b)
+#define EXPECT_LT(a, b) \
+  g_current_test->Check(a < b, __FILE__, __LINE__, #a " < " #b)
+#define EXPECT_GE(a, b) \
+  g_current_test->Check(a >= b, __FILE__, __LINE__, #a " >= " #b)
+#define EXPECT_LE(a, b) \
+  g_current_test->Check(a <= b, __FILE__, __LINE__, #a " <= " #b)
+#define EXPECT_TRUE(a) \
+  g_current_test->Check(static_cast<bool>(a), __FILE__, __LINE__, #a)
+#define EXPECT_FALSE(a) \
+  g_current_test->Check(!static_cast<bool>(a), __FILE__, __LINE__, #a)
+
+#define ASSERT_EQ(a, b) \
+  if (!EXPECT_EQ(a, b)) { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_NE(a, b) \
+  if (!EXPECT_NE(a, b)) { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_GT(a, b) \
+  if (!EXPECT_GT(a, b)) { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_LT(a, b) \
+  if (!EXPECT_LT(a, b)) { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_GE(a, b) \
+  if (!EXPECT_GE(a, b)) { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_LE(a, b) \
+  if (!EXPECT_LE(a, b)) { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_TRUE(a)  \
+  if (!EXPECT_TRUE(a))  { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_FALSE(a) \
+  if (!EXPECT_FALSE(a)) { g_current_test->AddAssertionFailure(); return; }
+#define ASSERT_NO_FATAL_FAILURE(a)                  \
+  {                                                 \
+    int f = g_current_test->AssertionFailures();    \
+    a;                                              \
+    if (f != g_current_test->AssertionFailures()) { \
+      g_current_test->AddAssertionFailure();        \
+      return;                                       \
+    }                                               \
+  }
+
 // Support utilites for tests.
 
 struct Node;
diff --git a/src/util.cc b/src/util.cc
index 484b0c1..746d7ed 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -85,19 +85,33 @@
   fprintf(stderr, "\n");
 }
 
-bool CanonicalizePath(string* path, string* err) {
+bool CanonicalizePath(string* path, unsigned int* slash_bits, string* err) {
   METRIC_RECORD("canonicalize str");
   size_t len = path->size();
   char* str = 0;
   if (len > 0)
     str = &(*path)[0];
-  if (!CanonicalizePath(str, &len, err))
+  if (!CanonicalizePath(str, &len, slash_bits, err))
     return false;
   path->resize(len);
   return true;
 }
 
-bool CanonicalizePath(char* path, size_t* len, string* err) {
+#ifdef _WIN32
+static unsigned int ShiftOverBit(int offset, unsigned int bits) {
+  // e.g. for |offset| == 2:
+  // | ... 9 8 7 6 5 4 3 2 1 0 |
+  // \_________________/   \_/
+  //        above         below
+  // So we drop the bit at offset and move above "down" into its place.
+  unsigned int above = bits & ~((1 << (offset + 1)) - 1);
+  unsigned int below = bits & ((1 << offset) - 1);
+  return (above >> 1) | below;
+}
+#endif
+
+bool CanonicalizePath(char* path, size_t* len, unsigned int* slash_bits,
+                      string* err) {
   // WARNING: this function is performance-critical; please benchmark
   // any changes you make to it.
   METRIC_RECORD("canonicalize path");
@@ -115,12 +129,37 @@
   const char* src = start;
   const char* end = start + *len;
 
+#ifdef _WIN32
+  unsigned int bits = 0;
+  unsigned int bits_mask = 1;
+  int bits_offset = 0;
+  // Convert \ to /, setting a bit in |bits| for each \ encountered.
+  for (char* c = path; c < end; ++c) {
+    switch (*c) {
+      case '\\':
+        bits |= bits_mask;
+        *c = '/';
+        // Intentional fallthrough.
+      case '/':
+        bits_mask <<= 1;
+        bits_offset++;
+    }
+  }
+  if (bits_offset > 32) {
+    *err = "too many path components";
+    return false;
+  }
+  bits_offset = 0;
+#endif
+
   if (*src == '/') {
 #ifdef _WIN32
+    bits_offset++;
     // network path starts with //
     if (*len > 1 && *(src + 1) == '/') {
       src += 2;
       dst += 2;
+      bits_offset++;
     } else {
       ++src;
       ++dst;
@@ -136,6 +175,9 @@
       if (src + 1 == end || src[1] == '/') {
         // '.' component; eliminate.
         src += 2;
+#ifdef _WIN32
+        bits = ShiftOverBit(bits_offset, bits);
+#endif
         continue;
       } else if (src[1] == '.' && (src + 2 == end || src[2] == '/')) {
         // '..' component.  Back up if possible.
@@ -143,6 +185,11 @@
           dst = components[component_count - 1];
           src += 3;
           --component_count;
+#ifdef _WIN32
+          bits = ShiftOverBit(bits_offset, bits);
+          bits_offset--;
+          bits = ShiftOverBit(bits_offset, bits);
+#endif
         } else {
           *dst++ = *src++;
           *dst++ = *src++;
@@ -154,6 +201,9 @@
 
     if (*src == '/') {
       src++;
+#ifdef _WIN32
+      bits = ShiftOverBit(bits_offset, bits);
+#endif
       continue;
     }
 
@@ -164,6 +214,9 @@
 
     while (*src != '/' && src != end)
       *dst++ = *src++;
+#ifdef _WIN32
+    bits_offset++;
+#endif
     *dst++ = *src++;  // Copy '/' or final \0 character as well.
   }
 
@@ -173,6 +226,11 @@
   }
 
   *len = dst - start - 1;
+#ifdef _WIN32
+  *slash_bits = bits;
+#else
+  *slash_bits = 0;
+#endif
   return true;
 }
 
@@ -280,7 +338,38 @@
 }
 
 int ReadFile(const string& path, string* contents, string* err) {
-  FILE* f = fopen(path.c_str(), "r");
+#ifdef _WIN32
+  // This makes a ninja run on a set of 1500 manifest files about 4% faster
+  // than using the generic fopen code below.
+  err->clear();
+  HANDLE f = ::CreateFile(path.c_str(),
+                          GENERIC_READ,
+                          FILE_SHARE_READ,
+                          NULL,
+                          OPEN_EXISTING,
+                          FILE_FLAG_SEQUENTIAL_SCAN,
+                          NULL);
+  if (f == INVALID_HANDLE_VALUE) {
+    err->assign(GetLastErrorString());
+    return -ENOENT;
+  }
+
+  for (;;) {
+    DWORD len;
+    char buf[64 << 10];
+    if (!::ReadFile(f, buf, sizeof(buf), &len, NULL)) {
+      err->assign(GetLastErrorString());
+      contents->clear();
+      return -1;
+    }
+    if (len == 0)
+      break;
+    contents->append(buf, len);
+  }
+  ::CloseHandle(f);
+  return 0;
+#else
+  FILE* f = fopen(path.c_str(), "rb");
   if (!f) {
     err->assign(strerror(errno));
     return -errno;
@@ -299,6 +388,7 @@
   }
   fclose(f);
   return 0;
+#endif
 }
 
 void SetCloseOnExec(int fd) {
@@ -415,10 +505,70 @@
 }
 
 #if defined(_WIN32) || defined(__CYGWIN__)
+static double CalculateProcessorLoad(uint64_t idle_ticks, uint64_t total_ticks)
+{
+  static uint64_t previous_idle_ticks = 0;
+  static uint64_t previous_total_ticks = 0;
+  static double previous_load = -0.0;
+
+  uint64_t idle_ticks_since_last_time = idle_ticks - previous_idle_ticks;
+  uint64_t total_ticks_since_last_time = total_ticks - previous_total_ticks;
+
+  bool first_call = (previous_total_ticks == 0);
+  bool ticks_not_updated_since_last_call = (total_ticks_since_last_time == 0);
+
+  double load;
+  if (first_call || ticks_not_updated_since_last_call) {
+    load = previous_load;
+  } else {
+    // Calculate load.
+    double idle_to_total_ratio =
+        ((double)idle_ticks_since_last_time) / total_ticks_since_last_time;
+    double load_since_last_call = 1.0 - idle_to_total_ratio;
+
+    // Filter/smooth result when possible.
+    if(previous_load > 0) {
+      load = 0.9 * previous_load + 0.1 * load_since_last_call;
+    } else {
+      load = load_since_last_call;
+    }
+  }
+
+  previous_load = load;
+  previous_total_ticks = total_ticks;
+  previous_idle_ticks = idle_ticks;
+
+  return load;
+}
+
+static uint64_t FileTimeToTickCount(const FILETIME & ft)
+{
+  uint64_t high = (((uint64_t)(ft.dwHighDateTime)) << 32);
+  uint64_t low  = ft.dwLowDateTime;
+  return (high | low);
+}
+
 double GetLoadAverage() {
-  // TODO(nicolas.despres@gmail.com): Find a way to implement it on Windows.
-  // Remember to also update Usage() when this is fixed.
-  return -0.0f;
+  FILETIME idle_time, kernel_time, user_time;
+  BOOL get_system_time_succeeded =
+      GetSystemTimes(&idle_time, &kernel_time, &user_time);
+
+  double posix_compatible_load;
+  if (get_system_time_succeeded) {
+    uint64_t idle_ticks = FileTimeToTickCount(idle_time);
+
+    // kernel_time from GetSystemTimes already includes idle_time.
+    uint64_t total_ticks =
+        FileTimeToTickCount(kernel_time) + FileTimeToTickCount(user_time);
+
+    double processor_load = CalculateProcessorLoad(idle_ticks, total_ticks);
+    posix_compatible_load = processor_load * GetProcessorCount();
+
+  } else {
+    posix_compatible_load = -0.0;
+  }
+
+  return posix_compatible_load;
 }
 #else
 double GetLoadAverage() {
diff --git a/src/util.h b/src/util.h
index 7101770..cbdc1a6 100644
--- a/src/util.h
+++ b/src/util.h
@@ -41,9 +41,11 @@
 void Error(const char* msg, ...);
 
 /// Canonicalize a path like "foo/../bar.h" into just "bar.h".
-bool CanonicalizePath(string* path, string* err);
-
-bool CanonicalizePath(char* path, size_t* len, string* err);
+/// |slash_bits| has bits set starting from lowest for a backslash that was
+/// normalized to a forward slash. (only used on Windows)
+bool CanonicalizePath(string* path, unsigned int* slash_bits, string* err);
+bool CanonicalizePath(char* path, size_t* len, unsigned int* slash_bits,
+                      string* err);
 
 /// Appends |input| to |*result|, escaping according to the whims of either
 /// Bash, or Win32's CommandLineToArgvW().
diff --git a/src/util_test.cc b/src/util_test.cc
index b58d15e..8ca7f56 100644
--- a/src/util_test.cc
+++ b/src/util_test.cc
@@ -16,6 +16,15 @@
 
 #include "test.h"
 
+namespace {
+
+bool CanonicalizePath(string* path, string* err) {
+  unsigned int unused;
+  return ::CanonicalizePath(path, &unused, err);
+}
+
+}  // namespace
+
 TEST(CanonicalizePath, PathSamples) {
   string path;
   string err;
@@ -84,6 +93,201 @@
   EXPECT_EQ("", path);
 }
 
+#ifdef _WIN32
+TEST(CanonicalizePath, PathSamplesWindows) {
+  string path;
+  string err;
+
+  EXPECT_FALSE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("empty path", err);
+
+  path = "foo.h"; err = "";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("foo.h", path);
+
+  path = ".\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("foo.h", path);
+
+  path = ".\\foo\\.\\bar.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("foo/bar.h", path);
+
+  path = ".\\x\\foo\\..\\bar.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("x/bar.h", path);
+
+  path = ".\\x\\foo\\..\\..\\bar.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("bar.h", path);
+
+  path = "foo\\\\bar";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("foo/bar", path);
+
+  path = "foo\\\\.\\\\..\\\\\\bar";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("bar", path);
+
+  path = ".\\x\\..\\foo\\..\\..\\bar.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("../bar.h", path);
+
+  path = "foo\\.\\.";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("foo", path);
+
+  path = "foo\\bar\\..";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("foo", path);
+
+  path = "foo\\.hidden_bar";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("foo/.hidden_bar", path);
+
+  path = "\\foo";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("/foo", path);
+
+  path = "\\\\foo";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("//foo", path);
+
+  path = "\\";
+  EXPECT_TRUE(CanonicalizePath(&path, &err));
+  EXPECT_EQ("", path);
+}
+
+TEST(CanonicalizePath, SlashTracking) {
+  string path;
+  string err;
+  unsigned int slash_bits;
+
+  path = "foo.h"; err = "";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("foo.h", path);
+  EXPECT_EQ(0, slash_bits);
+
+  path = "a\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/foo.h", path);
+  EXPECT_EQ(1, slash_bits);
+
+  path = "a/bcd/efh\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/bcd/efh/foo.h", path);
+  EXPECT_EQ(4, slash_bits);
+
+  path = "a\\bcd/efh\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/bcd/efh/foo.h", path);
+  EXPECT_EQ(5, slash_bits);
+
+  path = "a\\bcd\\efh\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/bcd/efh/foo.h", path);
+  EXPECT_EQ(7, slash_bits);
+
+  path = "a/bcd/efh/foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/bcd/efh/foo.h", path);
+  EXPECT_EQ(0, slash_bits);
+
+  path = "a\\./efh\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/efh/foo.h", path);
+  EXPECT_EQ(3, slash_bits);
+
+  path = "a\\../efh\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("efh/foo.h", path);
+  EXPECT_EQ(1, slash_bits);
+
+  path = "a\\b\\c\\d\\e\\f\\g\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/b/c/d/e/f/g/foo.h", path);
+  EXPECT_EQ(127, slash_bits);
+
+  path = "a\\b\\c\\..\\..\\..\\g\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("g/foo.h", path);
+  EXPECT_EQ(1, slash_bits);
+
+  path = "a\\b/c\\../../..\\g\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("g/foo.h", path);
+  EXPECT_EQ(1, slash_bits);
+
+  path = "a\\b/c\\./../..\\g\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/g/foo.h", path);
+  EXPECT_EQ(3, slash_bits);
+
+  path = "a\\b/c\\./../..\\g/foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/g/foo.h", path);
+  EXPECT_EQ(1, slash_bits);
+
+  path = "a\\\\\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/foo.h", path);
+  EXPECT_EQ(1, slash_bits);
+
+  path = "a/\\\\foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/foo.h", path);
+  EXPECT_EQ(0, slash_bits);
+
+  path = "a\\//foo.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ("a/foo.h", path);
+  EXPECT_EQ(1, slash_bits);
+}
+
+TEST(CanonicalizePath, CanonicalizeNotExceedingLen) {
+  // Make sure searching \/ doesn't go past supplied len.
+  char buf[] = "foo/bar\\baz.h\\";  // Last \ past end.
+  unsigned int slash_bits;
+  string err;
+  size_t size = 13;
+  EXPECT_TRUE(::CanonicalizePath(buf, &size, &slash_bits, &err));
+  EXPECT_EQ(0, strncmp("foo/bar/baz.h", buf, size));
+  EXPECT_EQ(2, slash_bits);  // Not including the trailing one.
+}
+
+TEST(CanonicalizePath, TooManyComponents) {
+  string path;
+  string err;
+  unsigned int slash_bits;
+
+  // 32 is OK.
+  path = "a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./x.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+
+  // Backslashes version.
+  path =
+      "a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\."
+      "\\a\\.\\a\\.\\a\\.\\a\\.\\x.h";
+  EXPECT_TRUE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ(slash_bits, 0xffff);
+
+  // 33 is not.
+  err = "";
+  path =
+      "a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/./a/x.h";
+  EXPECT_FALSE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ(err, "too many path components");
+
+  // Backslashes version.
+  err = "";
+  path =
+      "a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\.\\a\\."
+      "\\a\\.\\a\\.\\a\\.\\a\\.\\a\\x.h";
+  EXPECT_FALSE(CanonicalizePath(&path, &slash_bits, &err));
+  EXPECT_EQ(err, "too many path components");
+}
+#endif
+
 TEST(CanonicalizePath, EmptyResult) {
   string path;
   string err;
@@ -122,26 +326,27 @@
   string path;
   string err;
   size_t len;
+  unsigned int unused;
 
   path = "foo/. bar/.";
   len = strlen("foo/.");  // Canonicalize only the part before the space.
-  EXPECT_TRUE(CanonicalizePath(&path[0], &len, &err));
+  EXPECT_TRUE(CanonicalizePath(&path[0], &len, &unused, &err));
   EXPECT_EQ(strlen("foo"), len);
   EXPECT_EQ("foo/. bar/.", string(path));
 
   path = "foo/../file bar/.";
   len = strlen("foo/../file");
-  EXPECT_TRUE(CanonicalizePath(&path[0], &len, &err));
+  EXPECT_TRUE(CanonicalizePath(&path[0], &len, &unused, &err));
   EXPECT_EQ(strlen("file"), len);
   EXPECT_EQ("file ./file bar/.", string(path));
 }
 
 TEST(PathEscaping, TortureTest) {
   string result;
-  
+
   GetWin32EscapedString("foo bar\\\"'$@d!st!c'\\path'\\", &result);
   EXPECT_EQ("\"foo bar\\\\\\\"'$@d!st!c'\\path'\\\\\"", result);
-  result.clear();  
+  result.clear();
 
   GetShellEscapedString("foo bar\"/'$@d!st!c'/path'", &result);
   EXPECT_EQ("'foo bar\"/'\\''$@d!st!c'\\''/path'\\'''", result);
diff --git a/src/version.cc b/src/version.cc
index 3fb729f..2d2d9c0 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -18,7 +18,7 @@
 
 #include "util.h"
 
-const char* kNinjaVersion = "1.5.1";
+const char* kNinjaVersion = "1.5.3";
 
 void ParseVersion(const string& version, int* major, int* minor) {
   size_t end = version.find('.');