blob: 57793cbae3d440002718d52d8ba059bf074cce1c [file] [log] [blame]
Daniel Dunbara5677512009-01-05 19:53:30 +00001class Option(object):
2 """Root option class."""
3 def __init__(self, name):
4 self.name = name
5
6 def accept(self, index, arg, it):
7 """accept(index, arg, iterator) -> Arg or None
8
9 Accept the argument at the given index, returning an Arg, or
10 return None if the option does not accept this argument.
11
12 May raise MissingArgumentError.
13 """
14 abstract
15
16 def __repr__(self):
17 return '<%s name=%r>' % (self.__class__.__name__,
18 self.name)
19
20class FlagOption(Option):
21 """An option which takes no arguments."""
22
23 def accept(self, index, arg, it):
24 if arg == self.name:
25 return Arg(index, self)
26
27class JoinedOption(Option):
28 """An option which literally prefixes its argument."""
29
30 def accept(self, index, arg, it):
31 if arg.startswith(self.name):
32 return JoinedValueArg(index, self)
33
34class SeparateOption(Option):
35 """An option which is followed by its value."""
36
37 def accept(self, index, arg, it):
38 if arg == self.name:
39 try:
40 _,value = it.next()
41 except StopIteration:
42 raise MissingArgumentError,self
43 return SeparateValueArg(index, self)
44
45class MultiArgOption(Option):
46 """An option which takes multiple arguments."""
47
48 def __init__(self, name, numArgs):
49 assert numArgs > 1
50 super(MultiArgOption, self).__init__(name)
51 self.numArgs = numArgs
52
53 def accept(self, index, arg, it):
54 if arg.startswith(self.name):
55 try:
56 values = [it.next()[1] for i in range(self.numArgs)]
57 except StopIteration:
58 raise MissingArgumentError,self
59 return MultipleValuesArg(index, self)
60
61class JoinedOrSeparateOption(Option):
62 """An option which either literally prefixes its value or is
63 followed by an value."""
64
65 def accept(self, index, arg, it):
66 if arg.startswith(self.name):
67 if len(arg) != len(self.name): # Joined case
68 return JoinedValueArg(index, self)
69 else:
70 try:
71 _,value = it.next()
72 except StopIteration:
73 raise MissingArgumentError,self
74 return SeparateValueArg(index, self)
75
76class JoinedAndSeparateOption(Option):
77 """An option which literally prefixes its value and is followed by
78 an value."""
79
80 def accept(self, index, arg, it):
81 if arg.startswith(self.name):
82 try:
83 _,value = it.next()
84 except StopIteration:
85 raise MissingArgumentError,self
86 return JoinedAndSeparateValuesArg(index, self)
87
88###
89
90class Arg(object):
91 """Arg - Base class for actual driver arguments."""
92 def __init__(self, index, opt):
93 self.index = index
94 self.opt = opt
95
96 def __repr__(self):
97 return '<%s index=%r opt=%r>' % (self.__class__.__name__,
98 self.index,
99 self.opt)
100
101 def render(self, args):
102 """render(args) -> [str]
103
104 Map the argument into a list of actual program arguments,
105 given the source argument array."""
106 assert self.opt
107 return [self.opt.name]
108
109class ValueArg(Arg):
110 """ValueArg - An instance of an option which has an argument."""
111
112 def getValue(self, args):
113 abstract
114
115 def setValue(self, args, value):
116 abstract
117
118class UnknownArg(ValueArg):
119 def __init__(self, index):
120 super(UnknownArg, self).__init__(index, None)
121
122 def getValue(self, args):
123 return args[self.index]
124
125 def setValue(self, args, value):
126 args[self.index] = value
127
128 def render(self, args):
129 return [args[self.index]]
130
131class JoinedValueArg(ValueArg):
132 def getValue(self, args):
133 return args[self.index][len(self.opt.name):]
134
135 def setValue(self, args, value):
136 assert self.opt.name == args[self.index][:len(self.opt.name)]
137 args[self.index] = self.opt.name + value
138
139 def render(self, args):
140 return [self.opt.name + self.getValue(args)]
141
142class SeparateValueArg(ValueArg):
143 def getValue(self, args):
144 return args[self.index+1]
145
146 def setValue(self, args, value):
147 args[self.index+1] = value
148
149 def render(self, args):
150 return [self.opt.name, self.getValue(args)]
151
152class MultipleValuesArg(Arg):
153 def getValues(self, args):
154 return args[self.index + 1:self.index + 1 + self.opt.numArgs]
155
156 def setValues(self, args, value):
157 assert self.opt.numArgs == len(value)
158 args[self.index + 1:self.index + 1 + self.opt.numArgs] = value
159
160 def render(self, args):
161 return [self.opt.name] + self.getValues(args)
162
163# FIXME: Man, this is lame. It is only used by -Xarch. Maybe easier to
164# just special case?
165class JoinedAndSeparateValuesArg(Arg):
166 """JoinedAndSeparateValuesArg - An argument with both joined and
167 separate values."""
168
169 def getJoinedValue(self, args):
170 return args[self.index][len(self.opt.name):]
171
172 def getSeparateValue(self, args):
173 return args[self.index+1]
174
175 def setJoinedValue(self, args, value):
176 assert self.opt.name == args[self.index][:len(self.opt.name)]
177 args[self.index] = self.opt.name + value
178
179 def setSeparateValue(self, args, vaue):
180 args[self.index+1] = value
181
182 def render(self, args):
183 return ([self.opt.name + self.getJoinedValue(args)] +
184 [self.getSeparateValue(args)])
185
186class InputArg(ValueArg):
187 """InputArg - An input file (positional) argument."""
188
189 def __init__(self, index):
190 super(ValueArg, self).__init__(index, None)
191
192 def getValue(self, args):
193 return args[self.index]
194
195 def setValue(self, args, value):
196 args[self.index] = value
197
198 def render(self, args):
199 return [self.getValue(args)]
200
201class DerivedArg(ValueArg):
202 """DerivedArg - A synthesized argument which does not correspend
203 to the actual input arguments array."""
204
205 def __init__(self, value):
206 super(ValueArg, self).__init__(-1, None)
207 self.value = value
208
209 def getValue(self, args):
210 return self.value
211
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000212 def setValue(self, args, value):
Daniel Dunbara5677512009-01-05 19:53:30 +0000213 raise ValueError,"Cannot call setValue() on a DerivedArg."
214
215 def render(self, args):
216 return [self.value]
217
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000218class ArgList:
219 """ArgList - Collect an input argv along with a set of parsed Args
220 and supporting information."""
221
222 def __init__(self, argv):
223 self.argv = list(argv)
224 self.args = []
225
226 # Support use as a simple arg list.
227
228 def __iter__(self):
229 return iter(self.args)
230
231 def append(self, arg):
232 self.args.append(arg)
233
234 # Forwarding methods.
235
236 def getValue(self, arg):
237 return arg.getValue(self.argv)
238
239 def getValues(self, arg):
240 return arg.getValues(self.argv)
241
242 def getSeparateValue(self, arg):
243 return arg.getSeparateValue(self.argv)
244
245 def getJoinedValue(self, arg):
246 return arg.getJoinedValue(self.argv)
247
Daniel Dunbara5677512009-01-05 19:53:30 +0000248class OptionParser:
249 def __init__(self):
250 self.options = []
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000251
Daniel Dunbara5677512009-01-05 19:53:30 +0000252 def addOption(self, opt):
253 self.options.append(opt)
254
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000255 def parseArgs(self, argv):
Daniel Dunbara5677512009-01-05 19:53:30 +0000256 """
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000257 parseArgs([str]) -> ArgList
Daniel Dunbara5677512009-01-05 19:53:30 +0000258
259 Parse command line into individual option instances.
260 """
261
262 iargs = enumerate(argv)
263 it = iter(iargs)
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000264 args = ArgList(argv)
Daniel Dunbara5677512009-01-05 19:53:30 +0000265 for i,a in it:
266 # FIXME: Handle '@'
267 if not a:
268 # gcc's handling of empty arguments doesn't make
269 # sense, but this is not a common use case. :)
270 #
271 # We just ignore them here (note that other things may
272 # still take them as arguments).
273 pass
274 elif a[0] == '-' and a != '-':
275 args.append(self.lookupOptForArg(i, a, it))
276 else:
277 args.append(InputArg(i))
278 return args
279
280 def lookupOptForArg(self, i, arg, it):
281 for op in self.options:
282 opt = op.accept(i, arg, it)
283 if opt is not None:
284 return opt
285 return UnknownArg(i)
286
287def createOptionParser():
288 op = OptionParser()
289
290 # Driver driver options
291 op.addOption(SeparateOption('-arch'))
292
293 # Misc driver options
294 op.addOption(FlagOption('-pass-exit-codes'))
295 op.addOption(FlagOption('--help'))
296 op.addOption(FlagOption('--target-help'))
297
298 op.addOption(FlagOption('-dumpspecs'))
299 op.addOption(FlagOption('-dumpversion'))
300 op.addOption(FlagOption('-dumpmachine'))
301 op.addOption(FlagOption('-print-search-dirs'))
302 op.addOption(FlagOption('-print-libgcc-file-name'))
303 # FIXME: Hrm, where does this come from? It isn't always true that
304 # we take both - and --. For example, gcc --S ... ends up sending
305 # -fS to cc1. Investigate.
306 op.addOption(FlagOption('--print-libgcc-file-name'))
307 op.addOption(JoinedOption('-print-file-name='))
308 op.addOption(JoinedOption('-print-prog-name='))
309 op.addOption(JoinedOption('--print-prog-name='))
310 op.addOption(FlagOption('-print-multi-directory'))
311 op.addOption(FlagOption('-print-multi-lib'))
312 op.addOption(FlagOption('-print-multi-os-directory'))
313
314 # Hmmm, who really takes this?
315 op.addOption(FlagOption('--version'))
316
317 # Pipeline control
318 op.addOption(FlagOption('-###'))
319 op.addOption(FlagOption('-E'))
320 op.addOption(FlagOption('-S'))
321 op.addOption(FlagOption('-c'))
322 op.addOption(FlagOption('-combine'))
323 op.addOption(FlagOption('-no-integrated-cpp'))
324 op.addOption(FlagOption('-pipe'))
325 op.addOption(FlagOption('-save-temps'))
326 op.addOption(FlagOption('--save-temps'))
327 op.addOption(JoinedOption('-specs='))
328 op.addOption(FlagOption('-time'))
329 op.addOption(FlagOption('-v'))
330
331 # Input/output stuff
332 op.addOption(JoinedOrSeparateOption('-o'))
333 op.addOption(JoinedOrSeparateOption('-x'))
334
335 # FIXME: What do these actually do? The documentation is less than
336 # clear.
337 op.addOption(FlagOption('-ObjC'))
338 op.addOption(FlagOption('-ObjC++'))
339
340 # FIXME: Weird, gcc claims this here in help but I'm not sure why;
341 # perhaps interaction with preprocessor? Investigate.
342 op.addOption(JoinedOption('-std='))
343 op.addOption(JoinedOrSeparateOption('--sysroot'))
344
345 # Version control
346 op.addOption(JoinedOrSeparateOption('-B'))
347 op.addOption(JoinedOrSeparateOption('-V'))
348 op.addOption(JoinedOrSeparateOption('-b'))
349
350 # Blanket pass-through options.
351
352 op.addOption(JoinedOption('-Wa,'))
353 op.addOption(SeparateOption('-Xassembler'))
354
355 op.addOption(JoinedOption('-Wp,'))
356 op.addOption(SeparateOption('-Xpreprocessor'))
357
358 op.addOption(JoinedOption('-Wl,'))
359 op.addOption(SeparateOption('-Xlinker'))
360
361 ####
362 # Bring on the random garbage.
363
364 op.addOption(FlagOption('-MD'))
365 op.addOption(FlagOption('-MP'))
366 op.addOption(FlagOption('-MM'))
367 op.addOption(JoinedOrSeparateOption('-MF'))
368 op.addOption(JoinedOrSeparateOption('-MT'))
369 op.addOption(FlagOption('-undef'))
370
371 op.addOption(FlagOption('-w'))
372 op.addOption(JoinedOrSeparateOption('-allowable_client'))
373 op.addOption(JoinedOrSeparateOption('-client_name'))
374 op.addOption(JoinedOrSeparateOption('-compatibility_version'))
375 op.addOption(JoinedOrSeparateOption('-current_version'))
376 op.addOption(JoinedOrSeparateOption('-exported_symbols_list'))
377 op.addOption(JoinedOrSeparateOption('-idirafter'))
378 op.addOption(JoinedOrSeparateOption('-iquote'))
379 op.addOption(JoinedOrSeparateOption('-isysroot'))
380 op.addOption(JoinedOrSeparateOption('-keep_private_externs'))
381 op.addOption(JoinedOrSeparateOption('-seg1addr'))
382 op.addOption(JoinedOrSeparateOption('-segprot'))
383 op.addOption(JoinedOrSeparateOption('-sub_library'))
384 op.addOption(JoinedOrSeparateOption('-sub_umbrella'))
385 op.addOption(JoinedOrSeparateOption('-umbrella'))
386 op.addOption(JoinedOrSeparateOption('-undefined'))
387 op.addOption(JoinedOrSeparateOption('-unexported_symbols_list'))
388 op.addOption(JoinedOrSeparateOption('-weak_framework'))
389 op.addOption(JoinedOption('-headerpad_max_install_names'))
390 op.addOption(FlagOption('-twolevel_namespace'))
391 op.addOption(FlagOption('-prebind'))
392 op.addOption(FlagOption('-prebind_all_twolevel_modules'))
393 op.addOption(FlagOption('-single_module'))
394 op.addOption(FlagOption('-nomultidefs'))
395 op.addOption(FlagOption('-nostdlib'))
396 op.addOption(FlagOption('-nostdinc'))
397 op.addOption(FlagOption('-static'))
398 op.addOption(FlagOption('-shared'))
399 op.addOption(FlagOption('-C'))
400 op.addOption(FlagOption('-CC'))
401 op.addOption(FlagOption('-R'))
402 op.addOption(FlagOption('-P'))
403 op.addOption(FlagOption('-all_load'))
404 op.addOption(FlagOption('--constant-cfstrings'))
405 op.addOption(FlagOption('-traditional'))
406 op.addOption(FlagOption('--traditional'))
407 op.addOption(FlagOption('-no_dead_strip_inits_and_terms'))
408 op.addOption(MultiArgOption('-sectalign', numArgs=3))
409 op.addOption(MultiArgOption('-sectcreate', numArgs=3))
410 op.addOption(MultiArgOption('-sectorder', numArgs=3))
411
412 # I dunno why these don't end up working when joined. Maybe
413 # because of translation?
414 op.addOption(SeparateOption('-filelist'))
415 op.addOption(SeparateOption('-framework'))
416 op.addOption(SeparateOption('-install_name'))
417 op.addOption(SeparateOption('-seg_addr_table'))
418 op.addOption(SeparateOption('-seg_addr_table_filename'))
419
420 # Where are these coming from? I can't find them...
421 op.addOption(JoinedOrSeparateOption('-e')) # Gets forwarded to linker
422 op.addOption(JoinedOrSeparateOption('-r'))
423
424 # Is this actually declared anywhere? I can only find it in a
425 # spec. :(
426 op.addOption(FlagOption('-pg'))
427
428 doNotReallySupport = 1
429 if doNotReallySupport:
430 # Archaic gcc option.
431 op.addOption(FlagOption('-cpp-precomp'))
432 op.addOption(FlagOption('-no-cpp-precomp'))
433
434 # C options for testing
435
436 op.addOption(JoinedOrSeparateOption('-include'))
437 op.addOption(JoinedOrSeparateOption('-A'))
438 op.addOption(JoinedOrSeparateOption('-D'))
439 op.addOption(JoinedOrSeparateOption('-F'))
440 op.addOption(JoinedOrSeparateOption('-I'))
441 op.addOption(JoinedOrSeparateOption('-L'))
442 op.addOption(JoinedOrSeparateOption('-U'))
443 op.addOption(JoinedOrSeparateOption('-l'))
444 op.addOption(JoinedOrSeparateOption('-u'))
445
446 # FIXME: What is going on here? '-X' goes to linker, and -X ... goes nowhere?
447 op.addOption(FlagOption('-X'))
448 # Not exactly sure how to decompose this. I split out -Xarch_
449 # because we need to recognize that in the driver driver part.
450 # FIXME: Man, this is lame it needs its own option.
451 op.addOption(JoinedAndSeparateOption('-Xarch_'))
452 op.addOption(JoinedOption('-X'))
453
454 # The driver needs to know about this flag.
455 op.addOption(FlagOption('-fsyntax-only'))
456
457 # FIXME: Wrong?
458 # FIXME: What to do about the ambiguity of options like
459 # -dumpspecs? How is this handled in gcc?
460 op.addOption(JoinedOption('-d'))
461 op.addOption(JoinedOption('-g'))
462 op.addOption(JoinedOption('-f'))
463 op.addOption(JoinedOption('-m'))
464 op.addOption(JoinedOption('-i'))
465 op.addOption(JoinedOption('-O'))
466 op.addOption(JoinedOption('-W'))
467 # FIXME: Weird. This option isn't really separate, --param=a=b
468 # works. An alias somewhere?
469 op.addOption(SeparateOption('--param'))
470
471 # FIXME: What is this? Seems to do something on Linux. I think
472 # only one is valid, but have a log that uses both.
473 op.addOption(FlagOption('-pthread'))
474 op.addOption(FlagOption('-pthreads'))
475
476 return op