Add a platform filtering and executable setting abilities to download_from_google_storage.

Adds a command line flag --platform to control which platforms the given download occurs on.

Automatically marks downloads executable on non-Windows if the header x-goog-meta-executable is set. Automatically set this flag on upload when the executable bit is set.

BUG=233100

Review URL: https://codereview.chromium.org/42273002

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@231387 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/download_from_google_storage.py b/download_from_google_storage.py
index b398790..bbb9e64 100755
--- a/download_from_google_storage.py
+++ b/download_from_google_storage.py
@@ -11,6 +11,7 @@
 import os
 import Queue
 import re
+import stat
 import sys
 import threading
 import time
@@ -185,6 +186,20 @@
       out_q.put('%d> %s' % (thread_num, err))
       ret_codes.put((code, err))
 
+    # Mark executable if necessary. We key off of the custom header
+    # "x-goog-meta-executable".
+    #
+    # TODO(hinoka): It is supposedly faster to use "gsutil stat" but that
+    # doesn't appear to be supported by the gsutil currently in our tree. When
+    # we update, this code should use that instead of "gsutil ls -L".
+    if not sys.platform.startswith('win'):
+      code, out, _ = gsutil.check_call('ls', '-L', file_url)
+      if code != 0:
+        out_q.put('%d> %s' % (thread_num, err))
+        ret_codes.put((code, err))
+      elif re.search('x-goog-meta-executable:', out):
+        st = os.stat(output_filename)
+        os.chmod(output_filename, st.st_mode | stat.S_IEXEC)
 
 def printer_worker(output_queue):
   while True:
@@ -282,9 +297,21 @@
                     help='Alias for "gsutil config".  Run this if you want '
                          'to initialize your saved Google Storage '
                          'credentials.')
+  parser.add_option('-p', '--platform', 
+                    help='A regular expression that is compared against '
+                         'Python\'s sys.platform. If this option is specified, '
+                         'the download will happen only if there is a match.')
 
   (options, args) = parser.parse_args()
-  # First, make sure we can find a working instance of gsutil.
+
+  # Make sure we should run at all based on platform matching.
+  if options.platform:
+    if not re.match(options.platform, sys.platform):
+      print('The current platform doesn\'t match "%s", skipping.' %
+            options.platform)
+      return 0
+
+  # Make sure we can find a working instance of gsutil.
   if os.path.exists(GSUTIL_DEFAULT_PATH):
     gsutil = Gsutil(GSUTIL_DEFAULT_PATH, boto_path=options.boto)
   else:
diff --git a/upload_to_google_storage.py b/upload_to_google_storage.py
index f10a8d8..e6cd2ee 100755
--- a/upload_to_google_storage.py
+++ b/upload_to_google_storage.py
@@ -10,6 +10,7 @@
 import os
 import Queue
 import re
+import stat
 import sys
 import threading
 import time
@@ -103,6 +104,18 @@
            (filename, file_url, err)))
       continue
 
+    # Mark executable files with the header "x-goog-meta-executable: 1" which
+    # the download script will check for to preserve the executable bit.
+    if not sys.platform.startswith('win'):
+      if os.stat(filename).st_mode & stat.S_IEXEC:
+        code, _, err = gsutil.check_call('setmeta', '-h',
+                                         'x-goog-meta-executable:1', file_url)
+        if code:
+          ret_codes.put(
+              (code,
+               'Encountered error on setting metadata on %s\n%s' %
+               (file_url, err)))
+
 
 def get_targets(args, parser, use_null_terminator):
   if not args: