11320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci# Copyright 2014 The Chromium Authors. All rights reserved.
21320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci# Use of this source code is governed by a BSD-style license that can be
31320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci# found in the LICENSE file.
41320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci"""Writes the most recent "Cr-Commit-Position" value on the master branch
61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccito a C header file.
71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciUsage: last_commit_position.py <dir> <outfile> <headerguard>
91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  <dir>
111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    Some directory inside the repo to check. This will be used as the current
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    directory when running git. It's best to pass the repo toplevel directory.
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  <outfile>
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    C header file to write.
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  <headerguard>
181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    String to use as the header guard for the written file.
191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci"""
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport os
221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport re
231320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport subprocess
241320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciimport sys
251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
261320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccidef RunGitCommand(directory, command):
271320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  """
281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  Launches git subcommand.
291320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  Errors are swallowed.
311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  Returns:
331320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    A process object or None.
341320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  """
351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  command = ['git'] + command
361320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # Force shell usage under cygwin. This is a workaround for
371320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # mysterious loss of cwd while invoking cygwin's git.
381320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # We can't just pass shell=True to Popen, as under win32 this will
391320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # cause CMD to be used, while we explicitly want a cygwin shell.
401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if sys.platform == 'cygwin':
411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    command = ['sh', '-c', ' '.join(command)]
421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  try:
431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    proc = subprocess.Popen(command,
441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                            stdout=subprocess.PIPE,
451320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                            stderr=subprocess.PIPE,
461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                            cwd=directory,
471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                            shell=(sys.platform=='win32'))
481320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return proc
491320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  except OSError:
501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return None
511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
521320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccidef FetchCommitPosition(directory):
541320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  regex = re.compile(r'\s*Cr-Commit-Position: refs/heads/master@\{#(\d+)\}\s*')
551320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
561320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # Search this far backward in the git log. The commit position should be
571320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # close to the top. We allow some slop for long commit messages, and maybe
581320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # there were some local commits after the last "official" one. Having this
591320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  # max prevents us from searching all history in the case of an error.
601320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  max_lines = 2048
611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
621320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  proc = RunGitCommand(directory, ['log'])
631320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  for i in range(max_lines):
641320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    line = proc.stdout.readline()
651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if not line:
661320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return None
671320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
681320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    match = regex.match(line)
691320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if match:
701320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      return match.group(1)
711320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return None
731320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
741320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccidef WriteHeader(header_file, header_guard, value):
761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  with open(header_file, 'w') as f:
771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    f.write('''/* Generated by last_commit_position.py. */
781320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#ifndef %(guard)s
801320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#define %(guard)s
811320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
821320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#define LAST_COMMIT_POSITION "%(value)s"
831320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#endif
851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci''' % {'guard': header_guard, 'value': value})
861320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciif len(sys.argv) != 4:
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  print "Wrong number of arguments"
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  sys.exit(1)
911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
921320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccigit_directory = sys.argv[1]
931320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccioutput_file = sys.argv[2]
941320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciheader_guard = sys.argv[3]
951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
961320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivalue = FetchCommitPosition(git_directory)
971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucciif not value:
981320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  print "Could not get last commit position."
991320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  sys.exit(1)
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano TucciWriteHeader(output_file, header_guard, value)
102