blob: d10081224a20f2959bd9953c918fba49b1abd542 [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 Dunbar33a5d612009-01-14 01:03:36 +000032 self.cccHostBits = self.cccHostMachine = None
33 self.cccHostSystem = self.cccHostRelease = None
34 self.cccCXX = False
35 self.cccClang = False
Daniel Dunbarfb7ea272009-01-14 01:32:05 +000036 self.cccEcho = False
Daniel Dunbar33a5d612009-01-14 01:03:36 +000037 self.cccFallback = False
Daniel Dunbara5677512009-01-05 19:53:30 +000038
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000039 # Host queries which can be forcibly over-riden by the user for
40 # testing purposes.
41 #
42 # FIXME: We should make sure these are drawn from a fixed set so
43 # that nothing downstream ever plays a guessing game.
44
45 def getHostBits(self):
46 if self.cccHostBits:
47 return self.cccHostBits
48
49 return platform.architecture()[0].replace('bit','')
50
51 def getHostMachine(self):
52 if self.cccHostMachine:
53 return self.cccHostMachine
54
55 machine = platform.machine()
56 # Normalize names.
57 if machine == 'Power Macintosh':
58 return 'ppc'
59 return machine
60
61 def getHostSystemName(self):
62 if self.cccHostSystem:
63 return self.cccHostSystem
64
65 return platform.system().lower()
66
Daniel Dunbar9c257c32009-01-12 04:21:12 +000067 def getHostReleaseName(self):
68 if self.cccHostRelease:
69 return self.cccHostRelease
70
71 return platform.release()
72
Daniel Dunbarfb7ea272009-01-14 01:32:05 +000073 def getenvBool(self, name):
74 var = os.getenv(name)
75 if not var:
76 return False
77
78 try:
79 return bool(int(var))
80 except:
81 return False
82
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000083 ###
84
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +000085 def run(self, argv):
Daniel Dunbara5677512009-01-05 19:53:30 +000086 # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
87 # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
88 # QA_OVERRIDE_GCC3_OPTIONS, ...?
89
90 # FIXME: -V and -b processing
91
92 # Handle some special -ccc- options used for testing which are
93 # only allowed at the beginning of the command line.
94 cccPrintOptions = False
95 cccPrintPhases = False
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000096
97 # FIXME: How to handle override of host? ccc specific options?
98 # Abuse -b?
Daniel Dunbarfb7ea272009-01-14 01:32:05 +000099 if self.getenvBool('CCC_CLANG'):
100 self.cccClang = True
101 if self.getenvBool('CCC_ECHO'):
102 self.cccEcho = True
103 if self.getenvBool('CCC_FALLBACK'):
104 self.cccFallback = True
105
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000106 while argv and argv[0].startswith('-ccc-'):
107 opt,argv = argv[0][5:],argv[1:]
Daniel Dunbara5677512009-01-05 19:53:30 +0000108
109 if opt == 'print-options':
110 cccPrintOptions = True
111 elif opt == 'print-phases':
112 cccPrintPhases = True
Daniel Dunbar33a5d612009-01-14 01:03:36 +0000113 elif opt == 'cxx':
114 self.cccCXX = True
115 elif opt == 'clang':
116 self.cccClang = True
Daniel Dunbarfb7ea272009-01-14 01:32:05 +0000117 elif opt == 'echo':
118 self.cccEcho = True
Daniel Dunbar33a5d612009-01-14 01:03:36 +0000119 elif opt == 'fallback':
120 self.cccFallback = True
Daniel Dunbar9066af82009-01-09 01:00:40 +0000121 elif opt == 'host-bits':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000122 self.cccHostBits,argv = argv[0],argv[1:]
Daniel Dunbar9066af82009-01-09 01:00:40 +0000123 elif opt == 'host-machine':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000124 self.cccHostMachine,argv = argv[0],argv[1:]
Daniel Dunbar9066af82009-01-09 01:00:40 +0000125 elif opt == 'host-system':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000126 self.cccHostSystem,argv = argv[0],argv[1:]
Daniel Dunbar9c257c32009-01-12 04:21:12 +0000127 elif opt == 'host-release':
128 self.cccHostRelease,argv = argv[0],argv[1:]
Daniel Dunbara5677512009-01-05 19:53:30 +0000129 else:
Daniel Dunbara22c16d2009-01-16 21:20:42 +0000130 raise ValueError,"Invalid ccc option: %r" % opt
Daniel Dunbara5677512009-01-05 19:53:30 +0000131
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000132 self.hostInfo = HostInfo.getHostInfo(self)
Daniel Dunbar43124722009-01-10 02:07:54 +0000133 self.toolChain = self.hostInfo.getToolChain()
Daniel Dunbar9066af82009-01-09 01:00:40 +0000134
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000135 args = self.parser.parseArgs(argv)
Daniel Dunbara5677512009-01-05 19:53:30 +0000136
137 # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
138 # need to reparse the Arguments after they have been expanded by
139 # -Xarch. How is this going to work?
140 #
141 # Scratch that, we aren't going to do that; it really disrupts the
142 # organization, doesn't consistently work with gcc-dd, and is
143 # confusing. Instead we are going to enforce that -Xarch_ is only
144 # used with options which do not alter the driver behavior. Let's
145 # hope this is ok, because the current architecture is a little
146 # tied to it.
147
148 if cccPrintOptions:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000149 self.printOptions(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000150 sys.exit(0)
151
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000152 self.handleImmediateOptions(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000153
Daniel Dunbar9066af82009-01-09 01:00:40 +0000154 if self.hostInfo.useDriverDriver():
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000155 phases = self.buildPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000156 else:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000157 phases = self.buildNormalPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000158
159 if cccPrintPhases:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000160 self.printPhases(phases, args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000161 sys.exit(0)
Daniel Dunbar9066af82009-01-09 01:00:40 +0000162
Daniel Dunbara5677512009-01-05 19:53:30 +0000163 if 0:
164 print Util.pprint(phases)
165
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000166 jobs = self.bindPhases(phases, args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000167
168 # FIXME: We should provide some basic sanity checking of the
169 # pipeline as a "verification" sort of stage. For example, the
170 # pipeline should never end up writing to an output file in two
171 # places (I think). The pipeline should also never end up writing
172 # to an output file that is an input.
173 #
174 # This is intended to just be a "verify" step, not a functionality
175 # step. It should catch things like the driver driver not
176 # preventing -save-temps, but it shouldn't change behavior (so we
177 # can turn it off in Release-Asserts builds).
178
179 # Print in -### syntax.
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000180 hasHashHashHash = args.getLastArg(self.parser.hashHashHashOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000181 if hasHashHashHash:
182 self.claim(hasHashHashHash)
183 for j in jobs.iterjobs():
184 if isinstance(j, Jobs.Command):
Daniel Dunbar3ecc20f2009-01-12 19:36:35 +0000185 print >>sys.stderr, ' "%s"' % '" "'.join(j.getArgv())
Daniel Dunbara5677512009-01-05 19:53:30 +0000186 elif isinstance(j, Jobs.PipedJob):
187 for c in j.commands:
Daniel Dunbar3ecc20f2009-01-12 19:36:35 +0000188 print >>sys.stderr, ' "%s" %c' % ('" "'.join(c.getArgv()),
189 "| "[c is j.commands[-1]])
Daniel Dunbara5677512009-01-05 19:53:30 +0000190 elif not isinstance(j, JobList):
191 raise ValueError,'Encountered unknown job.'
192 sys.exit(0)
193
194 for j in jobs.iterjobs():
195 if isinstance(j, Jobs.Command):
Daniel Dunbarfb7ea272009-01-14 01:32:05 +0000196 if self.cccEcho:
197 print ' '.join(map(repr,j.getArgv()))
198 sys.stdout.flush()
Daniel Dunbardb439902009-01-07 18:40:45 +0000199 res = os.spawnvp(os.P_WAIT, j.executable, j.getArgv())
Daniel Dunbara5677512009-01-05 19:53:30 +0000200 if res:
201 sys.exit(res)
202 elif isinstance(j, Jobs.PipedJob):
203 raise NotImplementedError,"Piped jobs aren't implemented yet."
204 else:
205 raise ValueError,'Encountered unknown job.'
206
207 def claim(self, option):
208 # FIXME: Move to OptionList once introduced and implement.
209 pass
210
211 def warning(self, message):
212 print >>sys.stderr,'%s: %s' % (sys.argv[0], message)
213
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000214 def printOptions(self, args):
215 for i,arg in enumerate(args):
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000216 if isinstance(arg, Arguments.MultipleValuesArg):
217 values = list(args.getValues(arg))
218 elif isinstance(arg, Arguments.ValueArg):
219 values = [args.getValue(arg)]
220 elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg):
221 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)]
Daniel Dunbara5677512009-01-05 19:53:30 +0000222 else:
223 values = []
Daniel Dunbar5039f212009-01-06 02:30:10 +0000224 print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,
Daniel Dunbara5677512009-01-05 19:53:30 +0000225 ', '.join(['"%s"' % v
226 for v in values]))
227
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000228 def printPhases(self, phases, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000229 def printPhase(p, f, steps, arch=None):
230 if p in steps:
231 return steps[p]
232 elif isinstance(p, Phases.BindArchAction):
233 for kid in p.inputs:
234 printPhase(kid, f, steps, p.arch)
235 steps[p] = len(steps)
236 return
237
238 if isinstance(p, Phases.InputAction):
239 phaseName = 'input'
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000240 inputStr = '"%s"' % args.getValue(p.filename)
Daniel Dunbara5677512009-01-05 19:53:30 +0000241 else:
242 phaseName = p.phase.name
243 inputs = [printPhase(i, f, steps, arch)
244 for i in p.inputs]
245 inputStr = '{%s}' % ', '.join(map(str, inputs))
246 if arch is not None:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000247 phaseName += '-' + args.getValue(arch)
Daniel Dunbara5677512009-01-05 19:53:30 +0000248 steps[p] = index = len(steps)
249 print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
250 return index
251 steps = {}
252 for phase in phases:
253 printPhase(phase, sys.stdout, steps)
254
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000255 def handleImmediateOptions(self, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000256 # FIXME: Some driver Arguments are consumed right off the bat,
257 # like -dumpversion. Currently the gcc-dd handles these
258 # poorly, so we should be ok handling them upfront instead of
259 # after driver-driver level dispatching.
260 #
261 # FIXME: The actual order of these options in gcc is all over the
262 # place. The -dump ones seem to be first and in specification
263 # order, but there are other levels of precedence. For example,
264 # -print-search-dirs is evaluated before -print-prog-name=,
265 # regardless of order (and the last instance of -print-prog-name=
266 # wins verse itself).
267 #
268 # FIXME: Do we want to report "argument unused" type errors in the
269 # presence of things like -dumpmachine and -print-search-dirs?
270 # Probably not.
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000271 arg = args.getLastArg(self.parser.dumpmachineOption)
272 if arg:
273 print 'FIXME: %s' % arg.opt.name
274 sys.exit(1)
275
276 arg = args.getLastArg(self.parser.dumpspecsOption)
277 if arg:
278 print 'FIXME: %s' % arg.opt.name
279 sys.exit(1)
280
281 arg = args.getLastArg(self.parser.dumpversionOption)
282 if arg:
283 print 'FIXME: %s' % arg.opt.name
284 sys.exit(1)
285
286 arg = args.getLastArg(self.parser.printFileNameOption)
287 if arg:
288 print 'FIXME: %s' % arg.opt.name
289 sys.exit(1)
290
291 arg = args.getLastArg(self.parser.printMultiDirectoryOption)
292 if arg:
293 print 'FIXME: %s' % arg.opt.name
294 sys.exit(1)
295
296 arg = args.getLastArg(self.parser.printMultiLibOption)
297 if arg:
298 print 'FIXME: %s' % arg.opt.name
299 sys.exit(1)
300
301 arg = args.getLastArg(self.parser.printProgNameOption)
302 if arg:
303 print 'FIXME: %s' % arg.opt.name
304 sys.exit(1)
305
306 arg = args.getLastArg(self.parser.printLibgccFilenameOption)
307 if arg:
308 print 'FIXME: %s' % arg.opt.name
309 sys.exit(1)
310
311 arg = args.getLastArg(self.parser.printSearchDirsOption)
312 if arg:
313 print 'FIXME: %s' % arg.opt.name
314 sys.exit(1)
Daniel Dunbara5677512009-01-05 19:53:30 +0000315
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000316 def buildNormalPipeline(self, args):
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000317 hasCombine = args.getLastArg(self.parser.combineOption)
318 hasSyntaxOnly = args.getLastArg(self.parser.syntaxOnlyOption)
319 hasDashC = args.getLastArg(self.parser.cOption)
320 hasDashE = args.getLastArg(self.parser.EOption)
321 hasDashS = args.getLastArg(self.parser.SOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000322
323 inputType = None
324 inputTypeOpt = None
325 inputs = []
326 for a in args:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000327 if a.opt is self.parser.inputOption:
Daniel Dunbara5677512009-01-05 19:53:30 +0000328 if inputType is None:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000329 base,ext = os.path.splitext(args.getValue(a))
Daniel Dunbara5677512009-01-05 19:53:30 +0000330 if ext and ext in Types.kTypeSuffixMap:
331 klass = Types.kTypeSuffixMap[ext]
332 else:
333 # FIXME: Its not clear why we shouldn't just
334 # revert to unknown. I think this is more likely a
335 # bug / unintended behavior in gcc. Not very
336 # important though.
337 klass = Types.ObjectType
338 else:
339 assert inputTypeOpt is not None
340 self.claim(inputTypeOpt)
341 klass = inputType
342 inputs.append((klass, a))
Daniel Dunbar2ec55bc2009-01-12 03:33:58 +0000343 elif a.opt.isLinkerInput:
344 # Treat as a linker input.
Daniel Dunbar5039f212009-01-06 02:30:10 +0000345 #
346 # FIXME: This might not be good enough. We may
347 # need to introduce another type for this case, so
348 # that other code which needs to know the inputs
349 # handles this properly. Best not to try and lipo
350 # this, for example.
Daniel Dunbare99f9262009-01-11 22:03:55 +0000351 #
352 # FIXME: Actually, this is just flat out broken, the
353 # tools expect inputs to be accessible by .getValue
354 # but that of course only yields the argument.
Daniel Dunbar5039f212009-01-06 02:30:10 +0000355 inputs.append((Types.ObjectType, a))
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000356 elif a.opt is self.parser.xOption:
Daniel Dunbar5039f212009-01-06 02:30:10 +0000357 self.claim(a)
358 inputTypeOpt = a
359 value = args.getValue(a)
360 if value in Types.kTypeSpecifierMap:
361 inputType = Types.kTypeSpecifierMap[value]
362 else:
363 # FIXME: How are we going to handle diagnostics.
364 self.warning("language %s not recognized" % value)
Daniel Dunbara5677512009-01-05 19:53:30 +0000365
Daniel Dunbar5039f212009-01-06 02:30:10 +0000366 # FIXME: Its not clear why we shouldn't just
367 # revert to unknown. I think this is more likely a
368 # bug / unintended behavior in gcc. Not very
369 # important though.
Daniel Dunbar25d4a8f2009-01-13 21:07:43 +0000370 inputType = Types.ObjectType
Daniel Dunbara5677512009-01-05 19:53:30 +0000371
372 # We claim things here so that options for which we silently allow
373 # override only ever claim the used option.
374 if hasCombine:
375 self.claim(hasCombine)
376
377 finalPhase = Phases.Phase.eOrderPostAssemble
378 finalPhaseOpt = None
379
380 # Determine what compilation mode we are in.
381 if hasDashE:
382 finalPhase = Phases.Phase.eOrderPreprocess
383 finalPhaseOpt = hasDashE
384 elif hasSyntaxOnly:
385 finalPhase = Phases.Phase.eOrderCompile
386 finalPhaseOpt = hasSyntaxOnly
387 elif hasDashS:
388 finalPhase = Phases.Phase.eOrderCompile
389 finalPhaseOpt = hasDashS
390 elif hasDashC:
391 finalPhase = Phases.Phase.eOrderAssemble
392 finalPhaseOpt = hasDashC
393
394 if finalPhaseOpt:
395 self.claim(finalPhaseOpt)
396
397 # FIXME: Support -combine.
398 if hasCombine:
399 raise NotImplementedError,"-combine is not yet supported."
400
401 actions = []
402 linkerInputs = []
403 # FIXME: This is gross.
404 linkPhase = Phases.LinkPhase()
405 for klass,input in inputs:
406 # Figure out what step to start at.
407
408 # FIXME: This should be part of the input class probably?
409 # Altough it doesn't quite fit there either, things like
410 # asm-with-preprocess don't easily fit into a linear scheme.
411
412 # FIXME: I think we are going to end up wanting to just build
413 # a simple FSA which we run the inputs down.
414 sequence = []
415 if klass.preprocess:
416 sequence.append(Phases.PreprocessPhase())
417 if klass == Types.ObjectType:
418 sequence.append(linkPhase)
419 elif klass.onlyAssemble:
420 sequence.extend([Phases.AssemblePhase(),
421 linkPhase])
422 elif klass.onlyPrecompile:
423 sequence.append(Phases.PrecompilePhase())
424 else:
425 sequence.extend([Phases.CompilePhase(),
426 Phases.AssemblePhase(),
427 linkPhase])
428
429 if sequence[0].order > finalPhase:
430 assert finalPhaseOpt and finalPhaseOpt.opt
431 # FIXME: Explain what type of input file is. Or just match
432 # gcc warning.
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000433 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input),
Daniel Dunbara5677512009-01-05 19:53:30 +0000434 sequence[0].name,
435 finalPhaseOpt.opt.name))
436 else:
437 # Build the pipeline for this file.
438
439 current = Phases.InputAction(input, klass)
440 for transition in sequence:
441 # If the current action produces no output, or we are
442 # past what the user requested, we are done.
443 if (current.type is Types.NothingType or
444 transition.order > finalPhase):
445 break
446 else:
447 if isinstance(transition, Phases.PreprocessPhase):
448 assert isinstance(klass.preprocess, Types.InputType)
449 current = Phases.JobAction(transition,
450 [current],
451 klass.preprocess)
452 elif isinstance(transition, Phases.PrecompilePhase):
453 current = Phases.JobAction(transition,
454 [current],
455 Types.PCHType)
456 elif isinstance(transition, Phases.CompilePhase):
457 if hasSyntaxOnly:
458 output = Types.NothingType
459 else:
460 output = Types.AsmTypeNoPP
461 current = Phases.JobAction(transition,
462 [current],
463 output)
464 elif isinstance(transition, Phases.AssemblePhase):
465 current = Phases.JobAction(transition,
466 [current],
467 Types.ObjectType)
468 elif transition is linkPhase:
469 linkerInputs.append(current)
470 current = None
471 break
472 else:
473 raise RuntimeError,'Unrecognized transition: %s.' % transition
474 pass
475
476 if current is not None:
477 assert not isinstance(current, Phases.InputAction)
478 actions.append(current)
479
480 if linkerInputs:
481 actions.append(Phases.JobAction(linkPhase,
482 linkerInputs,
483 Types.ImageType))
484
485 return actions
486
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000487 def buildPipeline(self, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000488 # FIXME: We need to handle canonicalization of the specified arch.
489
490 archs = []
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000491 hasDashM = None
492 hasSaveTemps = (args.getLastArg(self.parser.saveTempsOption) or
493 args.getLastArg(self.parser.saveTempsOption2))
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000494 for arg in args:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000495 if arg.opt is self.parser.archOption:
Daniel Dunbar5039f212009-01-06 02:30:10 +0000496 archs.append(arg)
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000497 elif arg.opt.name.startswith('-M'):
498 hasDashM = arg
Daniel Dunbara5677512009-01-05 19:53:30 +0000499
500 if not archs:
Daniel Dunbar1f73ecb2009-01-13 04:05:40 +0000501 archs.append(args.makeSeparateArg(self.hostInfo.getArchName(args),
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000502 self.parser.archOption))
Daniel Dunbara5677512009-01-05 19:53:30 +0000503
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000504 actions = self.buildNormalPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000505
506 # FIXME: Use custom exception for this.
507 #
508 # FIXME: We killed off some others but these aren't yet detected in
509 # a functional manner. If we added information to jobs about which
510 # "auxiliary" files they wrote then we could detect the conflict
511 # these cause downstream.
512 if len(archs) > 1:
513 if hasDashM:
514 raise ValueError,"Cannot use -M options with multiple arch flags."
515 elif hasSaveTemps:
516 raise ValueError,"Cannot use -save-temps with multiple arch flags."
517
518 # Execute once per arch.
519 finalActions = []
520 for p in actions:
521 # Make sure we can lipo this kind of output. If not (and it
522 # is an actual output) then we disallow, since we can't
523 # create an output file with the right name without
524 # overwriting it. We could remove this oddity by just
525 # changing the output names to include the arch, which would
526 # also fix -save-temps. Compatibility wins for now.
527 #
528 # FIXME: Is this error substantially less useful than
529 # gcc-dd's? The main problem is that "Cannot use compiler
530 # output with multiple arch flags" won't make sense to most
531 # developers.
532 if (len(archs) > 1 and
533 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
534 raise ValueError,'Cannot use %s output with multiple arch flags.' % p.type.name
535
536 inputs = []
537 for arch in archs:
538 inputs.append(Phases.BindArchAction(p, arch))
539
540 # Lipo if necessary. We do it this way because we need to set
541 # the arch flag so that -Xarch_ gets rewritten.
542 if len(inputs) == 1 or p.type == Types.NothingType:
543 finalActions.extend(inputs)
544 else:
545 finalActions.append(Phases.JobAction(Phases.LipoPhase(),
546 inputs,
547 p.type))
548
549 # FIXME: We need to add -Wl,arch_multiple and -Wl,final_output in
550 # certain cases. This may be icky because we need to figure out the
551 # mode first. Current plan is to hack on the pipeline once it is built
552 # and we know what is being spit out. This avoids having to handling
553 # things like -c and -combine in multiple places.
554 #
555 # The annoying one of these is -Wl,final_output because it involves
556 # communication across different phases.
557 #
558 # Hopefully we can do this purely as part of the binding, but
559 # leaving comment here for now until it is clear this works.
560
561 return finalActions
562
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000563 def bindPhases(self, phases, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000564 jobs = Jobs.JobList()
565
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000566 finalOutput = args.getLastArg(self.parser.oOption)
567 hasSaveTemps = (args.getLastArg(self.parser.saveTempsOption) or
568 args.getLastArg(self.parser.saveTempsOption2))
569 hasNoIntegratedCPP = args.getLastArg(self.parser.noIntegratedCPPOption)
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000570 hasTraditionalCPP = args.getLastArg(self.parser.traditionalCPPOption)
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000571 hasPipe = args.getLastArg(self.parser.pipeOption)
Daniel Dunbar2ec55bc2009-01-12 03:33:58 +0000572
Daniel Dunbara5677512009-01-05 19:53:30 +0000573 # We claim things here so that options for which we silently allow
574 # override only ever claim the used option.
575 if hasPipe:
576 self.claim(hasPipe)
577 # FIXME: Hack, override -pipe till we support it.
578 hasPipe = None
579 # Claim these here. Its not completely accurate but any warnings
580 # about these being unused are likely to be noise anyway.
581 if hasSaveTemps:
582 self.claim(hasSaveTemps)
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000583
584 if hasTraditionalCPP:
585 self.claim(hasTraditionalCPP)
586 elif hasNoIntegratedCPP:
Daniel Dunbara5677512009-01-05 19:53:30 +0000587 self.claim(hasNoIntegratedCPP)
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000588
Daniel Dunbar76290532009-01-13 06:25:31 +0000589 # FIXME: Move to... somewhere else.
Daniel Dunbara5677512009-01-05 19:53:30 +0000590 class InputInfo:
591 def __init__(self, source, type, baseInput):
592 self.source = source
593 self.type = type
594 self.baseInput = baseInput
595
596 def __repr__(self):
597 return '%s(%r, %r, %r)' % (self.__class__.__name__,
598 self.source, self.type, self.baseInput)
Daniel Dunbar76290532009-01-13 06:25:31 +0000599
600 def isOriginalInput(self):
601 return self.source is self.baseInput
Daniel Dunbara5677512009-01-05 19:53:30 +0000602
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000603 def createJobs(tc, phase,
604 canAcceptPipe=False, atTopLevel=False, arch=None,
605 tcArgs=None):
Daniel Dunbara5677512009-01-05 19:53:30 +0000606 if isinstance(phase, Phases.InputAction):
607 return InputInfo(phase.filename, phase.type, phase.filename)
608 elif isinstance(phase, Phases.BindArchAction):
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000609 archName = args.getValue(phase.arch)
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000610 tc = self.hostInfo.getToolChainForArch(archName)
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000611 return createJobs(tc, phase.inputs[0],
612 canAcceptPipe, atTopLevel, phase.arch,
613 tcArgs=None)
614
615 if tcArgs is None:
616 tcArgs = tc.translateArgs(args, arch)
Daniel Dunbara5677512009-01-05 19:53:30 +0000617
618 assert isinstance(phase, Phases.JobAction)
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000619 tool = tc.selectTool(phase)
Daniel Dunbara5677512009-01-05 19:53:30 +0000620
621 # See if we should use an integrated CPP. We only use an
622 # integrated cpp when we have exactly one input, since this is
623 # the only use case we care about.
624 useIntegratedCPP = False
625 inputList = phase.inputs
626 if (not hasNoIntegratedCPP and
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000627 not hasTraditionalCPP and
Daniel Dunbara5677512009-01-05 19:53:30 +0000628 not hasSaveTemps and
629 tool.hasIntegratedCPP()):
630 if (len(phase.inputs) == 1 and
631 isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
632 useIntegratedCPP = True
633 inputList = phase.inputs[0].inputs
634
635 # Only try to use pipes when exactly one input.
636 canAcceptPipe = len(inputList) == 1 and tool.acceptsPipedInput()
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000637 inputs = [createJobs(tc, p, canAcceptPipe, False, arch, tcArgs)
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000638 for p in inputList]
Daniel Dunbara5677512009-01-05 19:53:30 +0000639
640 # Determine if we should output to a pipe.
641 canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
642 outputToPipe = False
643 if canOutputToPipe:
644 # Some things default to writing to a pipe if the final
645 # phase and there was no user override.
646 #
647 # FIXME: What is the best way to handle this?
648 if (atTopLevel and
649 isinstance(phase, Phases.PreprocessPhase) and
650 not finalOutput):
651 outputToPipe = True
652 elif hasPipe:
653 outputToPipe = True
654
655 # Figure out where to put the job (pipes).
656 jobList = jobs
657 if canAcceptPipe and isinstance(inputs[0].source, Jobs.PipedJob):
658 jobList = inputs[0].source
659
660 # Figure out where to put the output.
661 baseInput = inputs[0].baseInput
662 if phase.type == Types.NothingType:
663 output = None
664 elif outputToPipe:
665 if isinstance(jobList, Jobs.PipedJob):
666 output = jobList
667 else:
668 jobList = output = Jobs.PipedJob([])
669 jobs.addJob(output)
670 else:
671 # Figure out what the derived output location would be.
672 #
673 # FIXME: gcc has some special case in here so that it doesn't
674 # create output files if they would conflict with an input.
Daniel Dunbara5677512009-01-05 19:53:30 +0000675 if phase.type is Types.ImageType:
676 namedOutput = "a.out"
677 else:
Daniel Dunbar2ec55bc2009-01-12 03:33:58 +0000678 inputName = args.getValue(baseInput)
Daniel Dunbara5677512009-01-05 19:53:30 +0000679 base,_ = os.path.splitext(inputName)
680 assert phase.type.tempSuffix is not None
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000681 namedOutput = base + '.' + phase.type.tempSuffix
Daniel Dunbara5677512009-01-05 19:53:30 +0000682
683 # Output to user requested destination?
684 if atTopLevel and finalOutput:
685 output = finalOutput
686 # Contruct a named destination?
687 elif atTopLevel or hasSaveTemps:
Daniel Dunbar3235fdb2009-01-12 07:48:07 +0000688 output = args.makeSeparateArg(os.path.basename(namedOutput),
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000689 self.parser.oOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000690 else:
691 # Output to temp file...
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000692 fd,filename = tempfile.mkstemp(suffix='.'+phase.type.tempSuffix)
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000693 output = args.makeSeparateArg(filename,
694 self.parser.oOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000695
Daniel Dunbardb439902009-01-07 18:40:45 +0000696 tool.constructJob(phase, arch, jobList, inputs, output, phase.type,
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000697 tcArgs)
Daniel Dunbara5677512009-01-05 19:53:30 +0000698
699 return InputInfo(output, phase.type, baseInput)
700
701 # It is an error to provide a -o option if we are making multiple
702 # output files.
703 if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
704 # FIXME: Custom exception.
705 raise ValueError,"Cannot specify -o when generating multiple files."
706
707 for phase in phases:
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000708 createJobs(self.toolChain, phase,
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000709 canAcceptPipe=True, atTopLevel=True)
Daniel Dunbara5677512009-01-05 19:53:30 +0000710
711 return jobs