Adds --auto_platform to download_from_google_storage.

A very common pattern for the users of this script is to have binaries in
three different folders, eg:
some/folder/win/bin.exe
some/folder/mac/bin
some/folder/linux/bin

Instead of using --platform to specify three different paths, we can recognize
this usage and pass in --auto_platform, and then --directory some/folder/ as the
target.

When enumerating files, it will match each individual file to see if they have
(win|mac|linux) as a parent folder name, and filter out non-matching platforms.

BUG=
TEST= src/third_party/clang_format/bin$ download_from_google_storage --auto_platform --no_auth -b chromium-clang-fomat -dr .
0> Downloading ./linux/clang-format...

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@245643 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/download_from_google_storage.py b/download_from_google_storage.py
index bfb1b3c..8370515 100755
--- a/download_from_google_storage.py
+++ b/download_from_google_storage.py
@@ -22,6 +22,13 @@
 GSUTIL_DEFAULT_PATH = os.path.join(
     os.path.dirname(os.path.abspath(__file__)),
     'third_party', 'gsutil', 'gsutil')
+# Maps sys.platform to what we actually want to call them.
+PLATFORM_MAPPING = {
+    'cygwin': 'win',
+    'darwin': 'mac',
+    'linux2': 'linux',
+    'win32': 'win',
+}
 
 
 class FileNotFoundError(IOError):
@@ -32,6 +39,10 @@
   pass
 
 
+class InvalidPlatformError(Exception):
+  pass
+
+
 def GetNormalizedPlatform():
   """Returns the result of sys.platform accounting for cygwin.
   Under cygwin, this will always return "win32" like the native Python."""
@@ -116,6 +127,17 @@
   return (base_url, code)
 
 
+def check_platform(target):
+  """Checks if any parent directory of target matches (win|mac|linux)."""
+  assert os.path.isabs(target)
+  root, target_name = os.path.split(target)
+  if not target_name:
+    return None
+  if target_name in ('linux', 'mac', 'win'):
+    return target_name
+  return check_platform(root)
+
+
 def get_sha1(filename):
   sha1 = hashlib.sha1()
   with open(filename, 'rb') as f:
@@ -131,7 +153,8 @@
 # Download-specific code starts here
 
 def enumerate_work_queue(input_filename, work_queue, directory,
-                         recursive, ignore_errors, output, sha1_file):
+                         recursive, ignore_errors, output, sha1_file,
+                         auto_platform):
   if sha1_file:
     if not os.path.exists(input_filename):
       if not ignore_errors:
@@ -164,6 +187,20 @@
     for filename in files:
       full_path = os.path.join(root, filename)
       if full_path.endswith('.sha1'):
+        if auto_platform:
+          # Skip if the platform does not match.
+          target_platform = check_platform(os.path.abspath(full_path))
+          if not target_platform:
+            err = ('--auto_platform passed in but no platform name found in '
+                   'the path of %s' % full_path)
+            if not ignore_errors:
+              raise InvalidFileError(err)
+            print >> sys.stderr, err
+            continue
+          current_platform = PLATFORM_MAPPING[sys.platform]
+          if current_platform != target_platform:
+            continue
+
         with open(full_path, 'rb') as f:
           sha1_match = re.match('^([A-Za-z0-9]{40})$', f.read(1024).rstrip())
         if sha1_match:
@@ -240,7 +277,7 @@
 
 def download_from_google_storage(
     input_filename, base_url, gsutil, num_threads, directory, recursive,
-    force, output, ignore_errors, sha1_file, verbose):
+    force, output, ignore_errors, sha1_file, verbose, auto_platform):
   # Start up all the worker threads.
   all_threads = []
   download_start = time.time()
@@ -263,7 +300,7 @@
   # Enumerate our work queue.
   work_queue_size = enumerate_work_queue(
       input_filename, work_queue, directory, recursive,
-      ignore_errors, output, sha1_file)
+      ignore_errors, output, sha1_file, auto_platform)
   for _ in all_threads:
     work_queue.put((None, None))  # Used to tell worker threads to stop.
 
@@ -334,6 +371,12 @@
                     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.')
+  parser.add_option('-a', '--auto_platform',
+                    action='store_true',
+                    help='Detects if any parent folder of the target matches '
+                         '(linux|mac|win).  If so, the script will only '
+                         'process files that are in the paths that '
+                         'that matches the current platform.')
   parser.add_option('-v', '--verbose', action='store_true',
                     help='Output extra diagnostic and progress information.')
 
@@ -341,6 +384,8 @@
 
   # Make sure we should run at all based on platform matching.
   if options.platform:
+    if options.auto_platform:
+      parser.error('--platform can not be specified with --auto_platform')
     if not re.match(options.platform, GetNormalizedPlatform()):
       if options.verbose:
         print('The current platform doesn\'t match "%s", skipping.' %
@@ -378,6 +423,11 @@
     parser.error('--recursive specified but --directory not specified.')
   if options.output and options.directory:
     parser.error('--directory is specified, so --output has no effect.')
+  if (not (options.sha1_file or options.directory)
+      and options.auto_platform):
+    parser.error('--auto_platform must be specified with either '
+                 '--sha1_file or --directory')
+
   input_filename = args[0]
 
   # Set output filename if not specified.
@@ -410,7 +460,7 @@
   return download_from_google_storage(
       input_filename, base_url, gsutil, options.num_threads, options.directory,
       options.recursive, options.force, options.output, options.ignore_errors,
-      options.sha1_file, options.verbose)
+      options.sha1_file, options.verbose, options.auto_platform)
 
 
 if __name__ == '__main__':
diff --git a/tests/download_from_google_storage_unittests.py b/tests/download_from_google_storage_unittests.py
index e5e2055..abdca72 100755
--- a/tests/download_from_google_storage_unittests.py
+++ b/tests/download_from_google_storage_unittests.py
@@ -133,7 +133,7 @@
 
   def test_enumerate_files_non_recursive(self):
     queue_size = download_from_google_storage.enumerate_work_queue(
-        self.base_path, self.queue, True, False, False, None, False)
+        self.base_path, self.queue, True, False, False, None, False, False)
     expected_queue = [
         ('e6c4fbd4fe7607f3e6ebf68b2ea4ef694da7b4fe',
             os.path.join(self.base_path, 'rootfolder_text.txt')),
@@ -144,7 +144,7 @@
 
   def test_enumerate_files_recursive(self):
     queue_size = download_from_google_storage.enumerate_work_queue(
-        self.base_path, self.queue, True, True, False, None, False)
+        self.base_path, self.queue, True, True, False, None, False, False)
     expected_queue = [
         ('e6c4fbd4fe7607f3e6ebf68b2ea4ef694da7b4fe',
             os.path.join(self.base_path, 'rootfolder_text.txt')),
@@ -242,7 +242,8 @@
         output=output_filename,
         ignore_errors=False,
         sha1_file=False,
-        verbose=True)
+        verbose=True,
+        auto_platform=False)
     expected_calls = [
         ('check_call',
             ('ls', input_filename)),
@@ -273,7 +274,8 @@
         output=None,
         ignore_errors=False,
         sha1_file=False,
-        verbose=True)
+        verbose=True,
+        auto_platform=False)
     expected_calls = [
         ('check_call',
             ('ls', input_filename)),