2 # -*- coding: iso-8859-1 -*-
3 # Copyright © 2003-2008, The AROS Development Team. All rights reserved.
5 import sys
, re
, os
, errno
7 if not len(sys
.argv
) in [2, 3, 4, 5] :
8 print "Usage:",sys
.argv
[0],"tmplfile [inputfile outputfile]"
9 print "Usage:",sys
.argv
[0],"tmplfile --usetmp [inputfile outputfile]"
10 print "Usage:",sys
.argv
[0],"tmplfile --listfile filename"
11 print "Usage:",sys
.argv
[0],"tmplfile --usetmp --listfile filename"
13 # A regular expression for the start of a template instantiation (ex. %build_module)
14 re_tmplinst
= re
.compile('%([a-zA-Z0-9][a-zA-Z0-9_]*)(?=(?:\s|$))')
15 # A regular expression for the argument specification during template instantiation
16 # (ex. cflags=$(CFLAGS) or uselibs="amiga arosc")
17 re_arg
= re
.compile('([a-zA-Z0-9][a-zA-Z0-9_]*)=([^\s"]+|".*?")?')
19 ##################################
20 # Class and function definitions #
21 ##################################
23 # Exception used throughout this program
25 def __init__(self
, s
):
31 # Scan the given lines for template instantiations
33 # - lines: an array of strings
34 # - templates: an assosiative array of objects of class template
37 # - templrefs: an array of tuples with two elements. First element is the line number
38 # in the lines array, second is the result of the search for that line with
39 # re_tmplinst. Only templates which name are present in the templates argument
40 # will be added to this array.
41 def generate_templrefs(lines
, templates
):
44 for lineno
in range(len(lines
)):
46 if len(line
) == 0 or line
[0] == "#":
49 m
= re_tmplinst
.search(line
)
50 if m
and templates
.has_key(m
.group(1)) and not (m
.start() > 0 and line
[m
.start()-1] == "#"):
51 templrefs
.append((lineno
, m
))
56 # Write out the lines to a file with instantiating the templates present in the file
58 # - lines: The lines to write out
59 # - templrefs: the instantiated templates present in these lines
60 # - templates: the template definitions
61 # - outfile: the file to write to
63 # This function does not return anything but raises a GenmfException when there are
65 def writelines(lines
, templrefs
, templates
, outfile
):
67 for lineno
, m
in templrefs
:
69 outfile
.writelines(lines
[start
:lineno
])
73 while line
[len(line
)-2] == "\\" and start
< len(lines
):
74 line
= line
[0:len(line
)-2] + lines
[start
]
77 if m
.group(1) == "common":
78 template
.hascommon
= 1
81 templates
[m
.group(1)].write(outfile
, m
.group(1), line
[m
.end():].lstrip(), templates
)
82 except GenmfException
, ge
:
83 raise GenmfException(("In instantiation of %s, line %d\n" % (m
.group(1), lineno
+1))+ge
.s
)
85 if start
< len(lines
):
86 outfile
.writelines(lines
[start
:len(lines
)])
89 # arg is a class that stores the specification of an argument in the template header.
90 # The name of the arg is not stored in this class but this class is supposed to be
91 # stored in an assosiative array with the names of the arg as indices. E.g
92 # arg['cflags'] gives back an object of this class.
93 # It has the following members:
94 # - ismulti: boolean to indicate if it has the /M definition
95 # - isneeded: boolean to indicate if it has the /A definition
96 # - used: boolean to indicate if a value is used in the body of a template
97 # - default: default value when the argument is not given during a template
99 # - value: The value this argument has during a template instantiation is stored
102 # Specification can end with /A or /M
103 re_mode
= re
.compile('/(A|M)')
105 # You create this object with giving it the default value
106 def __init__(self
, default
=None):
111 while default
and len(default
)>1:
112 m
= arg
.re_mode
.match(default
[len(default
)-2:])
115 if m
.group(1) == "M":
117 elif m
.group(1) == "A":
120 sys
.exit('Internal error: Unknown match')
122 default
= default
[:len(default
)-2]
124 if default
and default
[0] == '"':
125 default
= default
[1:len(default
)-1]
127 self
.default
= default
128 # The value field will get the value passed to the argument when the template is used
132 # template is a class to store the whole definition of a genmf template
134 # - name: name of the template
135 # - args: an associative array of the arguments of this template
136 # - body: an array of strings with the body of the template
137 # - multiarg: contains the arg with /M if it is present
138 # - used: Is this template already used; used to check for recursive calling
140 # - linerefs: an array to indicate the genmf variables used in the body of the
141 # template. This is generated with the generate_linerefs method of this class
142 # - templrefs: an array to indicate the templates used in the body of the template.
143 # This is generated with the generate_templrefs function of this class.
145 re_arginst
= re
.compile('([a-zA-Z0-9-_]*)%\(([a-zA-Z0-9][a-zA-Z0-9_]*)\)([a-zA-Z0-9-_]*)')
148 # Generate a template
150 # - name: name of the template
151 # - args: an assosiative array of the arguments defined for this template
152 # - body: an array of the template with the bodylines of the template
153 def __init__(self
, name
, args
, body
):
160 self
.templrefs
= None
162 for argname
, argbody
in args
.items():
165 sys
.exit('A template can have only one main (/M) argument')
166 self
.multiarg
= argbody
168 # Generate the references for the genmf variable used in this template
169 # This function will return an assositive array of tuples with linerefs[lineno]
170 # an array of tuples named argrefs with the tuple of the form (argbody, start, stop, prefix, suffix)
171 # with argbody an object of class arg, start and stop the start and end of this variable
172 # in the string of this line.
173 def generate_linerefs(self
):
176 while lineno
< len(self
.body
):
178 for m
in template
.re_arginst
.finditer(self
.body
[lineno
]):
179 if self
.args
.has_key(m
.group(2)):
180 argbody
= self
.args
[m
.group(2)]
181 argrefs
.append((argbody
, m
.start(), m
.end(),m
.group(1),m
.group(3)))
185 linerefs
[lineno
] = argrefs
188 self
.linerefs
= linerefs
190 for argname
, argbody
in self
.args
.items():
192 sys
.stderr
.write("Warning: template '%s': unused argument '%s'\n" % (self
.name
, argname
))
195 # Write out the body of the template
196 def write(self
, outfile
, name
, line
, templates
):
198 raise GenmfException("Template '%s' called recursively" % name
)
201 # Reading arguments of the template
204 m
= re_arg
.match(line
)
205 if m
and self
.args
.has_key(m
.group(1)):
208 #sys.stderr.write("Arg:"+m.group(1)+" Value: None Line:"+line+"\n")
209 self
.args
[m
.group(1)].value
= ''
211 #sys.stderr.write("Arg:"+m.group(1)+" Value:"+m.group(2)+" Line:"+line+"\n")
212 if len(value
)>0 and value
[0] == '"':
213 value
= value
[1:len(value
)-1]
214 self
.args
[m
.group(1)].value
= value
215 line
= line
[m
.end():].lstrip()
217 self
.multiarg
.value
= line
[:len(line
)-1]
220 raise GenmfException('Syntax error in arguments: '+line
)
222 if self
.linerefs
== None:
223 self
.generate_linerefs()
224 self
.templrefs
= generate_templrefs(self
.body
, templates
)
226 for argname
, argbody
in self
.args
.items():
227 if argbody
.isneeded
and argbody
.value
== None:
228 raise GenmfException('Arg "%s" not specified but should have been' % argname
)
232 for lineno
, argrefs
in self
.linerefs
.items():
237 for argref
in argrefs
:
239 lineout
= lineout
+ line
[pos
:argref
[1]]
242 if not argref
[0].value
== None:
243 for val
in argref
[0].value
.split():
244 linevals
.append(argref
[3] + val
+ argref
[4])
245 elif argref
[0].default
:
246 for val
in argref
[0].default
.split():
247 linevals
.append(argref
[3] + val
+ argref
[4])
249 if len(linevals
) == 0:
250 lineout
+= argref
[3] + argref
[4]
252 lineout
+= " ".join(linevals
)
257 lineout
= lineout
+ line
[pos
:]
259 text
[lineno
] = lineout
261 writelines(text
, self
.templrefs
, templates
, outfile
)
264 for argname
, argbody
in self
.args
.items():
270 # Read in the definition of the genmf templates from the given filename
271 # Return an assosiative array of the templates present in this file.
272 def read_templates(filename
):
274 infile
= open(filename
)
276 print "Error reading template file: "+filename
278 re_name
= re
.compile('[a-zA-Z0-9][a-zA-Z0-9_]*(?=(?:\s|$))')
279 re_openstring
= re
.compile('[^\s"]*"[^"]*$')
280 re_define
= re
.compile('%define(?=\s)')
282 lines
= infile
.readlines()
285 while lineno
< len(lines
):
287 if re_define
.match(line
):
288 while line
[len(line
)-2] == "\\" and lineno
< len(lines
):
290 line
= line
[0:len(line
)-2] + lines
[lineno
]
292 line
= line
[7:].strip()
294 m
= re_name
.match(line
)
296 sys
.exit("%s:%d:Error in syntax of template name" % (filename
, lineno
+1))
297 tmplname
= m
.group(0)
298 line
= line
[m
.end():].lstrip()
302 m
= re_arg
.match(line
)
304 sys
.exit("%s:%d:Error in syntax of argument %d Line: %s" % (filename
, lineno
+1, len(args
)+1, line
))
305 args
[m
.group(1)] = arg(m
.group(2))
307 line
= line
[m
.end():].lstrip()
309 #print "Line: %d Template: %s" % (lineno+1, tmplname)
314 while lineno
< len(lines
) and line
[0:4] <> "%end":
318 if lineno
== len(lines
):
319 sys
.exit('%s:End of file reached in a template definition' % filename
)
321 templates
[tmplname
] = template(tmplname
, args
, lines
[bodystart
:lineno
])
338 while i
< len(sys
.argv
):
339 if sys
.argv
[i
] == "--listfile":
340 listfile
= sys
.argv
[i
+1]
342 elif sys
.argv
[i
] == "--usetmp":
347 argv
.append(sys
.argv
[i
])
350 #sys.stderr.write("Reading templates\n")
351 templates
= read_templates(argv
[1])
352 #sys.stderr.write("Read %d templates\n" % len(templates))
355 # Read one input file and write out one outputfile
356 if len(sys
.argv
) == argin
:
357 lines
= sys
.stdin
.readlines()
359 infile
= open(sys
.argv
[argin
], "r")
360 lines
= infile
.readlines()
363 if len(sys
.argv
) == argin
:
368 outfile
= open(sys
.argv
[argin
+ 1]+"tmp", "w")
370 outfile
= open(sys
.argv
[argin
+ 1], "w")
374 writelines(lines
, generate_templrefs(lines
, templates
), templates
, outfile
)
375 except GenmfException
, ge
:
377 if len(sys
.argv
) == argin
+ 2:
378 s
= sys
.argv
[argin
+ 1]+":"+s
381 # If %common was not present in the file write it out at the end of the file
382 if not template
.hascommon
:
384 if templates
.has_key("common"):
385 templates
["common"].write(outfile
, "common", "", templates
)
390 if os
.path
.isfile(files
[1]):
391 os
.remove(sys
.argv
[argin
+ 1])
392 os
.rename(sys
.argv
[argin
+ 1]+"tmp", sys
.argv
[argin
+ 1])
395 # When a listfile is specified each line in this listfile is of the form
396 # inputfile outputfile
397 # Apply the instantiation of the templates to all these files listed there
398 infile
= open(listfile
, "r")
399 filelist
= infile
.readlines()
402 sys
.stderr
.write('Regenerating %d files\n' % (len(filelist
)))
404 for fileno
in range(len(filelist
)):
405 files
= filelist
[fileno
].split()
407 sys
.exit('%s:%d: Syntax error: %s' % (listfile
, fileno
+1, filelist
[fileno
]))
409 progcount
= progcount
+ 1
412 sys
.stderr
.write('.')
415 infile
= open(files
[0], "r")
416 lines
= infile
.readlines()
420 # os.makedirs will also create all the parent directories
421 os
.makedirs(os
.path
.dirname(files
[1]))
427 outfile
= open(files
[1]+"tmp", "w")
429 outfile
= open(files
[1], "w")
431 template
.hascommon
= 0
434 writelines(lines
, generate_templrefs(lines
, templates
), templates
, outfile
)
435 except GenmfException
, ge
:
437 if len(sys
.argv
) == argin
+ 2:
441 if not template
.hascommon
:
443 if templates
.has_key("common"):
444 templates
["common"].write(outfile
, "common", "", templates
)
448 if os
.path
.isfile(files
[1]):
450 os
.rename(files
[1]+"tmp", files
[1])
452 sys
.stderr
.write('\n')