SDK stuff: Add a script to download the mojom parser binary.

(Using another new, added script that downloads/verifies stuff from
Google Storage.)

R=vardhan@google.com

Review URL: https://codereview.chromium.org/1720433003 .
diff --git a/sdk_build/build_sdk.py b/sdk_build/build_sdk.py
index e5a7638..586a8fc 100755
--- a/sdk_build/build_sdk.py
+++ b/sdk_build/build_sdk.py
@@ -15,12 +15,22 @@
 import sys
 
 
-def _CopyFiles(source_path, dest_path, **kwargs):
-  """Copies files from the source git repository (the current working directory
-  should be the root of this repository) to the destination path. |source_path|
-  and the keyword arguments are as for the arguments of |GitLsFiles|.
-  |dest_path| should be the "root" destination path. Note that a file such as
-  <source_path>/foo/bar/baz.quux is copied to <dest_path>/foo/bar/baz.quux."""
+def _MakeDirs(*args, **kwargs):
+  """Like |os.makedirs()|, but ignores |OSError| exceptions (it assumes that
+  these are due to the directory already existing)."""
+  try:
+    os.makedirs(*args, **kwargs)
+  except OSError:
+    pass
+
+
+def _CopyDir(source_path, dest_path, **kwargs):
+  """Copies directories from the source git repository (the current working
+  directory should be the root of this repository) to the destination path.
+  |source_path| and the keyword arguments are as for the arguments of
+  |GitLsFiles|. |dest_path| should be the "root" destination path. Note that a
+  file such as <source_path>/foo/bar/baz.quux is copied to
+  <dest_path>/foo/bar/baz.quux."""
 
   # Normalize the source path. Note that this strips any trailing '/'.
   source_path = os.path.normpath(source_path)
@@ -28,16 +38,29 @@
   for source_file in source_files:
     rel_path = source_file[len(source_path) + 1:]
     dest_file = os.path.join(dest_path, rel_path)
-    try:
-      os.makedirs(os.path.dirname(dest_file))
-    except OSError:
-      pass
-    shutil.copyfile(source_file, dest_file)
+    _MakeDirs(os.path.dirname(dest_file))
+    shutil.copy(source_file, dest_file)
+
+
+def _CopyFiles(source_files, dest_path):
+  """Copies a given source file or files from the source git repository to the
+  given destination path (the current working directory should be the root of
+  this repository) |source_files| should either be a relative path to a single
+  file in the source git repository or an iterable of such paths; note that this
+  does not check that files are actually in the git repository (i.e., are
+  tracked)."""
+
+  if type(source_files) is str:
+    source_files = [source_files]
+  _MakeDirs(dest_path)
+  for source_file in source_files:
+    shutil.copy(source_file,
+                os.path.join(dest_path, os.path.basename(source_file)))
 
 
 def main():
   parser = argparse.ArgumentParser(
-      description="Constructs an SDK from a specification")
+      description="Constructs an SDK from a specification.")
   parser.add_argument("--allow-dirty-tree", dest="allow_dirty_tree",
                       action="store_true",
                       help="proceed even if the source tree is dirty")
@@ -64,11 +87,16 @@
   except OSError:
     FatalError("failed to create target directory %s" % target_dir)
 
-  def CopyFilesToTargetDir(source_path, rel_dest_path, **kwargs):
-    return _CopyFiles(source_path, os.path.join(target_dir, rel_dest_path),
+  def CopyDirToTargetDir(source_path, rel_dest_path, **kwargs):
+    return _CopyDir(source_path, os.path.join(target_dir, rel_dest_path),
+                    **kwargs)
+
+  def CopyFilesToTargetDir(source_files, rel_dest_path, **kwargs):
+    return _CopyFiles(source_files, os.path.join(target_dir, rel_dest_path),
                       **kwargs)
 
-  execution_globals = {"CopyFiles": CopyFilesToTargetDir,
+  execution_globals = {"CopyDir": CopyDirToTargetDir,
+                       "CopyFiles": CopyFilesToTargetDir,
                        "FatalError": FatalError,
                        "GitLsFiles": GitLsFiles}
   exec args.sdk_spec_file in execution_globals
diff --git a/sdk_build/data/common/download_file_from_google_storage.py b/sdk_build/data/common/download_file_from_google_storage.py
new file mode 100755
index 0000000..28feb4d
--- /dev/null
+++ b/sdk_build/data/common/download_file_from_google_storage.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+#FIXME
+
+import argparse
+import hashlib
+import httplib
+import os
+import sys
+
+
+def _FatalError(message):
+  print >> sys.stderr, "%s: fatal error: %s" % (os.path.basename(sys.argv[0]),
+                                                message)
+
+
+def _DownloadFile(source):
+  """FIXME"""
+
+  try:
+    conn = httplib.HTTPSConnection("storage.googleapis.com")
+    conn.request("GET", "/" + source)
+    resp = conn.getresponse()
+    if resp.status != httplib.OK:
+      _FatalError("HTTP status: %s" % resp.reason)
+    data = resp.read()
+    conn.close()
+    return data
+  except httplib.HTTPException as e:
+    _FatalError("HTTP exception: %s" % str(e))
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Downloads a file from Google Cloud Storage.")
+  parser.add_argument("--sha1-hash", dest="sha1_hash",
+                      help="SHA-1 hash for the downloaded file")
+  parser.add_argument("--executable", action="store_true",
+                      help="make the downloaded file executable")
+  parser.add_argument("source", help="source path, including bucket name")
+  parser.add_argument("destination", help="destination path")
+  args = parser.parse_args()
+
+  bits = _DownloadFile(args.source)
+  if args.sha1_hash:
+    got_sha1_hash = hashlib.sha1(bits).hexdigest().lower()
+    expected_sha1_hash = args.sha1_hash.lower()
+    if got_sha1_hash != expected_sha1_hash:
+      _FatalError("SHA-1 hash did not match: got %s, expected %s" %
+                  (got_sha1_hash, expected_sha1_hash))
+
+  with open(args.destination, "wb") as f:
+    f.write(bits)
+
+  if args.executable:
+    curr_mode = os.stat(args.destination).st_mode
+    # Set the x bits (0111) where the r bits (0444) are set.
+    new_mode = curr_mode | ((curr_mode & 0444) >> 2)
+    os.chmod(args.destination, new_mode)
+
+  return 0
+
+
+if __name__ == "__main__":
+  sys.exit(main())
diff --git a/sdk_build/data/common/download_mojom_parser.sh b/sdk_build/data/common/download_mojom_parser.sh
new file mode 100755
index 0000000..5051e77
--- /dev/null
+++ b/sdk_build/data/common/download_mojom_parser.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# Copyright 2016 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# Note: In the SDK, this script lives in third_party/mojo_sdk_setup.
+SCRIPT_DIR=$(dirname $0)
+DOWNLOADER=${SCRIPT_DIR}/download_file_from_google_storage.py
+
+UNAME=$(uname)
+case "$UNAME" in
+  Linux)
+    # TODO(vtl): We currently just always assume 64-bit.
+    HOST_ARCH=linux64
+    ;;
+  Darwin)
+    HOST_ARCH=mac64
+    ;;
+  *)
+    echo "$0: unknown system: ${UNAME}" 1>&2
+    ;;
+esac
+
+FILE=${SCRIPT_DIR}/../mojo/public/tools/bindings/mojom_parser/bin/${HOST_ARCH}/mojom_parser
+HASH=$(cat "${FILE}.sha1")
+# This includes the bucket name first.
+GS_NAME=mojo/mojom_parser/${HOST_ARCH}/${HASH}
+
+"$DOWNLOADER" --sha1-hash="${HASH}" --executable "$GS_NAME" "$FILE"
diff --git a/sdk_build/data/cpp/cpp.sdk b/sdk_build/data/cpp/cpp.sdk
index 3e061ac..66d000b 100644
--- a/sdk_build/data/cpp/cpp.sdk
+++ b/sdk_build/data/cpp/cpp.sdk
@@ -9,34 +9,44 @@
 EXCLUDE_FILES=[".*", "*.gn", "*.gni", "PRESUBMIT.py", "*_win.*"]
 EXCLUDE_PATHS=["*/tests/*"]
 
-CopyFiles("mojo/public",
-          "third_party/mojo/public",
-          recursive=False,
-          exclude_file_patterns=EXCLUDE_FILES)
+CopyDir("mojo/public",
+        "third_party/mojo/public",
+        recursive=False,
+        exclude_file_patterns=EXCLUDE_FILES)
 
-CopyFiles("mojo/public/c",
-          "third_party/mojo/public/c",
-          recursive=True,
-          exclude_file_patterns=EXCLUDE_FILES,
-          exclude_path_patterns=EXCLUDE_PATHS)
-CopyFiles("mojo/public/cpp",
-          "third_party/mojo/public/cpp",
-          recursive=True,
-          exclude_file_patterns=EXCLUDE_FILES,
-          exclude_path_patterns=EXCLUDE_PATHS+
-              ["mojo/public/cpp/test_support/*"])
+CopyDir("mojo/public/c",
+        "third_party/mojo/public/c",
+        recursive=True,
+        exclude_file_patterns=EXCLUDE_FILES,
+        exclude_path_patterns=EXCLUDE_PATHS)
+CopyDir("mojo/public/cpp",
+        "third_party/mojo/public/cpp",
+        recursive=True,
+        exclude_file_patterns=EXCLUDE_FILES,
+        exclude_path_patterns=EXCLUDE_PATHS+["mojo/public/cpp/test_support/*"])
 
 # For simplicity, copy all of the bindings generators, even though we really
 # only need/want C++.
-CopyFiles("mojo/public/tools/bindings",
-          "third_party/mojo/public/tools/bindings",
-          recursive=True,
-          exclude_file_patterns=EXCLUDE_FILES,
-          exclude_path_patterns=EXCLUDE_PATHS+["*/mojom_tests/*"])
+CopyDir("mojo/public/tools/bindings",
+        "third_party/mojo/public/tools/bindings",
+        recursive=True,
+        exclude_file_patterns=EXCLUDE_FILES,
+        exclude_path_patterns=EXCLUDE_PATHS+["*/mojom_tests/*"])
 # The generators need jinja2, which needs markupsafe. Sigh.
-CopyFiles("mojo/public/third_party/jinja2",
-          "third_party/mojo/public/third_party/jinja2",
-          recursive=True)
-CopyFiles("mojo/public/third_party/markupsafe",
-          "third_party/mojo/public/third_party/markupsafe",
-          recursive=True)
+CopyDir("mojo/public/third_party/jinja2",
+        "third_party/mojo/public/third_party/jinja2",
+        recursive=True)
+CopyDir("mojo/public/third_party/markupsafe",
+        "third_party/mojo/public/third_party/markupsafe",
+        recursive=True)
+
+# Seed an example.
+CopyDir("examples/hello_mojo",
+        "examples/hello_mojo",
+        recursive=True,
+        exclude_file_patterns=EXCLUDE_FILES)
+
+# Scripts to download binaries.
+CopyFiles(["sdk_build/data/common/download_file_from_google_storage.py",
+           "sdk_build/data/common/download_mojom_parser.sh"],
+          "third_party/mojo_sdk_setup")