forall.py 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. #
  2. # Copyright (C) 2008 The Android Open Source Project
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import re
  16. import os
  17. import sys
  18. import subprocess
  19. from command import Command
  20. class Forall(Command):
  21. common = False
  22. helpSummary = "Run a shell command in each project"
  23. helpUsage = """
  24. %prog [<project>...] -c <command> [<arg>...]
  25. """
  26. helpDescription = """
  27. Executes the same shell command in each project.
  28. Environment
  29. -----------
  30. pwd is the project's working directory.
  31. REPO_PROJECT is set to the unique name of the project.
  32. REPO_PATH is the path relative the the root of the client.
  33. REPO_REMOTE is the name of the remote system from the manifest.
  34. REPO_LREV is the name of the revision from the manifest, translated
  35. to a local tracking branch. If you need to pass the manifest
  36. revision to a locally executed git command, use REPO_LREV.
  37. REPO_RREV is the name of the revision from the manifest, exactly
  38. as written in the manifest.
  39. shell positional arguments ($1, $2, .., $#) are set to any arguments
  40. following <command>.
  41. stdin, stdout, stderr are inherited from the terminal and are
  42. not redirected.
  43. """
  44. def _Options(self, p):
  45. def cmd(option, opt_str, value, parser):
  46. setattr(parser.values, option.dest, list(parser.rargs))
  47. while parser.rargs:
  48. del parser.rargs[0]
  49. p.add_option('-c', '--command',
  50. help='Command (and arguments) to execute',
  51. dest='command',
  52. action='callback',
  53. callback=cmd)
  54. def Execute(self, opt, args):
  55. if not opt.command:
  56. self.Usage()
  57. cmd = [opt.command[0]]
  58. shell = True
  59. if re.compile(r'^[a-z0-9A-Z_/\.-]+$').match(cmd[0]):
  60. shell = False
  61. if shell:
  62. cmd.append(cmd[0])
  63. cmd.extend(opt.command[1:])
  64. rc = 0
  65. for project in self.GetProjects(args):
  66. env = dict(os.environ.iteritems())
  67. env['REPO_PROJECT'] = project.name
  68. env['REPO_PATH'] = project.relpath
  69. env['REPO_REMOTE'] = project.remote.name
  70. env['REPO_LREV'] = project\
  71. .GetRemote(project.remote.name)\
  72. .ToLocal(project.revision)
  73. env['REPO_RREV'] = project.revision
  74. p = subprocess.Popen(cmd,
  75. cwd = project.worktree,
  76. shell = shell,
  77. env = env)
  78. r = p.wait()
  79. if r != 0 and r != rc:
  80. rc = r
  81. if rc != 0:
  82. sys.exit(rc)