blob: ef8466fd6232e85886ca0860b49bf406ebee8103 [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
212 def setValue(self, args):
213 raise ValueError,"Cannot call setValue() on a DerivedArg."
214
215 def render(self, args):
216 return [self.value]
217
218class OptionParser:
219 def __init__(self):
220 self.options = []
221
222 def addOption(self, opt):
223 self.options.append(opt)
224
225 def chunkArgs(self, argv):
226 """
227 chunkArgs([str]) -> [Arg]
228
229 Parse command line into individual option instances.
230 """
231
232 iargs = enumerate(argv)
233 it = iter(iargs)
234 args = []
235 for i,a in it:
236 # FIXME: Handle '@'
237 if not a:
238 # gcc's handling of empty arguments doesn't make
239 # sense, but this is not a common use case. :)
240 #
241 # We just ignore them here (note that other things may
242 # still take them as arguments).
243 pass
244 elif a[0] == '-' and a != '-':
245 args.append(self.lookupOptForArg(i, a, it))
246 else:
247 args.append(InputArg(i))
248 return args
249
250 def lookupOptForArg(self, i, arg, it):
251 for op in self.options:
252 opt = op.accept(i, arg, it)
253 if opt is not None:
254 return opt
255 return UnknownArg(i)
256
257def createOptionParser():
258 op = OptionParser()
259
260 # Driver driver options
261 op.addOption(SeparateOption('-arch'))
262
263 # Misc driver options
264 op.addOption(FlagOption('-pass-exit-codes'))
265 op.addOption(FlagOption('--help'))
266 op.addOption(FlagOption('--target-help'))
267
268 op.addOption(FlagOption('-dumpspecs'))
269 op.addOption(FlagOption('-dumpversion'))
270 op.addOption(FlagOption('-dumpmachine'))
271 op.addOption(FlagOption('-print-search-dirs'))
272 op.addOption(FlagOption('-print-libgcc-file-name'))
273 # FIXME: Hrm, where does this come from? It isn't always true that
274 # we take both - and --. For example, gcc --S ... ends up sending
275 # -fS to cc1. Investigate.
276 op.addOption(FlagOption('--print-libgcc-file-name'))
277 op.addOption(JoinedOption('-print-file-name='))
278 op.addOption(JoinedOption('-print-prog-name='))
279 op.addOption(JoinedOption('--print-prog-name='))
280 op.addOption(FlagOption('-print-multi-directory'))
281 op.addOption(FlagOption('-print-multi-lib'))
282 op.addOption(FlagOption('-print-multi-os-directory'))
283
284 # Hmmm, who really takes this?
285 op.addOption(FlagOption('--version'))
286
287 # Pipeline control
288 op.addOption(FlagOption('-###'))
289 op.addOption(FlagOption('-E'))
290 op.addOption(FlagOption('-S'))
291 op.addOption(FlagOption('-c'))
292 op.addOption(FlagOption('-combine'))
293 op.addOption(FlagOption('-no-integrated-cpp'))
294 op.addOption(FlagOption('-pipe'))
295 op.addOption(FlagOption('-save-temps'))
296 op.addOption(FlagOption('--save-temps'))
297 op.addOption(JoinedOption('-specs='))
298 op.addOption(FlagOption('-time'))
299 op.addOption(FlagOption('-v'))
300
301 # Input/output stuff
302 op.addOption(JoinedOrSeparateOption('-o'))
303 op.addOption(JoinedOrSeparateOption('-x'))
304
305 # FIXME: What do these actually do? The documentation is less than
306 # clear.
307 op.addOption(FlagOption('-ObjC'))
308 op.addOption(FlagOption('-ObjC++'))
309
310 # FIXME: Weird, gcc claims this here in help but I'm not sure why;
311 # perhaps interaction with preprocessor? Investigate.
312 op.addOption(JoinedOption('-std='))
313 op.addOption(JoinedOrSeparateOption('--sysroot'))
314
315 # Version control
316 op.addOption(JoinedOrSeparateOption('-B'))
317 op.addOption(JoinedOrSeparateOption('-V'))
318 op.addOption(JoinedOrSeparateOption('-b'))
319
320 # Blanket pass-through options.
321
322 op.addOption(JoinedOption('-Wa,'))
323 op.addOption(SeparateOption('-Xassembler'))
324
325 op.addOption(JoinedOption('-Wp,'))
326 op.addOption(SeparateOption('-Xpreprocessor'))
327
328 op.addOption(JoinedOption('-Wl,'))
329 op.addOption(SeparateOption('-Xlinker'))
330
331 ####
332 # Bring on the random garbage.
333
334 op.addOption(FlagOption('-MD'))
335 op.addOption(FlagOption('-MP'))
336 op.addOption(FlagOption('-MM'))
337 op.addOption(JoinedOrSeparateOption('-MF'))
338 op.addOption(JoinedOrSeparateOption('-MT'))
339 op.addOption(FlagOption('-undef'))
340
341 op.addOption(FlagOption('-w'))
342 op.addOption(JoinedOrSeparateOption('-allowable_client'))
343 op.addOption(JoinedOrSeparateOption('-client_name'))
344 op.addOption(JoinedOrSeparateOption('-compatibility_version'))
345 op.addOption(JoinedOrSeparateOption('-current_version'))
346 op.addOption(JoinedOrSeparateOption('-exported_symbols_list'))
347 op.addOption(JoinedOrSeparateOption('-idirafter'))
348 op.addOption(JoinedOrSeparateOption('-iquote'))
349 op.addOption(JoinedOrSeparateOption('-isysroot'))
350 op.addOption(JoinedOrSeparateOption('-keep_private_externs'))
351 op.addOption(JoinedOrSeparateOption('-seg1addr'))
352 op.addOption(JoinedOrSeparateOption('-segprot'))
353 op.addOption(JoinedOrSeparateOption('-sub_library'))
354 op.addOption(JoinedOrSeparateOption('-sub_umbrella'))
355 op.addOption(JoinedOrSeparateOption('-umbrella'))
356 op.addOption(JoinedOrSeparateOption('-undefined'))
357 op.addOption(JoinedOrSeparateOption('-unexported_symbols_list'))
358 op.addOption(JoinedOrSeparateOption('-weak_framework'))
359 op.addOption(JoinedOption('-headerpad_max_install_names'))
360 op.addOption(FlagOption('-twolevel_namespace'))
361 op.addOption(FlagOption('-prebind'))
362 op.addOption(FlagOption('-prebind_all_twolevel_modules'))
363 op.addOption(FlagOption('-single_module'))
364 op.addOption(FlagOption('-nomultidefs'))
365 op.addOption(FlagOption('-nostdlib'))
366 op.addOption(FlagOption('-nostdinc'))
367 op.addOption(FlagOption('-static'))
368 op.addOption(FlagOption('-shared'))
369 op.addOption(FlagOption('-C'))
370 op.addOption(FlagOption('-CC'))
371 op.addOption(FlagOption('-R'))
372 op.addOption(FlagOption('-P'))
373 op.addOption(FlagOption('-all_load'))
374 op.addOption(FlagOption('--constant-cfstrings'))
375 op.addOption(FlagOption('-traditional'))
376 op.addOption(FlagOption('--traditional'))
377 op.addOption(FlagOption('-no_dead_strip_inits_and_terms'))
378 op.addOption(MultiArgOption('-sectalign', numArgs=3))
379 op.addOption(MultiArgOption('-sectcreate', numArgs=3))
380 op.addOption(MultiArgOption('-sectorder', numArgs=3))
381
382 # I dunno why these don't end up working when joined. Maybe
383 # because of translation?
384 op.addOption(SeparateOption('-filelist'))
385 op.addOption(SeparateOption('-framework'))
386 op.addOption(SeparateOption('-install_name'))
387 op.addOption(SeparateOption('-seg_addr_table'))
388 op.addOption(SeparateOption('-seg_addr_table_filename'))
389
390 # Where are these coming from? I can't find them...
391 op.addOption(JoinedOrSeparateOption('-e')) # Gets forwarded to linker
392 op.addOption(JoinedOrSeparateOption('-r'))
393
394 # Is this actually declared anywhere? I can only find it in a
395 # spec. :(
396 op.addOption(FlagOption('-pg'))
397
398 doNotReallySupport = 1
399 if doNotReallySupport:
400 # Archaic gcc option.
401 op.addOption(FlagOption('-cpp-precomp'))
402 op.addOption(FlagOption('-no-cpp-precomp'))
403
404 # C options for testing
405
406 op.addOption(JoinedOrSeparateOption('-include'))
407 op.addOption(JoinedOrSeparateOption('-A'))
408 op.addOption(JoinedOrSeparateOption('-D'))
409 op.addOption(JoinedOrSeparateOption('-F'))
410 op.addOption(JoinedOrSeparateOption('-I'))
411 op.addOption(JoinedOrSeparateOption('-L'))
412 op.addOption(JoinedOrSeparateOption('-U'))
413 op.addOption(JoinedOrSeparateOption('-l'))
414 op.addOption(JoinedOrSeparateOption('-u'))
415
416 # FIXME: What is going on here? '-X' goes to linker, and -X ... goes nowhere?
417 op.addOption(FlagOption('-X'))
418 # Not exactly sure how to decompose this. I split out -Xarch_
419 # because we need to recognize that in the driver driver part.
420 # FIXME: Man, this is lame it needs its own option.
421 op.addOption(JoinedAndSeparateOption('-Xarch_'))
422 op.addOption(JoinedOption('-X'))
423
424 # The driver needs to know about this flag.
425 op.addOption(FlagOption('-fsyntax-only'))
426
427 # FIXME: Wrong?
428 # FIXME: What to do about the ambiguity of options like
429 # -dumpspecs? How is this handled in gcc?
430 op.addOption(JoinedOption('-d'))
431 op.addOption(JoinedOption('-g'))
432 op.addOption(JoinedOption('-f'))
433 op.addOption(JoinedOption('-m'))
434 op.addOption(JoinedOption('-i'))
435 op.addOption(JoinedOption('-O'))
436 op.addOption(JoinedOption('-W'))
437 # FIXME: Weird. This option isn't really separate, --param=a=b
438 # works. An alias somewhere?
439 op.addOption(SeparateOption('--param'))
440
441 # FIXME: What is this? Seems to do something on Linux. I think
442 # only one is valid, but have a log that uses both.
443 op.addOption(FlagOption('-pthread'))
444 op.addOption(FlagOption('-pthreads'))
445
446 return op