forall.py 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  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, MirrorSafeCommand
  20. class Forall(Command, MirrorSafeCommand):
  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. If the current client is
  31. a mirror client, then pwd is the Git repository.
  32. REPO_PROJECT is set to the unique name of the project.
  33. REPO_PATH is the path relative the the root of the client.
  34. REPO_REMOTE is the name of the remote system from the manifest.
  35. REPO_LREV is the name of the revision from the manifest, translated
  36. to a local tracking branch. If you need to pass the manifest
  37. revision to a locally executed git command, use REPO_LREV.
  38. REPO_RREV is the name of the revision from the manifest, exactly
  39. as written in the manifest.
  40. shell positional arguments ($1, $2, .., $#) are set to any arguments
  41. following <command>.
  42. stdin, stdout, stderr are inherited from the terminal and are
  43. not redirected.
  44. """
  45. def _Options(self, p):
  46. def cmd(option, opt_str, value, parser):
  47. setattr(parser.values, option.dest, list(parser.rargs))
  48. while parser.rargs:
  49. del parser.rargs[0]
  50. p.add_option('-c', '--command',
  51. help='Command (and arguments) to execute',
  52. dest='command',
  53. action='callback',
  54. callback=cmd)
  55. def Execute(self, opt, args):
  56. if not opt.command:
  57. self.Usage()
  58. cmd = [opt.command[0]]
  59. shell = True
  60. if re.compile(r'^[a-z0-9A-Z_/\.-]+$').match(cmd[0]):
  61. shell = False
  62. if shell:
  63. cmd.append(cmd[0])
  64. cmd.extend(opt.command[1:])
  65. mirror = self.manifest.IsMirror
  66. rc = 0
  67. for project in self.GetProjects(args):
  68. env = dict(os.environ.iteritems())
  69. def setenv(name, val):
  70. if val is None:
  71. val = ''
  72. env[name] = val
  73. setenv('REPO_PROJECT', project.name)
  74. setenv('REPO_PATH', project.relpath)
  75. setenv('REPO_REMOTE', project.remote.name)
  76. setenv('REPO_LREV', project\
  77. .GetRemote(project.remote.name)\
  78. .ToLocal(project.revision))
  79. setenv('REPO_RREV', project.revision)
  80. if mirror:
  81. setenv('GIT_DIR', project.gitdir)
  82. cwd = project.gitdir
  83. else:
  84. cwd = project.worktree
  85. p = subprocess.Popen(cmd,
  86. cwd = cwd,
  87. shell = shell,
  88. env = env)
  89. r = p.wait()
  90. if r != 0 and r != rc:
  91. rc = r
  92. if rc != 0:
  93. sys.exit(rc)