Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 # Copyright (c) 2013 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Fixup GCC-generated dependency files.
      7 
      8 Modify GCC generated dependency files so they are more suitable for including
      9 in a GNU Makefile.  Without the fixups, deleting or renaming headers can cause
     10 the build to be broken.
     11 
     12 See http://mad-scientist.net/make/autodep.html for more details of the problem.
     13 """
     14 
     15 import os
     16 import optparse
     17 import sys
     18 
     19 TAG_LINE = '# Updated by fix_deps.py\n'
     20 
     21 
     22 class Error(Exception):
     23   pass
     24 
     25 
     26 def ParseLine(line, new_target):
     27   """Parse one line of a GCC-generated deps file.
     28 
     29   Each line contains an optional target and then a list
     30   of space seperated dependencies.  Spaces within filenames
     31   are escaped with a backslash.
     32   """
     33   filenames = []
     34 
     35   if new_target and ':' in line:
     36     line = line.split(':', 1)[1]
     37 
     38   line = line.strip()
     39   line = line.rstrip('\\')
     40 
     41   while True:
     42     # Find the next non-escaped space
     43     line = line.strip()
     44     pos = line.find(' ')
     45     while pos > 0 and line[pos-1] == '\\':
     46       pos = line.find(' ', pos+1)
     47 
     48     if pos == -1:
     49       filenames.append(line)
     50       break
     51     filenames.append(line[:pos])
     52     line = line[pos+1:]
     53 
     54   return filenames
     55 
     56 
     57 def FixupDepFile(filename, output_filename=None):
     58   if not os.path.exists(filename):
     59     raise Error('File not found: %s' % filename)
     60 
     61   if output_filename is None:
     62     output_filename = filename
     63 
     64   outlines = [TAG_LINE]
     65   deps = []
     66   new_target = True
     67   with open(filename) as infile:
     68     for line in infile:
     69       if line == TAG_LINE:
     70         raise Error('Already processed: %s' % filename)
     71       outlines.append(line)
     72       deps += ParseLine(line, new_target)
     73       new_target = line.endswith('\\')
     74 
     75   # For every depenency found output a dummy target with no rules
     76   for dep in deps:
     77     outlines.append('%s:\n' % dep)
     78 
     79   with open(output_filename, 'w') as outfile:
     80     for line in outlines:
     81       outfile.write(line)
     82 
     83 
     84 def main(argv):
     85   usage = "usage: %prog [options] <dep_file>"
     86   parser = optparse.OptionParser(usage=usage, description=__doc__)
     87   parser.add_option('-o', '--output', help='Output filename (defaults to '
     88                     'input name with .deps extension')
     89   parser.add_option('-c', '--clean', action='store_true',
     90                     help='Remove input file after writing output')
     91   options, args = parser.parse_args(argv)
     92   if not args:
     93     raise parser.error('No input file specified')
     94   if len(args) > 1:
     95     raise parser.error('Only one argument supported')
     96   input_filename = args[0]
     97   output_filename = options.output
     98   if not output_filename:
     99     output_filename = os.path.splitext(input_filename)[0] + '.deps'
    100   FixupDepFile(input_filename, output_filename)
    101   if options.clean and input_filename != output_filename:
    102     os.remove(input_filename)
    103 
    104 
    105 if __name__ == '__main__':
    106   try:
    107     sys.exit(main(sys.argv[1:]))
    108   except Error as e:
    109     sys.stderr.write('%s: %s\n' % (os.path.basename(__file__), e))
    110     sys.exit(1)
    111