1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Writes the most recent "Cr-Commit-Position" value on the master branch
6to a C header file.
7
8Usage: last_commit_position.py <dir> <outfile> <headerguard>
9
10  <dir>
11    Some directory inside the repo to check. This will be used as the current
12    directory when running git. It's best to pass the repo toplevel directory.
13
14  <outfile>
15    C header file to write.
16
17  <headerguard>
18    String to use as the header guard for the written file.
19"""
20
21import os
22import re
23import subprocess
24import sys
25
26def RunGitCommand(directory, command):
27  """
28  Launches git subcommand.
29
30  Errors are swallowed.
31
32  Returns:
33    A process object or None.
34  """
35  command = ['git'] + command
36  # Force shell usage under cygwin. This is a workaround for
37  # mysterious loss of cwd while invoking cygwin's git.
38  # We can't just pass shell=True to Popen, as under win32 this will
39  # cause CMD to be used, while we explicitly want a cygwin shell.
40  if sys.platform == 'cygwin':
41    command = ['sh', '-c', ' '.join(command)]
42  try:
43    proc = subprocess.Popen(command,
44                            stdout=subprocess.PIPE,
45                            stderr=subprocess.PIPE,
46                            cwd=directory,
47                            shell=(sys.platform=='win32'))
48    return proc
49  except OSError:
50    return None
51
52
53def FetchCommitPosition(directory):
54  regex = re.compile(r'\s*Cr-Commit-Position: refs/heads/master@\{#(\d+)\}\s*')
55
56  # Search this far backward in the git log. The commit position should be
57  # close to the top. We allow some slop for long commit messages, and maybe
58  # there were some local commits after the last "official" one. Having this
59  # max prevents us from searching all history in the case of an error.
60  max_lines = 2048
61
62  proc = RunGitCommand(directory, ['log'])
63  for i in range(max_lines):
64    line = proc.stdout.readline()
65    if not line:
66      return None
67
68    match = regex.match(line)
69    if match:
70      return match.group(1)
71
72  return None
73
74
75def WriteHeader(header_file, header_guard, value):
76  with open(header_file, 'w') as f:
77    f.write('''/* Generated by last_commit_position.py. */
78
79#ifndef %(guard)s
80#define %(guard)s
81
82#define LAST_COMMIT_POSITION "%(value)s"
83
84#endif
85''' % {'guard': header_guard, 'value': value})
86
87
88if len(sys.argv) != 4:
89  print "Wrong number of arguments"
90  sys.exit(1)
91
92git_directory = sys.argv[1]
93output_file = sys.argv[2]
94header_guard = sys.argv[3]
95
96value = FetchCommitPosition(git_directory)
97if not value:
98  print "Could not get last commit position."
99  sys.exit(1)
100
101WriteHeader(output_file, header_guard, value)
102