Make WebRTC work with Chromium Git checkouts

WebRTC standalone shares a lot of dependencies and build
tools with Chromium. To make the build work, many of the
paths of a Chromium checkout is now emulated by creating
symlinks to files and directories.

All DEPS entries that previously used Var("chromium_trunk")
to reference a Chromium checkout or From("chromium_deps"..)
to reference the Chromium DEPS file are now removed and
replaced by symlink entries in setup_links.py.

The script also handles cleanup of the legacy
Subversion-based dependencies that's needed for the
transition.

Windows: One Windows-specific important change is that
gclient sync|runhooks must now be run from a shell
with Administrator privileges in order to be able to create
symlinks. This also means that Windows XP is no longer
supported.

To transition a previously created checkout:
Run "python setup_links.py --force" to cleanup the old
SVN-based dependencies that have been synced by gclient sync.
For Buildbots, the --force flag is automatically enabled for
their syncs.

BUG=2863, chromium:339647
TEST=Manual testing on Linux, Mac and Windows.
R=andrew@webrtc.org, iannucci@chromium.org, phoglund@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/18379005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6938 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/.gitignore b/.gitignore
index 96556d7..64ccdd5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,10 +37,13 @@
 /Makefile
 /build
 /buildtools
-/chromium_deps
-/chromium_gn
-/google_apis/build
+/chromium/.gclient_entries
+/chromium/.last_sync_chromium
+/chromium/src
+/google_apis
 /gyp-mac-tool
+/links
+/links.db
 /net
 /out
 /resources/*.*
@@ -56,12 +59,12 @@
 /third_party/asan
 /third_party/binutils
 /third_party/boringssl
-/third_party/build_gn
 /third_party/BUILD.gn
 /third_party/clang_format
 /third_party/colorama
 /third_party/cygwin
 /third_party/directxsdk
+/third_party/drmemory
 /third_party/expat
 /third_party/gaeunit
 /third_party/gflags/src
@@ -85,7 +88,7 @@
 /third_party/opus
 /third_party/protobuf
 /third_party/sqlite
-/third_party/syzygy/binaries
+/third_party/syzygy
 /third_party/usrsctp
 /third_party/valgrind
 /third_party/winsdk_samples/src
@@ -93,7 +96,7 @@
 /tools/android
 /tools/android-dummy-test
 /tools/clang
-/tools/find_depot_tools
+/tools/find_depot_tools.py
 /tools/generate_library_loader
 /tools/gn
 /tools/grit
diff --git a/DEPS b/DEPS
index 09a79ae..b9b0243 100644
--- a/DEPS
+++ b/DEPS
@@ -1,5 +1,10 @@
 use_relative_paths = True
 
+# This file contains dependencies for WebRTC that are not shared with Chromium.
+# If you wish to add a dependency that is present in Chromium's src/DEPS or a
+# directory from the Chromium checkout, you should add it to setup_links.py
+# instead.
+
 vars = {
   # Override root_dir in your .gclient's custom_vars to specify a custom root
   # folder name.
@@ -9,212 +14,25 @@
   # Use this googlecode_url variable only if there is an internal mirror for it.
   # If you do not know, use the full path while defining your new deps entry.
   "googlecode_url": "http://%s.googlecode.com/svn",
-  "sourceforge_url": "http://svn.code.sf.net/p/%(repo)s/code",
-  "chromium_trunk" : "http://src.chromium.org/svn/trunk",
-  # chrome://version/ for revision of canary Chrome.
-  # http://chromium-status.appspot.com/lkgr is a last known good revision.
-  "chromium_revision": "289723",
+  "chromium_revision": "3da41f9378f9d075a94cc278f99ce4344f9acc7b",
 }
 
 # NOTE: Prefer revision numbers to tags for svn deps. Use http rather than
 # https; the latter can cause problems for users behind proxies.
 deps = {
-  "../chromium_deps":
-    File(Var("chromium_trunk") + "/src/DEPS@" + Var("chromium_revision")),
-
-  "../chromium_gn":
-    File(Var("chromium_trunk") + "/src/.gn@" + Var("chromium_revision")),
-
-  "build":
-    Var("chromium_trunk") + "/src/build@" + Var("chromium_revision"),
-
-  "buildtools":
-    From("chromium_deps", "src/buildtools"),
-
-  # Needed by common.gypi.
-  "google_apis/build":
-    Var("chromium_trunk") + "/src/google_apis/build@" + Var("chromium_revision"),
-
-  "testing":
-    Var("chromium_trunk") + "/src/testing@" + Var("chromium_revision"),
-
-  "testing/gmock":
-    From("chromium_deps", "src/testing/gmock"),
-
-  "testing/gtest":
-    From("chromium_deps", "src/testing/gtest"),
-
-  "third_party/binutils":
-    Var("chromium_trunk") + "/src/third_party/binutils@" + Var("chromium_revision"),
-
-  "third_party/build_gn":
-    File(Var("chromium_trunk") + "/src/third_party/BUILD.gn@" + Var("chromium_revision")),
-
-  "third_party/colorama/src":
-    From("chromium_deps", "src/third_party/colorama/src"),
-
-  "third_party/expat":
-    Var("chromium_trunk") + "/src/third_party/expat@" + Var("chromium_revision"),
-
   # When rolling gflags, also update deps/third_party/webrtc/webrtc.DEPS/DEPS
   # in Chromium's repo.
   "third_party/gflags/src":
     (Var("googlecode_url") % "gflags") + "/trunk/src@84",
 
-  "third_party/icu/":
-    From("chromium_deps", "src/third_party/icu"),
-
-  "third_party/jsoncpp/":
-    Var("chromium_trunk") + "/src/third_party/jsoncpp@" + Var("chromium_revision"),
-
-  "third_party/jsoncpp/source":
-    (Var("sourceforge_url") % {"repo": "jsoncpp"}) + "/trunk/jsoncpp@248",
-
   "third_party/junit/":
     (Var("googlecode_url") % "webrtc") + "/deps/third_party/junit@3367",
-
-  "third_party/libc++":
-    Var("chromium_trunk") + "/src/third_party/libc++@" + Var("chromium_revision"),
-
-  "third_party/libc++/trunk":
-    From("chromium_deps", "src/third_party/libc++/trunk"),
-
-  "third_party/libc++abi":
-    Var("chromium_trunk") + "/src/third_party/libc++abi@" + Var("chromium_revision"),
-
-  "third_party/libc++abi/trunk":
-    From("chromium_deps", "src/third_party/libc++abi/trunk"),
-
-  "third_party/openmax_dl/":
-    (Var("googlecode_url") % "webrtc") + "/deps/third_party/openmax@6096",
-
-  "third_party/libjpeg":
-    Var("chromium_trunk") + "/src/third_party/libjpeg@" + Var("chromium_revision"),
-
-  "third_party/libjpeg_turbo":
-    From("chromium_deps", "src/third_party/libjpeg_turbo"),
-
-  "third_party/libsrtp/":
-    From("chromium_deps", "src/third_party/libsrtp"),
-
-  "third_party/libvpx":
-    From("chromium_deps", "src/third_party/libvpx"),
-
-  "third_party/libyuv":
-    (Var("googlecode_url") % "libyuv") + "/trunk@1038",
-
-  "third_party/opus":
-    Var("chromium_trunk") + "/src/third_party/opus@" + Var("chromium_revision"),
-
-  "third_party/opus/src":
-    From("chromium_deps", "src/third_party/opus/src"),
-
-  "third_party/protobuf":
-    Var("chromium_trunk") + "/src/third_party/protobuf@" + Var("chromium_revision"),
-
-  "third_party/sqlite/":
-    Var("chromium_trunk") + "/src/third_party/sqlite@" + Var("chromium_revision"),
-
-  "third_party/yasm":
-    Var("chromium_trunk") + "/src/third_party/yasm@" + Var("chromium_revision"),
-
-  "third_party/yasm/source/patched-yasm":
-    From("chromium_deps", "src/third_party/yasm/source/patched-yasm"),
-
-  "tools/clang":
-    Var("chromium_trunk") + "/src/tools/clang@" + Var("chromium_revision"),
-
-  "tools/generate_library_loader":
-    Var("chromium_trunk") + "/src/tools/generate_library_loader@" + Var("chromium_revision"),
-
-  "tools/gn":
-    Var("chromium_trunk") + "/src/tools/gn@" + Var("chromium_revision"),
-
-  "tools/gyp":
-    From("chromium_deps", "src/tools/gyp"),
-
-  "tools/memory":
-    Var("chromium_trunk") + "/src/tools/memory@" + Var("chromium_revision"),
-
-  "tools/protoc_wrapper":
-    Var("chromium_trunk") + "/src/tools/protoc_wrapper@" + Var("chromium_revision"),
-
-  "tools/python":
-    Var("chromium_trunk") + "/src/tools/python@" + Var("chromium_revision"),
-
-  "tools/sanitizer_options":
-    File(Var("chromium_trunk") + "/src/base/debug/sanitizer_options.cc@" + Var("chromium_revision")),
-
-  "tools/swarming_client":
-    From("chromium_deps", "src/tools/swarming_client"),
-
-  "tools/valgrind":
-    Var("chromium_trunk") + "/src/tools/valgrind@" + Var("chromium_revision"),
-
-  # Needed by build/common.gypi.
-  "tools/win/supalink":
-    Var("chromium_trunk") + "/src/tools/win/supalink@" + Var("chromium_revision"),
-
-  "net/third_party/nss":
-    Var("chromium_trunk") + "/src/net/third_party/nss@" + Var("chromium_revision"),
-
-  "third_party/boringssl":
-    Var("chromium_trunk") + "/src/third_party/boringssl@" + Var("chromium_revision"),
-
-  "third_party/boringssl/src":
-    From("chromium_deps", "src/third_party/boringssl/src"),
-
-  "third_party/usrsctp/":
-    Var("chromium_trunk") + "/src/third_party/usrsctp@" + Var("chromium_revision"),
-
-  "third_party/usrsctp/usrsctplib":
-    From("chromium_deps", "src/third_party/usrsctp/usrsctplib"),
 }
 
 deps_os = {
   "win": {
-    "third_party/drmemory":
-      Var("chromium_trunk") + "/src/third_party/drmemory@" + Var("chromium_revision"),
-
     "third_party/winsdk_samples/src":
       (Var("googlecode_url") % "webrtc") + "/deps/third_party/winsdk_samples_v71@3145",
-
-    # Used by libjpeg-turbo.
-    "third_party/yasm/binaries":
-      From("chromium_deps", "src/third_party/yasm/binaries"),
-
-    # NSS, for SSLClientSocketNSS.
-    "third_party/nss":
-      From("chromium_deps", "src/third_party/nss"),
-
-    "tools/find_depot_tools":
-      File(Var("chromium_trunk") + "/src/tools/find_depot_tools.py@" + Var("chromium_revision")),
-  },
-
-  "mac": {
-    # NSS, for SSLClientSocketNSS.
-    "third_party/nss":
-      From("chromium_deps", "src/third_party/nss"),
-
-    # TODO(kjellander): remove once bug 2152 is fixed.
-    # This needs to specify the path directly (instead of using the
-    # chromium_deps version) because chromium_deps only defines this for ios.
-    "testing/iossim/third_party/class-dump":
-      Var("chromium_trunk") + "/deps/third_party/class-dump@199203",
-  },
-
-  "ios": {
-    # NSS, for SSLClientSocketNSS.
-    "third_party/nss":
-      From("chromium_deps", "src/third_party/nss"),
-
-    # class-dump utility to generate header files for undocumented SDKs.
-    "testing/iossim/third_party/class-dump":
-      From("chromium_deps", "src/testing/iossim/third_party/class-dump"),
-
-    # Helper for running under the simulator.
-    "testing/iossim":
-      Var("chromium_trunk") + "/src/testing/iossim@" + Var("chromium_revision"),
   },
 
   "android": {
@@ -222,156 +40,26 @@
     # compile them from source in WebRTC since they depend on Chromium's base.
     "tools/android":
       (Var("googlecode_url") % "webrtc") + "/deps/tools/android@6306",
-
-    "third_party/android_tools":
-      From("chromium_deps", "src/third_party/android_tools"),
-
-    "third_party/android_testrunner":
-      Var("chromium_trunk") + "/src/third_party/android_testrunner@" + Var("chromium_revision"),
   },
 }
 
 hooks = [
   {
-    # Copy .gn from temporary place (../chromium_gn) to root_dir.
-    "name": "copy .gn",
+    # Clone chromium and its deps.
+    "name": "sync chromium",
     "pattern": ".",
-    "action": ["python", Var("root_dir") + "/build/cp.py",
-               Var("root_dir") + "/../chromium_gn/.gn",
-               Var("root_dir")],
+    "action": ["python", "-u", Var("root_dir") + "/sync_chromium.py",
+               "--target-revision", Var("chromium_revision")],
   },
   {
-    # Copy BUILD.gn from temporary place (third_party/build_gn) to third_party.
-    "name": "copy third_party/BUILD.gn",
+    # Create links to shared dependencies in Chromium.
+    "name": "setup_links",
     "pattern": ".",
-    "action": ["python", Var("root_dir") + "/build/cp.py",
-               Var("root_dir") + "/third_party/build_gn/BUILD.gn",
-               Var("root_dir") + "/third_party"],
-  },
-  # Pull GN binaries. This needs to be before running GYP below.
-  {
-    "name": "gn_win",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=win32",
-                "--no_auth",
-                "--bucket", "chromium-gn",
-                "-s", Var("root_dir") + "/buildtools/win/gn.exe.sha1",
-    ],
-  },
-  {
-    "name": "gn_mac",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=darwin",
-                "--no_auth",
-                "--bucket", "chromium-gn",
-                "-s", Var("root_dir") + "/buildtools/mac/gn.sha1",
-    ],
-  },
-  {
-    "name": "gn_linux",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=linux*",
-                "--no_auth",
-                "--bucket", "chromium-gn",
-                "-s", Var("root_dir") + "/buildtools/linux64/gn.sha1",
-    ],
-  },
-  {
-    "name": "gn_linux32",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=linux*",
-                "--no_auth",
-                "--bucket", "chromium-gn",
-                "-s", Var("root_dir") + "/buildtools/linux32/gn.sha1",
-    ],
-  },
-  # Pull clang-format binaries using checked-in hashes.
-  {
-    "name": "clang_format_win",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=win32",
-                "--no_auth",
-                "--bucket", "chromium-clang-format",
-                "-s", Var("root_dir") + "/buildtools/win/clang-format.exe.sha1",
-    ],
-  },
-  {
-    "name": "clang_format_mac",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=darwin",
-                "--no_auth",
-                "--bucket", "chromium-clang-format",
-                "-s", Var("root_dir") + "/buildtools/mac/clang-format.sha1",
-    ],
-  },
-  {
-    "name": "clang_format_linux",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=linux*",
-                "--no_auth",
-                "--bucket", "chromium-clang-format",
-                "-s", Var("root_dir") + "/buildtools/linux64/clang-format.sha1",
-    ],
-  },
-  {
-    # Pull clang if on Mac or clang is requested via GYP_DEFINES.
-    "pattern": ".",
-    "action": ["python", Var("root_dir") + "/tools/clang/scripts/update.py",
-               "--if-needed"],
-  },
-  {
-    # Update the Windows toolchain if necessary.
-    "name": "win_toolchain",
-    "pattern": ".",
-    "action": ["python",
-               Var("root_dir") + "/webrtc/build/download_vs_toolchain.py",
-               "update"],
-  },
-  {
-    # Pull binutils for gold.
-    "name": "binutils",
-    "pattern": ".",
-    "action": ["python", Var("root_dir") + "/third_party/binutils/download.py"],
-  },
-  {
-    "name": "drmemory",
-    "pattern": ".",
-    "action": [ "download_from_google_storage",
-                "--no_resume",
-                "--platform=win32",
-                "--no_auth",
-                "--bucket", "chromium-drmemory",
-                "-s", Var("root_dir") + "/third_party/drmemory/drmemory-windows-sfx.exe.sha1",
-    ],
-  },
-  {
-    # Pull the Syzygy binaries, used for optimization and instrumentation.
-    "name": "syzygy-binaries",
-    "pattern": ".",
-    "action": ["python",
-               Var("root_dir") + "/build/get_syzygy_binaries.py",
-               "--output-dir=%s/third_party/syzygy/binaries" % Var("root_dir"),
-               "--revision=b08fb72610963d31cc3eae33f746a04e263bd860",
-               "--overwrite",
-    ],
+    "action": ["python", Var("root_dir") + "/setup_links.py"],
   },
   {
     # Download test resources, i.e. video and audio files from Google Storage.
-    "pattern": "\\.sha1",
+    "pattern": ".",
     "action": ["download_from_google_storage",
                "--directory",
                "--recursive",
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
old mode 100644
new mode 100755
index 143d35f..430628c
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -123,6 +123,7 @@
                   # Embedded shell-script fakes out pylint.
                   r'^build/.*\.py$',
                   r'^buildtools/.*\.py$',
+                  r'^chromium/.*\.py$',
                   r'^out.*/.*\.py$',
                   r'^talk/site_scons/site_tools/talk_linux.py$',
                   r'^testing/.*\.py$',
diff --git a/chromium/.gclient b/chromium/.gclient
new file mode 100644
index 0000000..68b9dd1
--- /dev/null
+++ b/chromium/.gclient
@@ -0,0 +1,11 @@
+solutions = [
+  { "name"        : "src",
+    "url"         : "https://chromium.googlesource.com/chromium/src.git",
+    "deps_file"   : ".DEPS.git",
+    "managed"     : True,
+    "custom_deps" : {
+    },
+    "safesync_url": "",
+  },
+]
+cache_dir = None
diff --git a/setup_links.py b/setup_links.py
new file mode 100755
index 0000000..2fea2c3
--- /dev/null
+++ b/setup_links.py
@@ -0,0 +1,483 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+"""Setup links to a Chromium checkout for WebRTC.
+
+WebRTC standalone shares a lot of dependencies and build tools with Chromium.
+To do this, many of the paths of a Chromium checkout is emulated by creating
+symlinks to files and directories. This script handles the setup of symlinks to
+achieve this.
+
+It also handles cleanup of the legacy Subversion-based approach that was used
+before Chrome switched over their master repo from Subversion to Git.
+"""
+
+
+import ctypes
+import errno
+import logging
+import optparse
+import os
+import shelve
+import shutil
+import subprocess
+import sys
+import textwrap
+
+
+DIRECTORIES = [
+  'build',
+  'buildtools',
+  'google_apis',  # Needed by build/common.gypi.
+  'net',
+  'testing',
+  'third_party/android_testrunner',
+  'third_party/android_tools',
+  'third_party/binutils',
+  'third_party/boringssl',
+  'third_party/colorama',
+  'third_party/drmemory',
+  'third_party/expat',
+  'third_party/icu',
+  'third_party/jsoncpp',
+  'third_party/libc++',
+  'third_party/libc++abi',
+  'third_party/libjpeg',
+  'third_party/libjpeg_turbo',
+  'third_party/libsrtp',
+  'third_party/libvpx',
+  'third_party/libyuv',
+  'third_party/llvm-build',
+  'third_party/nss',
+  'third_party/openmax_dl',
+  'third_party/opus',
+  'third_party/protobuf',
+  'third_party/sqlite',
+  'third_party/syzygy',
+  'third_party/usrsctp',
+  'third_party/yasm',
+  'tools/clang',
+  'tools/generate_library_loader',
+  'tools/gn',
+  'tools/gyp',
+  'tools/memory',
+  'tools/protoc_wrapper',
+  'tools/python',
+  'tools/swarming_client',
+  'tools/valgrind',
+  'tools/win',
+]
+
+FILES = {
+  '.gn': None,
+  'tools/find_depot_tools.py': None,
+  'third_party/BUILD.gn': None,
+
+  # This can be removed after https://codereview.chromium.org/357623003/ is
+  # landed and WebRTC is refactored+rolled past that Chromium revision.
+  'base/debug/sanitizer_options.cc': (
+      'tools/sanitizer_options/sanitizer_options.cc'),
+}
+
+CHROMIUM_CHECKOUT = os.path.join('chromium', 'src')
+LINKS_DB = 'links'
+
+# Version management to make future upgrades/downgrades easier to support.
+SCHEMA_VERSION = 1
+
+
+def query_yes_no(question, default=False):
+  """Ask a yes/no question via raw_input() and return their answer.
+
+  Modified from http://stackoverflow.com/a/3041990.
+  """
+  prompt = " [%s/%%s]: "
+  prompt = prompt % ('Y' if default is True  else 'y')
+  prompt = prompt % ('N' if default is False else 'n')
+
+  if default is None:
+    default = 'INVALID'
+
+  while True:
+    sys.stdout.write(question + prompt)
+    choice = raw_input().lower()
+    if choice == '' and default != 'INVALID':
+      return default
+
+    if 'yes'.startswith(choice):
+      return True
+    elif 'no'.startswith(choice):
+      return False
+
+    print "Please respond with 'yes' or 'no' (or 'y' or 'n')."
+
+
+# Actions
+class Action(object):
+  def __init__(self, dangerous):
+    self.dangerous = dangerous
+
+  def announce(self, planning):
+    """Log a description of this action.
+
+    Args:
+      planning - True iff we're in the planning stage, False if we're in the
+                 doit stage.
+    """
+    pass
+
+  def doit(self, links_db):
+    """Execute the action, recording what we did to links_db, if necessary."""
+    pass
+
+
+class Remove(Action):
+  def __init__(self, path, dangerous):
+    super(Remove, self).__init__(dangerous)
+    self._priority = 0
+    self._path = path
+
+  def announce(self, planning):
+    log = logging.warn
+    filesystem_type = 'file'
+    if not self.dangerous:
+      log = logging.info
+      filesystem_type = 'link'
+    if planning:
+      log('Planning to remove %s: %s', filesystem_type, self._path)
+    else:
+      log('Removing %s: %s', filesystem_type, self._path)
+
+  def doit(self, _links_db):
+    os.remove(self._path)
+
+
+class Rmtree(Action):
+  def __init__(self, path):
+    super(Rmtree, self).__init__(dangerous=True)
+    self._priority = 0
+    self._path = path
+
+  def announce(self, planning):
+    if planning:
+      logging.warn('Planning to remove directory: %s', self._path)
+    else:
+      logging.warn('Removing directory: %s', self._path)
+
+  def doit(self, _links_db):
+    if sys.platform.startswith('win'):
+      # shutil.rmtree() doesn't work on Windows if any of the directories are
+      # read-only, which svn repositories are.
+      subprocess.check_call(['rd', '/q', '/s', self._path], shell=True)
+    else:
+      shutil.rmtree(self._path)
+
+
+class Makedirs(Action):
+  def __init__(self, path):
+    super(Makedirs, self).__init__(dangerous=False)
+    self._priority = 1
+    self._path = path
+
+  def doit(self, _links_db):
+    try:
+      os.makedirs(self._path)
+    except OSError as e:
+      if e.errno != errno.EEXIST:
+        raise
+
+
+class Symlink(Action):
+  def __init__(self, source_path, link_path):
+    super(Symlink, self).__init__(dangerous=False)
+    self._priority = 2
+    self._source_path = source_path
+    self._link_path = link_path
+
+  def announce(self, planning):
+    if planning:
+      logging.info(
+          'Planning to create link from %s to %s', self._link_path,
+          self._source_path)
+    else:
+      logging.debug(
+          'Linking from %s to %s', self._link_path, self._source_path)
+
+  def doit(self, links_db):
+    # Files not in the root directory need relative path calculation.
+    # On Windows, use absolute paths instead since NTFS doesn't seem to support
+    # relative paths for symlinks.
+    if sys.platform.startswith('win'):
+      source_path = os.path.abspath(self._source_path)
+    else:
+      if os.path.dirname(self._link_path) != self._link_path:
+        source_path = os.path.relpath(self._source_path,
+                                      os.path.dirname(self._link_path))
+
+    os.symlink(source_path, os.path.abspath(self._link_path))
+    links_db[self._source_path] = self._link_path
+
+
+class LinkError(IOError):
+  """Failed to create a link."""
+  pass
+
+
+# Handles symlink creation on the different platforms.
+if sys.platform.startswith('win'):
+  def symlink(source_path, link_path):
+    flag = 1 if os.path.isdir(source_path) else 0
+    if not ctypes.windll.kernel32.CreateSymbolicLinkW(
+        unicode(link_path), unicode(source_path), flag):
+      raise OSError('Failed to create symlink to %s. Notice that only NTFS '
+                    'version 5.0 and up has all the needed APIs for '
+                    'creating symlinks.' % source_path)
+  os.symlink = symlink
+
+
+class WebRTCLinkSetup():
+  def __init__(self, links_db, force=False, dry_run=False, prompt=False):
+    self._force = force
+    self._dry_run = dry_run
+    self._prompt = prompt
+    self._links_db = links_db
+
+  def CreateLinks(self, on_bot):
+    logging.debug('CreateLinks')
+    # First, make a plan of action
+    actions = []
+
+    for source_path, link_path in FILES.iteritems():
+      actions += self._ActionForPath(
+          source_path, link_path, check_fn=os.path.isfile, check_msg='files')
+    for source_dir in DIRECTORIES:
+      actions += self._ActionForPath(
+          source_dir, None, check_fn=os.path.isdir,
+          check_msg='directories')
+
+    actions.sort()
+
+    if self._dry_run:
+      for action in actions:
+        action.announce(planning=True)
+      logging.info('Not doing anything because dry-run was specified.')
+      sys.exit(0)
+
+    if any(a.dangerous for a in actions):
+      logging.warn('Dangerous actions:')
+      for action in (a for a in actions if a.dangerous):
+        action.announce(planning=True)
+      print
+
+      if not self._force:
+        logging.error(textwrap.dedent("""\
+        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+                              A C T I O N     R E Q I R E D
+        @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+        Because chromium/src is transitioning to Git (from SVN), we needed to
+        change the way that the WebRTC standalone checkout works. Instead of
+        individually syncing subdirectories of Chromium in SVN, we're now
+        syncing Chromium (and all of its DEPS, as defined by its own DEPS file),
+        into the `chromium/src` directory.
+
+        As such, all Chromium directories which are currently pulled by DEPS are
+        now replaced with a symlink into the full Chromium checkout.
+
+        To avoid disrupting developers, we've chosen to not delete your
+        directories forcibly, in case you have some work in progress in one of
+        them :).
+
+        ACTION REQUIRED:
+        Before running `gclient sync|runhooks` again, you must run:
+        %s%s --force
+
+        Which will replace all directories which now must be symlinks, after
+        prompting with a summary of the work-to-be-done.
+        """), 'python ' if sys.platform.startswith('win') else '', sys.argv[0])
+        sys.exit(1)
+      elif self._prompt:
+        if not query_yes_no('Would you like to perform the above plan?'):
+          sys.exit(1)
+
+    for action in actions:
+      action.announce(planning=False)
+      action.doit(self._links_db)
+
+    if not on_bot and self._force:
+      logging.info('Completed!\n\nNow run `gclient sync|runhooks` again to '
+                   'let the remaining hooks (that probably were interrupted) '
+                   'execute.')
+
+  def CleanupLinks(self):
+    logging.debug('CleanupLinks')
+    for source, link_path  in self._links_db.iteritems():
+      if source == 'SCHEMA_VERSION':
+        continue
+      if os.path.islink(link_path) or sys.platform.startswith('win'):
+        # os.path.islink() always returns false on Windows
+        # See http://bugs.python.org/issue13143.
+        logging.debug('Removing link to %s at %s', source, link_path)
+        if not self._dry_run:
+          if os.path.exists(link_path):
+            if sys.platform.startswith('win') and os.path.isdir(link_path):
+              subprocess.check_call(['rmdir', '/q', link_path], shell=True)
+            else:
+              os.remove(link_path)
+          del self._links_db[source]
+
+  @staticmethod
+  def _ActionForPath(source_path, link_path=None, check_fn=None,
+                     check_msg=None):
+    """Create zero or more Actions to link to a file or directory.
+
+    This will be a symlink on POSIX platforms. On Windows this requires
+    that NTFS is version 5.0 or higher (Vista or newer).
+
+    Args:
+      source_path: Path relative to the Chromium checkout root.
+        For readability, the path may contain slashes, which will
+        automatically be converted to the right path delimiter on Windows.
+      link_path: The location for the link to create. If omitted it will be the
+        same path as source_path.
+      check_fn: A function returning true if the type of filesystem object is
+        correct for the attempted call. Otherwise an error message with
+        check_msg will be printed.
+      check_msg: String used to inform the user of an invalid attempt to create
+        a file.
+    Returns:
+      A list of Action objects.
+    """
+    def fix_separators(path):
+      if sys.platform.startswith('win'):
+        return path.replace(os.altsep, os.sep)
+      else:
+        return path
+
+    assert check_fn
+    assert check_msg
+    link_path = link_path or source_path
+    link_path = fix_separators(link_path)
+
+    source_path = fix_separators(source_path)
+    source_path = os.path.join(CHROMIUM_CHECKOUT, source_path)
+    if os.path.exists(source_path) and not check_fn:
+      raise LinkError('_LinkChromiumPath can only be used to link to %s: '
+                      'Tried to link to: %s' % (check_msg, source_path))
+
+    if not os.path.exists(source_path):
+      logging.debug('Silently ignoring missing source: %s. This is to avoid '
+                    'errors on platform-specific dependencies.', source_path)
+      return []
+
+    actions = []
+
+    if os.path.exists(link_path) or os.path.islink(link_path):
+      if os.path.islink(link_path):
+        actions.append(Remove(link_path, dangerous=False))
+      elif os.path.isfile(link_path):
+        actions.append(Remove(link_path, dangerous=True))
+      elif os.path.isdir(link_path):
+        actions.append(Rmtree(link_path))
+      else:
+        raise LinkError('Don\'t know how to plan: %s' % link_path)
+
+    # Create parent directories to the target link if needed.
+    target_parent_dirs = os.path.dirname(link_path)
+    if (target_parent_dirs and
+        target_parent_dirs != link_path and
+        not os.path.exists(target_parent_dirs)):
+      actions.append(Makedirs(target_parent_dirs))
+
+    actions.append(Symlink(source_path, link_path))
+
+    return actions
+
+def _initialize_database(filename):
+  links_database = shelve.open(filename)
+
+  # Wipe the database if this version of the script ends up looking at a
+  # newer (future) version of the links db, just to be sure.
+  version = links_database.get('SCHEMA_VERSION')
+  if version and version != SCHEMA_VERSION:
+    logging.info('Found database with schema version %s while this script only '
+                 'supports %s. Wiping previous database contents.', version,
+                 SCHEMA_VERSION)
+    links_database.clear()
+  links_database['SCHEMA_VERSION'] = SCHEMA_VERSION
+  return links_database
+
+
+def main():
+  on_bot = os.environ.get('CHROME_HEADLESS') == '1'
+
+  parser = optparse.OptionParser()
+  parser.add_option('-d', '--dry-run', action='store_true', default=False,
+                    help='Print what would be done, but don\'t perform any '
+                         'operations. This will automatically set logging to '
+                         'verbose.')
+  parser.add_option('-c', '--clean-only', action='store_true', default=False,
+                    help='Only clean previously created links, don\'t create '
+                         'new ones. This will automatically set logging to '
+                         'verbose.')
+  parser.add_option('-f', '--force', action='store_true', default=on_bot,
+                    help='Force link creation. CAUTION: This deletes existing '
+                         'folders and files in the locations where links are '
+                         'about to be created.')
+  parser.add_option('-n', '--no-prompt', action='store_false', dest='prompt',
+                    default=(not on_bot),
+                    help='Prompt if we\'re planning to do a dangerous action')
+  parser.add_option('-v', '--verbose', action='store_const',
+                    const=logging.DEBUG, default=logging.INFO,
+                    help='Print verbose output for debugging.')
+  options, _ = parser.parse_args()
+
+  if options.dry_run or options.force or options.clean_only:
+    options.verbose = logging.DEBUG
+  logging.basicConfig(format='%(message)s', level=options.verbose)
+
+  # Work from the root directory of the checkout.
+  script_dir = os.path.dirname(os.path.abspath(__file__))
+  os.chdir(script_dir)
+
+  if sys.platform.startswith('win'):
+    def is_admin():
+      try:
+        return os.getuid() == 0
+      except AttributeError:
+        return ctypes.windll.shell32.IsUserAnAdmin() != 0
+    if not is_admin():
+      logging.error('On Windows, you now need to have administrator '
+                    'privileges for the shell running %s (or '
+                    '`gclient sync|runhooks`).\nPlease start another command '
+                    'prompt as Administrator and try again.' % sys.argv[0])
+      return 1
+
+  if not os.path.exists(CHROMIUM_CHECKOUT):
+    logging.error('Cannot find a Chromium checkout at %s. Did you run "gclient '
+                  'sync" before running this script?', CHROMIUM_CHECKOUT)
+    return 2
+
+  links_database = _initialize_database(LINKS_DB)
+  try:
+    symlink_creator = WebRTCLinkSetup(links_database, options.force,
+                                      options.dry_run, options.prompt)
+    symlink_creator.CleanupLinks()
+    if not options.clean_only:
+      symlink_creator.CreateLinks(on_bot)
+  except LinkError as e:
+    print >> sys.stderr, e.message
+    return 3
+  finally:
+    links_database.close()
+  return 0
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/sync_chromium.py b/sync_chromium.py
new file mode 100755
index 0000000..ab40c37
--- /dev/null
+++ b/sync_chromium.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+
+def get_target_os_list():
+  try:
+    main_gclient = os.path.join(os.path.dirname(ROOT_DIR), '.gclient')
+    config_dict = {}
+    with open(main_gclient, 'rb') as deps_content:
+      exec(deps_content, config_dict)
+    return ','.join(config_dict.get('target_os', []))
+  except Exception as e:
+    print >> sys.stderr, "error while parsing .gclient:", e
+
+
+def main():
+  CR_DIR = os.path.join(ROOT_DIR, 'chromium')
+
+  p = argparse.ArgumentParser()
+  p.add_argument('--target-revision', required=True,
+                 help='The target chromium git revision [REQUIRED]')
+  p.add_argument('--chromium-dir', default=CR_DIR,
+                 help=('The path to the chromium directory to sync '
+                       '(default: %(default)r)'))
+  opts = p.parse_args()
+  opts.chromium_dir = os.path.abspath(opts.chromium_dir)
+
+  # Do a quick check to see if we were successful last time to make runhooks
+  # sooper fast.
+  flag_file = os.path.join(opts.chromium_dir, '.last_sync_chromium')
+  if os.path.exists(flag_file):
+    with open(flag_file, 'r') as f:
+      if f.read() == opts.target_revision:
+        print "Chromium already up to date:", opts.target_revision
+        return 0
+    os.unlink(flag_file)
+
+  env = os.environ.copy()
+  env['GYP_CHROMIUM_NO_ACTION'] = '1'
+  gclient_cmd = 'gclient.bat' if sys.platform.startswith('win') else 'gclient'
+  args = [
+      gclient_cmd, 'sync', '--no-history', '--force', '--revision',
+      'src@'+opts.target_revision]
+  target_os_list = get_target_os_list()
+  if target_os_list:
+    args += ['--deps=' + target_os_list]
+
+  print 'Running "%s" in %s' % (' '.join(args), opts.chromium_dir)
+  ret = subprocess.call(args, cwd=opts.chromium_dir, env=env)
+  if ret == 0:
+    with open(flag_file, 'wb') as f:
+      f.write(opts.target_revision)
+
+  return ret
+
+
+if __name__ == '__main__':
+  sys.exit(main())
diff --git a/webrtc/build/download_vs_toolchain.py b/webrtc/build/download_vs_toolchain.py
index 2462bdc..e69de29 100644
--- a/webrtc/build/download_vs_toolchain.py
+++ b/webrtc/build/download_vs_toolchain.py
@@ -1,30 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
-#
-# Use of this source code is governed by a BSD-style license
-# that can be found in the LICENSE file in the root of the source
-# tree. An additional intellectual property rights grant can be found
-# in the file PATENTS.  All contributing project authors may
-# be found in the AUTHORS file in the root of the source tree.
-
-# This script is used to run the vs_toolchain.py script to download the
-# Visual Studio toolchain. It's just a temporary measure while waiting for the
-# Chrome team to move find_depot_tools into src/build to get rid of these
-# workarounds (similar one in gyp_webrtc).
-
-import os
-import sys
-
-
-script_dir = os.path.dirname(os.path.realpath(__file__))
-checkout_root = os.path.abspath(os.path.join(script_dir, os.pardir, os.pardir))
-sys.path.insert(0, os.path.join(checkout_root, 'build'))
-sys.path.insert(0, os.path.join(checkout_root, 'tools', 'find_depot_tools'))
-
-
-import vs_toolchain
-
-
-if __name__ == '__main__':
-  sys.exit(vs_toolchain.main())
diff --git a/webrtc/build/gyp_webrtc b/webrtc/build/gyp_webrtc
index 4d5ae79..edc6b36 100755
--- a/webrtc/build/gyp_webrtc
+++ b/webrtc/build/gyp_webrtc
@@ -20,7 +20,6 @@
 checkout_root = os.path.abspath(os.path.join(script_dir, os.pardir, os.pardir))
 
 sys.path.insert(0, os.path.join(checkout_root, 'build'))
-sys.path.insert(0, os.path.join(checkout_root, 'tools', 'find_depot_tools'))
 import gyp_chromium
 import gyp_helper
 import vs_toolchain
@@ -28,6 +27,13 @@
 sys.path.insert(0, os.path.join(checkout_root, 'tools', 'gyp', 'pylib'))
 import gyp
 
+def GetSupplementalFiles():
+  """Returns a list of the supplemental files that are included in all GYP
+  sources."""
+  # Can't use the one in gyp_chromium since the directory location of the root
+  # is different.
+  return glob.glob(os.path.join(checkout_root, '*', 'supplement.gypi'))
+
 
 if __name__ == '__main__':
   args = sys.argv[1:]
@@ -52,7 +58,10 @@
 
   # If we didn't get a file, assume 'all.gyp' in the root of the checkout.
   if not gyp_file_specified:
-    args.append(os.path.join(checkout_root, 'all.gyp'))
+    # Because of a bug in gyp, simply adding the abspath to all.gyp doesn't
+    # work, but chdir'ing and adding the relative path does. Spooky :/
+    os.chdir(checkout_root)
+    args.append('all.gyp')
 
   # There shouldn't be a circular dependency relationship between .gyp files,
   args.append('--no-circular-check')
@@ -68,7 +77,7 @@
   # Enforce gyp syntax checking. This adds about 20% execution time.
   args.append('--check')
 
-  supplemental_includes = gyp_chromium.GetSupplementalFiles()
+  supplemental_includes = GetSupplementalFiles()
   gn_vars_dict = gyp_chromium.GetGypVars(supplemental_includes)
 
   # Automatically turn on crosscompile support for platforms that need it.