blob: 392737ff409a8461885c0a8b4f77604251dea406 [file] [log] [blame]
Daniel Dunbara5677512009-01-05 19:53:30 +00001import os
Daniel Dunbar9066af82009-01-09 01:00:40 +00002import platform
Daniel Dunbara5677512009-01-05 19:53:30 +00003import sys
4import tempfile
5from pprint import pprint
6
7###
8
9import Arguments
10import Jobs
Daniel Dunbar9066af82009-01-09 01:00:40 +000011import HostInfo
Daniel Dunbara5677512009-01-05 19:53:30 +000012import Phases
13import Tools
14import Types
15import Util
16
17# FIXME: Clean up naming of options and arguments. Decide whether to
18# rename Option and be consistent about use of Option/Arg.
19
20####
21
22class MissingArgumentError(ValueError):
23 """MissingArgumentError - An option required an argument but none
24 was given."""
25
26###
27
28class Driver(object):
29 def __init__(self):
Daniel Dunbar9066af82009-01-09 01:00:40 +000030 self.hostInfo = None
Daniel Dunbarba6e3232009-01-06 06:12:13 +000031 self.parser = Arguments.OptionParser()
Daniel Dunbara5677512009-01-05 19:53:30 +000032
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000033 # Host queries which can be forcibly over-riden by the user for
34 # testing purposes.
35 #
36 # FIXME: We should make sure these are drawn from a fixed set so
37 # that nothing downstream ever plays a guessing game.
38
39 def getHostBits(self):
40 if self.cccHostBits:
41 return self.cccHostBits
42
43 return platform.architecture()[0].replace('bit','')
44
45 def getHostMachine(self):
46 if self.cccHostMachine:
47 return self.cccHostMachine
48
49 machine = platform.machine()
50 # Normalize names.
51 if machine == 'Power Macintosh':
52 return 'ppc'
53 return machine
54
55 def getHostSystemName(self):
56 if self.cccHostSystem:
57 return self.cccHostSystem
58
59 return platform.system().lower()
60
61 ###
62
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +000063 def run(self, argv):
Daniel Dunbara5677512009-01-05 19:53:30 +000064 # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
65 # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
66 # QA_OVERRIDE_GCC3_OPTIONS, ...?
67
68 # FIXME: -V and -b processing
69
70 # Handle some special -ccc- options used for testing which are
71 # only allowed at the beginning of the command line.
72 cccPrintOptions = False
73 cccPrintPhases = False
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000074
75 # FIXME: How to handle override of host? ccc specific options?
76 # Abuse -b?
77 self.cccHostBits = self.cccHostMachine = self.cccHostSystem = None
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +000078 while argv and argv[0].startswith('-ccc-'):
79 opt,argv = argv[0][5:],argv[1:]
Daniel Dunbara5677512009-01-05 19:53:30 +000080
81 if opt == 'print-options':
82 cccPrintOptions = True
83 elif opt == 'print-phases':
84 cccPrintPhases = True
Daniel Dunbar9066af82009-01-09 01:00:40 +000085 elif opt == 'host-bits':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000086 self.cccHostBits,argv = argv[0],argv[1:]
Daniel Dunbar9066af82009-01-09 01:00:40 +000087 elif opt == 'host-machine':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000088 self.cccHostMachine,argv = argv[0],argv[1:]
Daniel Dunbar9066af82009-01-09 01:00:40 +000089 elif opt == 'host-system':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000090 self.cccHostSystem,argv = argv[0],argv[1:]
Daniel Dunbara5677512009-01-05 19:53:30 +000091 else:
92 raise ValueError,"Invalid ccc option: %r" % cccPrintOptions
93
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000094 self.hostInfo = HostInfo.getHostInfo(self)
Daniel Dunbar9066af82009-01-09 01:00:40 +000095
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +000096 args = self.parser.parseArgs(argv)
Daniel Dunbara5677512009-01-05 19:53:30 +000097
98 # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
99 # need to reparse the Arguments after they have been expanded by
100 # -Xarch. How is this going to work?
101 #
102 # Scratch that, we aren't going to do that; it really disrupts the
103 # organization, doesn't consistently work with gcc-dd, and is
104 # confusing. Instead we are going to enforce that -Xarch_ is only
105 # used with options which do not alter the driver behavior. Let's
106 # hope this is ok, because the current architecture is a little
107 # tied to it.
108
109 if cccPrintOptions:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000110 self.printOptions(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000111 sys.exit(0)
112
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000113 self.handleImmediateOptions(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000114
Daniel Dunbar9066af82009-01-09 01:00:40 +0000115 if self.hostInfo.useDriverDriver():
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000116 phases = self.buildPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000117 else:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000118 phases = self.buildNormalPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000119
120 if cccPrintPhases:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000121 self.printPhases(phases, args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000122 sys.exit(0)
Daniel Dunbar9066af82009-01-09 01:00:40 +0000123
Daniel Dunbara5677512009-01-05 19:53:30 +0000124 if 0:
125 print Util.pprint(phases)
126
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000127 jobs = self.bindPhases(phases, args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000128
129 # FIXME: We should provide some basic sanity checking of the
130 # pipeline as a "verification" sort of stage. For example, the
131 # pipeline should never end up writing to an output file in two
132 # places (I think). The pipeline should also never end up writing
133 # to an output file that is an input.
134 #
135 # This is intended to just be a "verify" step, not a functionality
136 # step. It should catch things like the driver driver not
137 # preventing -save-temps, but it shouldn't change behavior (so we
138 # can turn it off in Release-Asserts builds).
139
140 # Print in -### syntax.
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000141 hasHashHashHash = args.getLastArg(self.parser.hashHashHashOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000142 if hasHashHashHash:
143 self.claim(hasHashHashHash)
144 for j in jobs.iterjobs():
145 if isinstance(j, Jobs.Command):
Daniel Dunbardb439902009-01-07 18:40:45 +0000146 print '"%s"' % '" "'.join(j.getArgv())
Daniel Dunbara5677512009-01-05 19:53:30 +0000147 elif isinstance(j, Jobs.PipedJob):
148 for c in j.commands:
Daniel Dunbardb439902009-01-07 18:40:45 +0000149 print '"%s" %c' % ('" "'.join(c.getArgv()),
Daniel Dunbara5677512009-01-05 19:53:30 +0000150 "| "[c is j.commands[-1]])
151 elif not isinstance(j, JobList):
152 raise ValueError,'Encountered unknown job.'
153 sys.exit(0)
154
155 for j in jobs.iterjobs():
156 if isinstance(j, Jobs.Command):
Daniel Dunbardb439902009-01-07 18:40:45 +0000157 res = os.spawnvp(os.P_WAIT, j.executable, j.getArgv())
Daniel Dunbara5677512009-01-05 19:53:30 +0000158 if res:
159 sys.exit(res)
160 elif isinstance(j, Jobs.PipedJob):
161 raise NotImplementedError,"Piped jobs aren't implemented yet."
162 else:
163 raise ValueError,'Encountered unknown job.'
164
165 def claim(self, option):
166 # FIXME: Move to OptionList once introduced and implement.
167 pass
168
169 def warning(self, message):
170 print >>sys.stderr,'%s: %s' % (sys.argv[0], message)
171
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000172 def printOptions(self, args):
173 for i,arg in enumerate(args):
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000174 if isinstance(arg, Arguments.MultipleValuesArg):
175 values = list(args.getValues(arg))
176 elif isinstance(arg, Arguments.ValueArg):
177 values = [args.getValue(arg)]
178 elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg):
179 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)]
Daniel Dunbara5677512009-01-05 19:53:30 +0000180 else:
181 values = []
Daniel Dunbar5039f212009-01-06 02:30:10 +0000182 print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,
Daniel Dunbara5677512009-01-05 19:53:30 +0000183 ', '.join(['"%s"' % v
184 for v in values]))
185
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000186 def printPhases(self, phases, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000187 def printPhase(p, f, steps, arch=None):
188 if p in steps:
189 return steps[p]
190 elif isinstance(p, Phases.BindArchAction):
191 for kid in p.inputs:
192 printPhase(kid, f, steps, p.arch)
193 steps[p] = len(steps)
194 return
195
196 if isinstance(p, Phases.InputAction):
197 phaseName = 'input'
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000198 inputStr = '"%s"' % args.getValue(p.filename)
Daniel Dunbara5677512009-01-05 19:53:30 +0000199 else:
200 phaseName = p.phase.name
201 inputs = [printPhase(i, f, steps, arch)
202 for i in p.inputs]
203 inputStr = '{%s}' % ', '.join(map(str, inputs))
204 if arch is not None:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000205 phaseName += '-' + args.getValue(arch)
Daniel Dunbara5677512009-01-05 19:53:30 +0000206 steps[p] = index = len(steps)
207 print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
208 return index
209 steps = {}
210 for phase in phases:
211 printPhase(phase, sys.stdout, steps)
212
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000213 def handleImmediateOptions(self, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000214 # FIXME: Some driver Arguments are consumed right off the bat,
215 # like -dumpversion. Currently the gcc-dd handles these
216 # poorly, so we should be ok handling them upfront instead of
217 # after driver-driver level dispatching.
218 #
219 # FIXME: The actual order of these options in gcc is all over the
220 # place. The -dump ones seem to be first and in specification
221 # order, but there are other levels of precedence. For example,
222 # -print-search-dirs is evaluated before -print-prog-name=,
223 # regardless of order (and the last instance of -print-prog-name=
224 # wins verse itself).
225 #
226 # FIXME: Do we want to report "argument unused" type errors in the
227 # presence of things like -dumpmachine and -print-search-dirs?
228 # Probably not.
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000229 arg = args.getLastArg(self.parser.dumpmachineOption)
230 if arg:
231 print 'FIXME: %s' % arg.opt.name
232 sys.exit(1)
233
234 arg = args.getLastArg(self.parser.dumpspecsOption)
235 if arg:
236 print 'FIXME: %s' % arg.opt.name
237 sys.exit(1)
238
239 arg = args.getLastArg(self.parser.dumpversionOption)
240 if arg:
241 print 'FIXME: %s' % arg.opt.name
242 sys.exit(1)
243
244 arg = args.getLastArg(self.parser.printFileNameOption)
245 if arg:
246 print 'FIXME: %s' % arg.opt.name
247 sys.exit(1)
248
249 arg = args.getLastArg(self.parser.printMultiDirectoryOption)
250 if arg:
251 print 'FIXME: %s' % arg.opt.name
252 sys.exit(1)
253
254 arg = args.getLastArg(self.parser.printMultiLibOption)
255 if arg:
256 print 'FIXME: %s' % arg.opt.name
257 sys.exit(1)
258
259 arg = args.getLastArg(self.parser.printProgNameOption)
260 if arg:
261 print 'FIXME: %s' % arg.opt.name
262 sys.exit(1)
263
264 arg = args.getLastArg(self.parser.printLibgccFilenameOption)
265 if arg:
266 print 'FIXME: %s' % arg.opt.name
267 sys.exit(1)
268
269 arg = args.getLastArg(self.parser.printSearchDirsOption)
270 if arg:
271 print 'FIXME: %s' % arg.opt.name
272 sys.exit(1)
Daniel Dunbara5677512009-01-05 19:53:30 +0000273
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000274 def buildNormalPipeline(self, args):
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000275 hasCombine = args.getLastArg(self.parser.combineOption)
276 hasSyntaxOnly = args.getLastArg(self.parser.syntaxOnlyOption)
277 hasDashC = args.getLastArg(self.parser.cOption)
278 hasDashE = args.getLastArg(self.parser.EOption)
279 hasDashS = args.getLastArg(self.parser.SOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000280
281 inputType = None
282 inputTypeOpt = None
283 inputs = []
284 for a in args:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000285 if a.opt is self.parser.inputOption:
Daniel Dunbara5677512009-01-05 19:53:30 +0000286 if inputType is None:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000287 base,ext = os.path.splitext(args.getValue(a))
Daniel Dunbara5677512009-01-05 19:53:30 +0000288 if ext and ext in Types.kTypeSuffixMap:
289 klass = Types.kTypeSuffixMap[ext]
290 else:
291 # FIXME: Its not clear why we shouldn't just
292 # revert to unknown. I think this is more likely a
293 # bug / unintended behavior in gcc. Not very
294 # important though.
295 klass = Types.ObjectType
296 else:
297 assert inputTypeOpt is not None
298 self.claim(inputTypeOpt)
299 klass = inputType
300 inputs.append((klass, a))
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000301 elif a.opt is self.parser.filelistOption:
302 # Treat as a linker input. Investigate how gcc is
303 # handling this.
Daniel Dunbar5039f212009-01-06 02:30:10 +0000304 #
305 # FIXME: This might not be good enough. We may
306 # need to introduce another type for this case, so
307 # that other code which needs to know the inputs
308 # handles this properly. Best not to try and lipo
309 # this, for example.
310 inputs.append((Types.ObjectType, a))
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000311 elif a.opt is self.parser.xOption:
Daniel Dunbar5039f212009-01-06 02:30:10 +0000312 self.claim(a)
313 inputTypeOpt = a
314 value = args.getValue(a)
315 if value in Types.kTypeSpecifierMap:
316 inputType = Types.kTypeSpecifierMap[value]
317 else:
318 # FIXME: How are we going to handle diagnostics.
319 self.warning("language %s not recognized" % value)
Daniel Dunbara5677512009-01-05 19:53:30 +0000320
Daniel Dunbar5039f212009-01-06 02:30:10 +0000321 # FIXME: Its not clear why we shouldn't just
322 # revert to unknown. I think this is more likely a
323 # bug / unintended behavior in gcc. Not very
324 # important though.
325 inputType = ObjectType
Daniel Dunbara5677512009-01-05 19:53:30 +0000326
327 # We claim things here so that options for which we silently allow
328 # override only ever claim the used option.
329 if hasCombine:
330 self.claim(hasCombine)
331
332 finalPhase = Phases.Phase.eOrderPostAssemble
333 finalPhaseOpt = None
334
335 # Determine what compilation mode we are in.
336 if hasDashE:
337 finalPhase = Phases.Phase.eOrderPreprocess
338 finalPhaseOpt = hasDashE
339 elif hasSyntaxOnly:
340 finalPhase = Phases.Phase.eOrderCompile
341 finalPhaseOpt = hasSyntaxOnly
342 elif hasDashS:
343 finalPhase = Phases.Phase.eOrderCompile
344 finalPhaseOpt = hasDashS
345 elif hasDashC:
346 finalPhase = Phases.Phase.eOrderAssemble
347 finalPhaseOpt = hasDashC
348
349 if finalPhaseOpt:
350 self.claim(finalPhaseOpt)
351
352 # FIXME: Support -combine.
353 if hasCombine:
354 raise NotImplementedError,"-combine is not yet supported."
355
356 actions = []
357 linkerInputs = []
358 # FIXME: This is gross.
359 linkPhase = Phases.LinkPhase()
360 for klass,input in inputs:
361 # Figure out what step to start at.
362
363 # FIXME: This should be part of the input class probably?
364 # Altough it doesn't quite fit there either, things like
365 # asm-with-preprocess don't easily fit into a linear scheme.
366
367 # FIXME: I think we are going to end up wanting to just build
368 # a simple FSA which we run the inputs down.
369 sequence = []
370 if klass.preprocess:
371 sequence.append(Phases.PreprocessPhase())
372 if klass == Types.ObjectType:
373 sequence.append(linkPhase)
374 elif klass.onlyAssemble:
375 sequence.extend([Phases.AssemblePhase(),
376 linkPhase])
377 elif klass.onlyPrecompile:
378 sequence.append(Phases.PrecompilePhase())
379 else:
380 sequence.extend([Phases.CompilePhase(),
381 Phases.AssemblePhase(),
382 linkPhase])
383
384 if sequence[0].order > finalPhase:
385 assert finalPhaseOpt and finalPhaseOpt.opt
386 # FIXME: Explain what type of input file is. Or just match
387 # gcc warning.
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000388 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input),
Daniel Dunbara5677512009-01-05 19:53:30 +0000389 sequence[0].name,
390 finalPhaseOpt.opt.name))
391 else:
392 # Build the pipeline for this file.
393
394 current = Phases.InputAction(input, klass)
395 for transition in sequence:
396 # If the current action produces no output, or we are
397 # past what the user requested, we are done.
398 if (current.type is Types.NothingType or
399 transition.order > finalPhase):
400 break
401 else:
402 if isinstance(transition, Phases.PreprocessPhase):
403 assert isinstance(klass.preprocess, Types.InputType)
404 current = Phases.JobAction(transition,
405 [current],
406 klass.preprocess)
407 elif isinstance(transition, Phases.PrecompilePhase):
408 current = Phases.JobAction(transition,
409 [current],
410 Types.PCHType)
411 elif isinstance(transition, Phases.CompilePhase):
412 if hasSyntaxOnly:
413 output = Types.NothingType
414 else:
415 output = Types.AsmTypeNoPP
416 current = Phases.JobAction(transition,
417 [current],
418 output)
419 elif isinstance(transition, Phases.AssemblePhase):
420 current = Phases.JobAction(transition,
421 [current],
422 Types.ObjectType)
423 elif transition is linkPhase:
424 linkerInputs.append(current)
425 current = None
426 break
427 else:
428 raise RuntimeError,'Unrecognized transition: %s.' % transition
429 pass
430
431 if current is not None:
432 assert not isinstance(current, Phases.InputAction)
433 actions.append(current)
434
435 if linkerInputs:
436 actions.append(Phases.JobAction(linkPhase,
437 linkerInputs,
438 Types.ImageType))
439
440 return actions
441
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000442 def buildPipeline(self, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000443 # FIXME: We need to handle canonicalization of the specified arch.
444
445 archs = []
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000446 hasDashM = None
447 hasSaveTemps = (args.getLastArg(self.parser.saveTempsOption) or
448 args.getLastArg(self.parser.saveTempsOption2))
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000449 for arg in args:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000450 if arg.opt is self.parser.archOption:
Daniel Dunbar5039f212009-01-06 02:30:10 +0000451 archs.append(arg)
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000452 elif arg.opt.name.startswith('-M'):
453 hasDashM = arg
Daniel Dunbara5677512009-01-05 19:53:30 +0000454
455 if not archs:
Daniel Dunbar9066af82009-01-09 01:00:40 +0000456 archs.append(args.makeSeparateArg(self.hostInfo.getArchName(),
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000457 self.parser.archOption))
Daniel Dunbara5677512009-01-05 19:53:30 +0000458
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000459 actions = self.buildNormalPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000460
461 # FIXME: Use custom exception for this.
462 #
463 # FIXME: We killed off some others but these aren't yet detected in
464 # a functional manner. If we added information to jobs about which
465 # "auxiliary" files they wrote then we could detect the conflict
466 # these cause downstream.
467 if len(archs) > 1:
468 if hasDashM:
469 raise ValueError,"Cannot use -M options with multiple arch flags."
470 elif hasSaveTemps:
471 raise ValueError,"Cannot use -save-temps with multiple arch flags."
472
473 # Execute once per arch.
474 finalActions = []
475 for p in actions:
476 # Make sure we can lipo this kind of output. If not (and it
477 # is an actual output) then we disallow, since we can't
478 # create an output file with the right name without
479 # overwriting it. We could remove this oddity by just
480 # changing the output names to include the arch, which would
481 # also fix -save-temps. Compatibility wins for now.
482 #
483 # FIXME: Is this error substantially less useful than
484 # gcc-dd's? The main problem is that "Cannot use compiler
485 # output with multiple arch flags" won't make sense to most
486 # developers.
487 if (len(archs) > 1 and
488 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
489 raise ValueError,'Cannot use %s output with multiple arch flags.' % p.type.name
490
491 inputs = []
492 for arch in archs:
493 inputs.append(Phases.BindArchAction(p, arch))
494
495 # Lipo if necessary. We do it this way because we need to set
496 # the arch flag so that -Xarch_ gets rewritten.
497 if len(inputs) == 1 or p.type == Types.NothingType:
498 finalActions.extend(inputs)
499 else:
500 finalActions.append(Phases.JobAction(Phases.LipoPhase(),
501 inputs,
502 p.type))
503
504 # FIXME: We need to add -Wl,arch_multiple and -Wl,final_output in
505 # certain cases. This may be icky because we need to figure out the
506 # mode first. Current plan is to hack on the pipeline once it is built
507 # and we know what is being spit out. This avoids having to handling
508 # things like -c and -combine in multiple places.
509 #
510 # The annoying one of these is -Wl,final_output because it involves
511 # communication across different phases.
512 #
513 # Hopefully we can do this purely as part of the binding, but
514 # leaving comment here for now until it is clear this works.
515
516 return finalActions
517
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000518 def bindPhases(self, phases, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000519 jobs = Jobs.JobList()
520
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000521 finalOutput = args.getLastArg(self.parser.oOption)
522 hasSaveTemps = (args.getLastArg(self.parser.saveTempsOption) or
523 args.getLastArg(self.parser.saveTempsOption2))
524 hasNoIntegratedCPP = args.getLastArg(self.parser.noIntegratedCPPOption)
525 hasPipe = args.getLastArg(self.parser.pipeOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000526 forward = []
527 for a in args:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000528 if a.opt is self.parser.inputOption:
Daniel Dunbara5677512009-01-05 19:53:30 +0000529 pass
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000530
531 # FIXME: Needs to be part of option.
Daniel Dunbar5039f212009-01-06 02:30:10 +0000532 elif a.opt.name in ('-E', '-S', '-c',
533 '-arch', '-fsyntax-only', '-combine', '-x',
534 '-###'):
535 pass
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000536
Daniel Dunbara5677512009-01-05 19:53:30 +0000537 else:
538 forward.append(a)
539
540 # We claim things here so that options for which we silently allow
541 # override only ever claim the used option.
542 if hasPipe:
543 self.claim(hasPipe)
544 # FIXME: Hack, override -pipe till we support it.
545 hasPipe = None
546 # Claim these here. Its not completely accurate but any warnings
547 # about these being unused are likely to be noise anyway.
548 if hasSaveTemps:
549 self.claim(hasSaveTemps)
550 if hasNoIntegratedCPP:
551 self.claim(hasNoIntegratedCPP)
552
553 toolMap = {
554 Phases.PreprocessPhase : Tools.GCC_PreprocessTool(),
555 Phases.CompilePhase : Tools.GCC_CompileTool(),
556 Phases.PrecompilePhase : Tools.GCC_PrecompileTool(),
557 Phases.AssemblePhase : Tools.DarwinAssemblerTool(),
558 Phases.LinkPhase : Tools.Collect2Tool(),
559 Phases.LipoPhase : Tools.LipoTool(),
560 }
561
562 class InputInfo:
563 def __init__(self, source, type, baseInput):
564 self.source = source
565 self.type = type
566 self.baseInput = baseInput
567
568 def __repr__(self):
569 return '%s(%r, %r, %r)' % (self.__class__.__name__,
570 self.source, self.type, self.baseInput)
571
572 def createJobs(phase, forwardArgs,
573 canAcceptPipe=False, atTopLevel=False, arch=None):
574 if isinstance(phase, Phases.InputAction):
575 return InputInfo(phase.filename, phase.type, phase.filename)
576 elif isinstance(phase, Phases.BindArchAction):
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000577 archName = args.getValue(phase.arch)
Daniel Dunbara5677512009-01-05 19:53:30 +0000578 filteredArgs = []
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000579 for arg in forwardArgs:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000580 if arg.opt is self.parser.archOption:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000581 if arg is phase.arch:
582 filteredArgs.append(arg)
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000583 elif arg.opt is self.parser.XarchOption:
Daniel Dunbara5677512009-01-05 19:53:30 +0000584 # FIXME: gcc-dd has another conditional for passing
585 # through, when the arch conditional array has an empty
586 # string. Why?
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000587 if args.getJoinedValue(arg) == archName:
Daniel Dunbara5677512009-01-05 19:53:30 +0000588 # FIXME: This is wrong, we don't want a
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000589 # unknown arg we want an actual parsed
590 # version of this arg.
591 filteredArgs.append(args.makeUnknownArg(args.getSeparateValue(arg)))
Daniel Dunbara5677512009-01-05 19:53:30 +0000592 else:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000593 filteredArgs.append(arg)
Daniel Dunbara5677512009-01-05 19:53:30 +0000594
595 return createJobs(phase.inputs[0], filteredArgs,
596 canAcceptPipe, atTopLevel, phase.arch)
597
598 assert isinstance(phase, Phases.JobAction)
599 tool = toolMap[phase.phase.__class__]
600
601 # See if we should use an integrated CPP. We only use an
602 # integrated cpp when we have exactly one input, since this is
603 # the only use case we care about.
604 useIntegratedCPP = False
605 inputList = phase.inputs
606 if (not hasNoIntegratedCPP and
607 not hasSaveTemps and
608 tool.hasIntegratedCPP()):
609 if (len(phase.inputs) == 1 and
610 isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
611 useIntegratedCPP = True
612 inputList = phase.inputs[0].inputs
613
614 # Only try to use pipes when exactly one input.
615 canAcceptPipe = len(inputList) == 1 and tool.acceptsPipedInput()
616 inputs = [createJobs(p, forwardArgs, canAcceptPipe, False, arch) for p in inputList]
617
618 # Determine if we should output to a pipe.
619 canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
620 outputToPipe = False
621 if canOutputToPipe:
622 # Some things default to writing to a pipe if the final
623 # phase and there was no user override.
624 #
625 # FIXME: What is the best way to handle this?
626 if (atTopLevel and
627 isinstance(phase, Phases.PreprocessPhase) and
628 not finalOutput):
629 outputToPipe = True
630 elif hasPipe:
631 outputToPipe = True
632
633 # Figure out where to put the job (pipes).
634 jobList = jobs
635 if canAcceptPipe and isinstance(inputs[0].source, Jobs.PipedJob):
636 jobList = inputs[0].source
637
638 # Figure out where to put the output.
639 baseInput = inputs[0].baseInput
640 if phase.type == Types.NothingType:
641 output = None
642 elif outputToPipe:
643 if isinstance(jobList, Jobs.PipedJob):
644 output = jobList
645 else:
646 jobList = output = Jobs.PipedJob([])
647 jobs.addJob(output)
648 else:
649 # Figure out what the derived output location would be.
650 #
651 # FIXME: gcc has some special case in here so that it doesn't
652 # create output files if they would conflict with an input.
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000653 inputName = args.getValue(baseInput)
Daniel Dunbara5677512009-01-05 19:53:30 +0000654 if phase.type is Types.ImageType:
655 namedOutput = "a.out"
656 else:
657 base,_ = os.path.splitext(inputName)
658 assert phase.type.tempSuffix is not None
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000659 namedOutput = base + '.' + phase.type.tempSuffix
Daniel Dunbara5677512009-01-05 19:53:30 +0000660
661 # Output to user requested destination?
662 if atTopLevel and finalOutput:
663 output = finalOutput
664 # Contruct a named destination?
665 elif atTopLevel or hasSaveTemps:
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000666 output = args.makeSeparateArg(namedOutput,
667 self.parser.oOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000668 else:
669 # Output to temp file...
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000670 fd,filename = tempfile.mkstemp(suffix='.'+phase.type.tempSuffix)
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000671 output = args.makeSeparateArg(filename,
672 self.parser.oOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000673
Daniel Dunbardb439902009-01-07 18:40:45 +0000674 tool.constructJob(phase, arch, jobList, inputs, output, phase.type,
675 forwardArgs, args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000676
677 return InputInfo(output, phase.type, baseInput)
678
679 # It is an error to provide a -o option if we are making multiple
680 # output files.
681 if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
682 # FIXME: Custom exception.
683 raise ValueError,"Cannot specify -o when generating multiple files."
684
685 for phase in phases:
686 createJobs(phase, forward, canAcceptPipe=True, atTopLevel=True)
687
688 return jobs