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