#!/usr/local/bin/python # written by CJ Bell """Create dependencies in makefiles for LilyPond scripts Usage: makedepend_ly [options] files... Options: -h / --help Print this message and exit. -f, --file=MAKEFILE Filename. This allows you to specify an alternate makefile in which makedepend_ly can place its output. Specifying ``-'' as the file name (i.e., -f-) sends the output to standard output instead of modifying an existing file. -I, --include=INCLUDEDIR Include directory. This option tells makedepend_ly to prepend INCLUDEDIR to its list of directories to search when it encounters an \include directive. By default, makedepend_ly only searches the standard include directories. -Y, --onlyinclude=INCLUDEDIR Replace all of the standard include directories with the single specified include directory. -o, -objsuffix=SUFFIX Object file suffix. Default is '.ps'. -a, --append Append the dependencies to the end of the file instead of replacing them. -s, --start=STRING Starting string delimiter. This option permits you to specify a different string for makedepend_ly to look for in the makefile. """ import tempfile import os import shutil import sys import getopt import fileinput import re # Pattern used to parse for a lilypond include inclfn = re.compile(r"[ \t]*\\include[ \t]+\"(?P(?:(?:\\[\"])|[^\"])*)\"") ntPath = 'C:/Program Files/LilyPond/usr/share/lilypond/current/ly' posixPath = '/usr/share/lilypond/current/ly' includePaths = [] standardIncludes = [] if os.path.exists(ntPath): standardIncludes.append(ntPath) if os.path.exists(posixPath): standardIncludes.append(posixPath) if 'relpath' not in dir(os.path): os.path.relpath = lambda p: p ############################################################################## def main(argv): global includePaths linkspaces = False append = False starting_str = '# DO NOT DELETE THIS LINE -- makedepend_ly depends on it.' objsuffix = '.ps' isphony = True if os.path.exists('makefile'): outputFile = 'makefile' else: outputFile = 'Makefile' try: longopts = ['help','file=','include=','onlyinclude=','linkspaces=','append','start=','objsuffix='] opts, files= getopt.getopt(argv, 'hf:I:Y:as:o:', longopts) except getopt.GetoptError: usage() sys.exit(2) for opt, arg in opts: if opt in ("-h", "--help"): usage() sys.exit() if opt in ('-f','--file'): outputFile = arg if opt in ('-I','--include'): if not os.path.exists(arg): print "Included directory " + arg + " doesn't exist" sys.exit(1) includePaths.append(arg) if opt in ('-Y','--onlyinclude'): if not os.path.exists(arg): print "Included directory " + arg + " doesn't exist" sys.exit(1) standardInclude = [arg] if opt == '--linkspaces': linkspaces = True ns_link_dir = arg if opt in ('-a','--append'): append = True if opt in ('-s','--start'): starting_string = arg if opt in ('-o','--objsuffix'): objsuffix = arg includePaths+= standardIncludes objfiles = set() for file in files: objfiles.add(os.path.join(os.path.abspath(file))) # Prevents cycles checked = set() # current directory cd = './' depends = dict() for objfile in objfiles: depends[objfile] = set() toCheck = set([objfile]) while toCheck: files = toCheck - checked toCheck = set() for file in files: included = parseFile(file, includePaths) depends[objfile]|= included toCheck |= included checked.add(file) # Make links for each filename with a space so it can be referred-to in the # makefile if linkspaces: try: os.mkdir(ns_link_dir) except EnvironmentError, err: print err new_keys = [] for file in depends.keys(): new_keys.append(linkNoSpace(file, ns_link_dir)) new_values = []; for deps in depends.values(): new_deps = [] for file in deps: new_deps.append(linkNoSpace(file, ns_link_dir)) new_values.append(new_deps) depends = dict(zip(new_keys, new_values)) # Output dependencies old_f = None if outputFile == '-': f = sys.stdout else: try: old_f = open(outputFile, 'r') except IOError, err: if err.errno != 2: pass f = open(outputFile, 'w') f.write(starting_str + '\n') else: # Copy the file up until the starting deliminator f = tempfile.TemporaryFile() startFound = False for line in old_f: f.write(line) if line == starting_str+'\n': startFound = True break if not startFound: f.write('\n' + starting_str + '\n') if not f: print "Cannot open " + outputFile + " for writing" sys.exit() for file, deps in depends.items(): (filebase,_) = os.path.splitext(os.path.relpath(file)) f.write(filebase+objsuffix + ': ' + os.path.relpath(file) + '\n\n') for dep in deps: f.write(filebase+objsuffix + ': ' + os.path.relpath(dep) + '\n\n') f.write('\n') if isphony and False: f.write('.PHONY '+filebase+objsuffix+'\n') if old_f: old_f.close() old_f = open(outputFile, 'w') f.seek(0) shutil.copyfileobj(f,old_f) old_f.close() if f != sys.stdout: f.close() # End main ############################################################################## ############################################################################## def usage(): print __doc__ # End usage ############################################################################## ############################################################################## def linkNoSpace(file, basedir): if ' ' not in file: return file new_filename = os.path.basename(file) if os.path.exists(os.path.join(basedir,new_filename)): if os.readlink(os.path.join(basedir,new_filename)) == file: return os.path.join(basedir,new_filename) n = 2 while os.path.exists(os.path.join(basedir,str(n) + '.' + new_filename)): n+= 1 new_filename = str(n) + '.' + new_filename os.symlink(file, os.path.join(basedir,new_filename)) return os.path.join(basedir,new_filename) # End linkNoSpace ############################################################################## ############################################################################## def parseFile(file, includePaths): includes = set() cd = os.path.dirname(file) for line in fileinput.input(file): try: f = parseLine(line, cd, includePaths) except EnvironmentError, err: print "In " + os.path.relpath(file) + ", " + err.strerror + ": " + err.filename else: if f is not None: includes.add(f) return includes # End parseFile ############################################################################## ############################################################################## def parseLine(line, cd, includePaths): global inclfn match = inclfn.match(line) if match is None: return None file = match.group('filename') # cheack each include path, then the file's directory for incl in includePaths+[cd]: f = os.path.join(incl,file) if os.path.exists(os.path.join(incl,file)): return f # included file can't be found raise EnvironmentError(-1,"cannot find included file", file) # End parseLine ############################################################################## if __name__ == "__main__": main(sys.argv[1:])