blob: 42a789b61a298c60fd81ab912de3e91777a2bdf4 [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
Daniel Dunbara5677512009-01-05 19:53:30 +000022class Driver(object):
Daniel Dunbar94713452009-01-16 23:12:12 +000023 def __init__(self, driverName, driverDir):
24 self.driverName = driverName
25 self.driverDir = driverDir
Daniel Dunbar9066af82009-01-09 01:00:40 +000026 self.hostInfo = None
Daniel Dunbarba6e3232009-01-06 06:12:13 +000027 self.parser = Arguments.OptionParser()
Daniel Dunbar33a5d612009-01-14 01:03:36 +000028 self.cccHostBits = self.cccHostMachine = None
29 self.cccHostSystem = self.cccHostRelease = None
30 self.cccCXX = False
31 self.cccClang = False
Daniel Dunbarfb7ea272009-01-14 01:32:05 +000032 self.cccEcho = False
Daniel Dunbar33a5d612009-01-14 01:03:36 +000033 self.cccFallback = False
Daniel Dunbara5677512009-01-05 19:53:30 +000034
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000035 # Host queries which can be forcibly over-riden by the user for
36 # testing purposes.
37 #
38 # FIXME: We should make sure these are drawn from a fixed set so
39 # that nothing downstream ever plays a guessing game.
40
41 def getHostBits(self):
42 if self.cccHostBits:
43 return self.cccHostBits
44
45 return platform.architecture()[0].replace('bit','')
46
47 def getHostMachine(self):
48 if self.cccHostMachine:
49 return self.cccHostMachine
50
51 machine = platform.machine()
52 # Normalize names.
53 if machine == 'Power Macintosh':
54 return 'ppc'
55 return machine
56
57 def getHostSystemName(self):
58 if self.cccHostSystem:
59 return self.cccHostSystem
60
61 return platform.system().lower()
62
Daniel Dunbar9c257c32009-01-12 04:21:12 +000063 def getHostReleaseName(self):
64 if self.cccHostRelease:
65 return self.cccHostRelease
66
67 return platform.release()
68
Daniel Dunbarfb7ea272009-01-14 01:32:05 +000069 def getenvBool(self, name):
70 var = os.getenv(name)
71 if not var:
72 return False
73
74 try:
75 return bool(int(var))
76 except:
77 return False
78
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000079 ###
80
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +000081 def run(self, argv):
Daniel Dunbara5677512009-01-05 19:53:30 +000082 # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
83 # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
84 # QA_OVERRIDE_GCC3_OPTIONS, ...?
85
86 # FIXME: -V and -b processing
87
88 # Handle some special -ccc- options used for testing which are
89 # only allowed at the beginning of the command line.
90 cccPrintOptions = False
91 cccPrintPhases = False
Daniel Dunbara75ea3d2009-01-09 22:21:24 +000092
93 # FIXME: How to handle override of host? ccc specific options?
94 # Abuse -b?
Daniel Dunbarfb7ea272009-01-14 01:32:05 +000095 if self.getenvBool('CCC_CLANG'):
96 self.cccClang = True
97 if self.getenvBool('CCC_ECHO'):
98 self.cccEcho = True
99 if self.getenvBool('CCC_FALLBACK'):
100 self.cccFallback = True
101
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000102 while argv and argv[0].startswith('-ccc-'):
Daniel Dunbar94713452009-01-16 23:12:12 +0000103 fullOpt,argv = argv[0],argv[1:]
104 opt = fullOpt[5:]
Daniel Dunbara5677512009-01-05 19:53:30 +0000105
106 if opt == 'print-options':
107 cccPrintOptions = True
108 elif opt == 'print-phases':
109 cccPrintPhases = True
Daniel Dunbar33a5d612009-01-14 01:03:36 +0000110 elif opt == 'cxx':
111 self.cccCXX = True
112 elif opt == 'clang':
113 self.cccClang = True
Daniel Dunbarfb7ea272009-01-14 01:32:05 +0000114 elif opt == 'echo':
115 self.cccEcho = True
Daniel Dunbar33a5d612009-01-14 01:03:36 +0000116 elif opt == 'fallback':
117 self.cccFallback = True
Daniel Dunbar9066af82009-01-09 01:00:40 +0000118 elif opt == 'host-bits':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000119 self.cccHostBits,argv = argv[0],argv[1:]
Daniel Dunbar9066af82009-01-09 01:00:40 +0000120 elif opt == 'host-machine':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000121 self.cccHostMachine,argv = argv[0],argv[1:]
Daniel Dunbar9066af82009-01-09 01:00:40 +0000122 elif opt == 'host-system':
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000123 self.cccHostSystem,argv = argv[0],argv[1:]
Daniel Dunbar9c257c32009-01-12 04:21:12 +0000124 elif opt == 'host-release':
125 self.cccHostRelease,argv = argv[0],argv[1:]
Daniel Dunbara5677512009-01-05 19:53:30 +0000126 else:
Daniel Dunbar94713452009-01-16 23:12:12 +0000127 raise Arguments.InvalidArgumentsError("invalid option: %r" % fullOpt)
Daniel Dunbara5677512009-01-05 19:53:30 +0000128
Daniel Dunbara75ea3d2009-01-09 22:21:24 +0000129 self.hostInfo = HostInfo.getHostInfo(self)
Daniel Dunbar43124722009-01-10 02:07:54 +0000130 self.toolChain = self.hostInfo.getToolChain()
Daniel Dunbar9066af82009-01-09 01:00:40 +0000131
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000132 args = self.parser.parseArgs(argv)
Daniel Dunbara5677512009-01-05 19:53:30 +0000133
134 # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
135 # need to reparse the Arguments after they have been expanded by
136 # -Xarch. How is this going to work?
137 #
138 # Scratch that, we aren't going to do that; it really disrupts the
139 # organization, doesn't consistently work with gcc-dd, and is
140 # confusing. Instead we are going to enforce that -Xarch_ is only
141 # used with options which do not alter the driver behavior. Let's
142 # hope this is ok, because the current architecture is a little
143 # tied to it.
144
145 if cccPrintOptions:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000146 self.printOptions(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000147 sys.exit(0)
148
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000149 self.handleImmediateOptions(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000150
Daniel Dunbar9066af82009-01-09 01:00:40 +0000151 if self.hostInfo.useDriverDriver():
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000152 phases = self.buildPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000153 else:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000154 phases = self.buildNormalPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000155
156 if cccPrintPhases:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000157 self.printPhases(phases, args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000158 sys.exit(0)
Daniel Dunbar9066af82009-01-09 01:00:40 +0000159
Daniel Dunbara5677512009-01-05 19:53:30 +0000160 if 0:
161 print Util.pprint(phases)
162
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000163 jobs = self.bindPhases(phases, args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000164
165 # FIXME: We should provide some basic sanity checking of the
166 # pipeline as a "verification" sort of stage. For example, the
167 # pipeline should never end up writing to an output file in two
168 # places (I think). The pipeline should also never end up writing
169 # to an output file that is an input.
170 #
171 # This is intended to just be a "verify" step, not a functionality
172 # step. It should catch things like the driver driver not
173 # preventing -save-temps, but it shouldn't change behavior (so we
174 # can turn it off in Release-Asserts builds).
175
176 # Print in -### syntax.
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000177 hasHashHashHash = args.getLastArg(self.parser.hashHashHashOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000178 if hasHashHashHash:
179 self.claim(hasHashHashHash)
180 for j in jobs.iterjobs():
181 if isinstance(j, Jobs.Command):
Daniel Dunbar3ecc20f2009-01-12 19:36:35 +0000182 print >>sys.stderr, ' "%s"' % '" "'.join(j.getArgv())
Daniel Dunbara5677512009-01-05 19:53:30 +0000183 elif isinstance(j, Jobs.PipedJob):
184 for c in j.commands:
Daniel Dunbar3ecc20f2009-01-12 19:36:35 +0000185 print >>sys.stderr, ' "%s" %c' % ('" "'.join(c.getArgv()),
186 "| "[c is j.commands[-1]])
Daniel Dunbara5677512009-01-05 19:53:30 +0000187 elif not isinstance(j, JobList):
188 raise ValueError,'Encountered unknown job.'
189 sys.exit(0)
190
191 for j in jobs.iterjobs():
192 if isinstance(j, Jobs.Command):
Daniel Dunbarfb7ea272009-01-14 01:32:05 +0000193 if self.cccEcho:
Anders Carlsson0f7d9ec2009-01-18 02:54:17 +0000194 print >>sys.stderr, ' '.join(map(repr,j.getArgv()))
195 sys.stderr.flush()
Daniel Dunbardb439902009-01-07 18:40:45 +0000196 res = os.spawnvp(os.P_WAIT, j.executable, j.getArgv())
Daniel Dunbara5677512009-01-05 19:53:30 +0000197 if res:
198 sys.exit(res)
199 elif isinstance(j, Jobs.PipedJob):
Daniel Dunbar7d791fd2009-01-17 02:02:35 +0000200 import subprocess
201 procs = []
202 for sj in j.commands:
203 if self.cccEcho:
Anders Carlsson0f7d9ec2009-01-18 02:54:17 +0000204 print >> sys.stderr, ' '.join(map(repr,sj.getArgv()))
Daniel Dunbar7d791fd2009-01-17 02:02:35 +0000205 sys.stdout.flush()
206
207 if not procs:
208 stdin = None
209 else:
210 stdin = procs[-1].stdout
211 if sj is j.commands[-1]:
212 stdout = None
213 else:
214 stdout = subprocess.PIPE
215 procs.append(subprocess.Popen(sj.getArgv(),
216 executable=sj.executable,
217 stdin=stdin,
218 stdout=stdout))
219 for proc in procs:
220 res = proc.wait()
221 if res:
222 sys.exit(res)
Daniel Dunbara5677512009-01-05 19:53:30 +0000223 else:
224 raise ValueError,'Encountered unknown job.'
225
226 def claim(self, option):
227 # FIXME: Move to OptionList once introduced and implement.
228 pass
229
230 def warning(self, message):
Daniel Dunbar94713452009-01-16 23:12:12 +0000231 print >>sys.stderr,'%s: %s' % (self.driverName, message)
Daniel Dunbara5677512009-01-05 19:53:30 +0000232
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000233 def printOptions(self, args):
234 for i,arg in enumerate(args):
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000235 if isinstance(arg, Arguments.MultipleValuesArg):
236 values = list(args.getValues(arg))
237 elif isinstance(arg, Arguments.ValueArg):
238 values = [args.getValue(arg)]
239 elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg):
240 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)]
Daniel Dunbara5677512009-01-05 19:53:30 +0000241 else:
242 values = []
Daniel Dunbar5039f212009-01-06 02:30:10 +0000243 print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,
Daniel Dunbara5677512009-01-05 19:53:30 +0000244 ', '.join(['"%s"' % v
245 for v in values]))
246
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000247 def printPhases(self, phases, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000248 def printPhase(p, f, steps, arch=None):
249 if p in steps:
250 return steps[p]
251 elif isinstance(p, Phases.BindArchAction):
252 for kid in p.inputs:
253 printPhase(kid, f, steps, p.arch)
254 steps[p] = len(steps)
255 return
256
257 if isinstance(p, Phases.InputAction):
258 phaseName = 'input'
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000259 inputStr = '"%s"' % args.getValue(p.filename)
Daniel Dunbara5677512009-01-05 19:53:30 +0000260 else:
261 phaseName = p.phase.name
262 inputs = [printPhase(i, f, steps, arch)
263 for i in p.inputs]
264 inputStr = '{%s}' % ', '.join(map(str, inputs))
265 if arch is not None:
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000266 phaseName += '-' + args.getValue(arch)
Daniel Dunbara5677512009-01-05 19:53:30 +0000267 steps[p] = index = len(steps)
268 print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
269 return index
270 steps = {}
271 for phase in phases:
272 printPhase(phase, sys.stdout, steps)
273
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000274 def handleImmediateOptions(self, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000275 # FIXME: Some driver Arguments are consumed right off the bat,
276 # like -dumpversion. Currently the gcc-dd handles these
277 # poorly, so we should be ok handling them upfront instead of
278 # after driver-driver level dispatching.
279 #
280 # FIXME: The actual order of these options in gcc is all over the
281 # place. The -dump ones seem to be first and in specification
282 # order, but there are other levels of precedence. For example,
283 # -print-search-dirs is evaluated before -print-prog-name=,
284 # regardless of order (and the last instance of -print-prog-name=
285 # wins verse itself).
286 #
287 # FIXME: Do we want to report "argument unused" type errors in the
288 # presence of things like -dumpmachine and -print-search-dirs?
289 # Probably not.
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000290 arg = args.getLastArg(self.parser.dumpmachineOption)
291 if arg:
292 print 'FIXME: %s' % arg.opt.name
293 sys.exit(1)
294
295 arg = args.getLastArg(self.parser.dumpspecsOption)
296 if arg:
297 print 'FIXME: %s' % arg.opt.name
298 sys.exit(1)
299
300 arg = args.getLastArg(self.parser.dumpversionOption)
301 if arg:
302 print 'FIXME: %s' % arg.opt.name
303 sys.exit(1)
304
305 arg = args.getLastArg(self.parser.printFileNameOption)
306 if arg:
307 print 'FIXME: %s' % arg.opt.name
308 sys.exit(1)
309
310 arg = args.getLastArg(self.parser.printMultiDirectoryOption)
311 if arg:
312 print 'FIXME: %s' % arg.opt.name
313 sys.exit(1)
314
315 arg = args.getLastArg(self.parser.printMultiLibOption)
316 if arg:
317 print 'FIXME: %s' % arg.opt.name
318 sys.exit(1)
319
320 arg = args.getLastArg(self.parser.printProgNameOption)
321 if arg:
322 print 'FIXME: %s' % arg.opt.name
323 sys.exit(1)
324
325 arg = args.getLastArg(self.parser.printLibgccFilenameOption)
326 if arg:
327 print 'FIXME: %s' % arg.opt.name
328 sys.exit(1)
329
330 arg = args.getLastArg(self.parser.printSearchDirsOption)
331 if arg:
332 print 'FIXME: %s' % arg.opt.name
333 sys.exit(1)
Daniel Dunbara5677512009-01-05 19:53:30 +0000334
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000335 def buildNormalPipeline(self, args):
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000336 hasCombine = args.getLastArg(self.parser.combineOption)
337 hasSyntaxOnly = args.getLastArg(self.parser.syntaxOnlyOption)
338 hasDashC = args.getLastArg(self.parser.cOption)
339 hasDashE = args.getLastArg(self.parser.EOption)
340 hasDashS = args.getLastArg(self.parser.SOption)
Daniel Dunbarfce72bc2009-01-20 01:53:54 +0000341 hasDashM = args.getLastArg(self.parser.MOption)
342 hasDashMM = args.getLastArg(self.parser.MMOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000343
344 inputType = None
345 inputTypeOpt = None
346 inputs = []
347 for a in args:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000348 if a.opt is self.parser.inputOption:
Daniel Dunbar94713452009-01-16 23:12:12 +0000349 inputValue = args.getValue(a)
Daniel Dunbara5677512009-01-05 19:53:30 +0000350 if inputType is None:
Daniel Dunbar94713452009-01-16 23:12:12 +0000351 base,ext = os.path.splitext(inputValue)
Daniel Dunbara5677512009-01-05 19:53:30 +0000352 if ext and ext in Types.kTypeSuffixMap:
353 klass = Types.kTypeSuffixMap[ext]
354 else:
355 # FIXME: Its not clear why we shouldn't just
356 # revert to unknown. I think this is more likely a
357 # bug / unintended behavior in gcc. Not very
358 # important though.
359 klass = Types.ObjectType
360 else:
361 assert inputTypeOpt is not None
362 self.claim(inputTypeOpt)
363 klass = inputType
Daniel Dunbar94713452009-01-16 23:12:12 +0000364
365 # Check that the file exists. It isn't clear this is
366 # worth doing, since the tool presumably does this
367 # anyway, and this just adds an extra stat to the
368 # equation, but this is gcc compatible.
369 if not os.path.exists(inputValue):
370 self.warning("%s: No such file or directory" % inputValue)
371 else:
372 inputs.append((klass, a))
Daniel Dunbar2ec55bc2009-01-12 03:33:58 +0000373 elif a.opt.isLinkerInput:
374 # Treat as a linker input.
Daniel Dunbar5039f212009-01-06 02:30:10 +0000375 #
376 # FIXME: This might not be good enough. We may
377 # need to introduce another type for this case, so
378 # that other code which needs to know the inputs
379 # handles this properly. Best not to try and lipo
380 # this, for example.
381 inputs.append((Types.ObjectType, a))
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000382 elif a.opt is self.parser.xOption:
Daniel Dunbar5039f212009-01-06 02:30:10 +0000383 inputTypeOpt = a
384 value = args.getValue(a)
385 if value in Types.kTypeSpecifierMap:
386 inputType = Types.kTypeSpecifierMap[value]
387 else:
388 # FIXME: How are we going to handle diagnostics.
389 self.warning("language %s not recognized" % value)
Daniel Dunbara5677512009-01-05 19:53:30 +0000390
Daniel Dunbar5039f212009-01-06 02:30:10 +0000391 # FIXME: Its not clear why we shouldn't just
392 # revert to unknown. I think this is more likely a
393 # bug / unintended behavior in gcc. Not very
394 # important though.
Daniel Dunbar25d4a8f2009-01-13 21:07:43 +0000395 inputType = Types.ObjectType
Daniel Dunbara5677512009-01-05 19:53:30 +0000396
397 # We claim things here so that options for which we silently allow
398 # override only ever claim the used option.
399 if hasCombine:
400 self.claim(hasCombine)
401
402 finalPhase = Phases.Phase.eOrderPostAssemble
403 finalPhaseOpt = None
404
405 # Determine what compilation mode we are in.
Daniel Dunbarfce72bc2009-01-20 01:53:54 +0000406 if hasDashE or hasDashM or hasDashMM:
Daniel Dunbara5677512009-01-05 19:53:30 +0000407 finalPhase = Phases.Phase.eOrderPreprocess
408 finalPhaseOpt = hasDashE
409 elif hasSyntaxOnly:
410 finalPhase = Phases.Phase.eOrderCompile
411 finalPhaseOpt = hasSyntaxOnly
412 elif hasDashS:
413 finalPhase = Phases.Phase.eOrderCompile
414 finalPhaseOpt = hasDashS
415 elif hasDashC:
416 finalPhase = Phases.Phase.eOrderAssemble
417 finalPhaseOpt = hasDashC
418
419 if finalPhaseOpt:
420 self.claim(finalPhaseOpt)
421
422 # FIXME: Support -combine.
423 if hasCombine:
Daniel Dunbar94713452009-01-16 23:12:12 +0000424 raise NotImplementedError,"-combine is not yet supported"
425
Daniel Dunbar470104e2009-01-17 00:53:19 +0000426 # Reject -Z* at the top level for now.
427 arg = args.getLastArg(self.parser.ZOption)
428 if arg:
429 raise Arguments.InvalidArgumentsError("%s: unsupported use of internal gcc option" % ' '.join(args.render(arg)))
430
Daniel Dunbar94713452009-01-16 23:12:12 +0000431 if (not inputs and
432 not args.getLastArg(self.parser.hashHashHashOption)):
433 raise Arguments.InvalidArgumentsError("no input files")
Daniel Dunbara5677512009-01-05 19:53:30 +0000434
435 actions = []
436 linkerInputs = []
437 # FIXME: This is gross.
438 linkPhase = Phases.LinkPhase()
439 for klass,input in inputs:
440 # Figure out what step to start at.
441
442 # FIXME: This should be part of the input class probably?
443 # Altough it doesn't quite fit there either, things like
444 # asm-with-preprocess don't easily fit into a linear scheme.
445
446 # FIXME: I think we are going to end up wanting to just build
447 # a simple FSA which we run the inputs down.
448 sequence = []
449 if klass.preprocess:
450 sequence.append(Phases.PreprocessPhase())
451 if klass == Types.ObjectType:
452 sequence.append(linkPhase)
453 elif klass.onlyAssemble:
454 sequence.extend([Phases.AssemblePhase(),
455 linkPhase])
456 elif klass.onlyPrecompile:
457 sequence.append(Phases.PrecompilePhase())
458 else:
459 sequence.extend([Phases.CompilePhase(),
460 Phases.AssemblePhase(),
461 linkPhase])
462
463 if sequence[0].order > finalPhase:
464 assert finalPhaseOpt and finalPhaseOpt.opt
465 # FIXME: Explain what type of input file is. Or just match
466 # gcc warning.
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000467 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input),
Daniel Dunbara5677512009-01-05 19:53:30 +0000468 sequence[0].name,
469 finalPhaseOpt.opt.name))
470 else:
471 # Build the pipeline for this file.
472
473 current = Phases.InputAction(input, klass)
474 for transition in sequence:
475 # If the current action produces no output, or we are
476 # past what the user requested, we are done.
477 if (current.type is Types.NothingType or
478 transition.order > finalPhase):
479 break
480 else:
481 if isinstance(transition, Phases.PreprocessPhase):
482 assert isinstance(klass.preprocess, Types.InputType)
483 current = Phases.JobAction(transition,
484 [current],
485 klass.preprocess)
486 elif isinstance(transition, Phases.PrecompilePhase):
487 current = Phases.JobAction(transition,
488 [current],
489 Types.PCHType)
490 elif isinstance(transition, Phases.CompilePhase):
491 if hasSyntaxOnly:
492 output = Types.NothingType
493 else:
494 output = Types.AsmTypeNoPP
495 current = Phases.JobAction(transition,
496 [current],
497 output)
498 elif isinstance(transition, Phases.AssemblePhase):
499 current = Phases.JobAction(transition,
500 [current],
501 Types.ObjectType)
502 elif transition is linkPhase:
503 linkerInputs.append(current)
504 current = None
505 break
506 else:
507 raise RuntimeError,'Unrecognized transition: %s.' % transition
508 pass
509
510 if current is not None:
511 assert not isinstance(current, Phases.InputAction)
512 actions.append(current)
513
514 if linkerInputs:
515 actions.append(Phases.JobAction(linkPhase,
516 linkerInputs,
517 Types.ImageType))
518
519 return actions
520
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000521 def buildPipeline(self, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000522 # FIXME: We need to handle canonicalization of the specified arch.
523
524 archs = []
Daniel Dunbarfce72bc2009-01-20 01:53:54 +0000525 hasDashM = args.getLastArg(self.parser.MGroup)
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000526 hasSaveTemps = (args.getLastArg(self.parser.saveTempsOption) or
527 args.getLastArg(self.parser.saveTempsOption2))
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000528 for arg in args:
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000529 if arg.opt is self.parser.archOption:
Daniel Dunbar5039f212009-01-06 02:30:10 +0000530 archs.append(arg)
Daniel Dunbara5677512009-01-05 19:53:30 +0000531
532 if not archs:
Daniel Dunbar1f73ecb2009-01-13 04:05:40 +0000533 archs.append(args.makeSeparateArg(self.hostInfo.getArchName(args),
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000534 self.parser.archOption))
Daniel Dunbara5677512009-01-05 19:53:30 +0000535
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000536 actions = self.buildNormalPipeline(args)
Daniel Dunbara5677512009-01-05 19:53:30 +0000537
538 # FIXME: Use custom exception for this.
539 #
540 # FIXME: We killed off some others but these aren't yet detected in
541 # a functional manner. If we added information to jobs about which
542 # "auxiliary" files they wrote then we could detect the conflict
543 # these cause downstream.
544 if len(archs) > 1:
545 if hasDashM:
Daniel Dunbar94713452009-01-16 23:12:12 +0000546 raise Arguments.InvalidArgumentsError("Cannot use -M options with multiple arch flags.")
Daniel Dunbara5677512009-01-05 19:53:30 +0000547 elif hasSaveTemps:
Daniel Dunbar94713452009-01-16 23:12:12 +0000548 raise Arguments.InvalidArgumentsError("Cannot use -save-temps with multiple arch flags.")
Daniel Dunbara5677512009-01-05 19:53:30 +0000549
550 # Execute once per arch.
551 finalActions = []
552 for p in actions:
553 # Make sure we can lipo this kind of output. If not (and it
554 # is an actual output) then we disallow, since we can't
555 # create an output file with the right name without
556 # overwriting it. We could remove this oddity by just
557 # changing the output names to include the arch, which would
558 # also fix -save-temps. Compatibility wins for now.
559 #
560 # FIXME: Is this error substantially less useful than
561 # gcc-dd's? The main problem is that "Cannot use compiler
562 # output with multiple arch flags" won't make sense to most
563 # developers.
564 if (len(archs) > 1 and
565 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
Daniel Dunbar94713452009-01-16 23:12:12 +0000566 raise Arguments.InvalidArgumentsError('Cannot use %s output with multiple arch flags.' % p.type.name)
Daniel Dunbara5677512009-01-05 19:53:30 +0000567
568 inputs = []
569 for arch in archs:
570 inputs.append(Phases.BindArchAction(p, arch))
571
572 # Lipo if necessary. We do it this way because we need to set
573 # the arch flag so that -Xarch_ gets rewritten.
574 if len(inputs) == 1 or p.type == Types.NothingType:
575 finalActions.extend(inputs)
576 else:
577 finalActions.append(Phases.JobAction(Phases.LipoPhase(),
578 inputs,
579 p.type))
580
581 # FIXME: We need to add -Wl,arch_multiple and -Wl,final_output in
582 # certain cases. This may be icky because we need to figure out the
583 # mode first. Current plan is to hack on the pipeline once it is built
584 # and we know what is being spit out. This avoids having to handling
585 # things like -c and -combine in multiple places.
586 #
587 # The annoying one of these is -Wl,final_output because it involves
588 # communication across different phases.
589 #
590 # Hopefully we can do this purely as part of the binding, but
591 # leaving comment here for now until it is clear this works.
592
593 return finalActions
594
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000595 def bindPhases(self, phases, args):
Daniel Dunbara5677512009-01-05 19:53:30 +0000596 jobs = Jobs.JobList()
597
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000598 finalOutput = args.getLastArg(self.parser.oOption)
599 hasSaveTemps = (args.getLastArg(self.parser.saveTempsOption) or
600 args.getLastArg(self.parser.saveTempsOption2))
601 hasNoIntegratedCPP = args.getLastArg(self.parser.noIntegratedCPPOption)
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000602 hasTraditionalCPP = args.getLastArg(self.parser.traditionalCPPOption)
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000603 hasPipe = args.getLastArg(self.parser.pipeOption)
Daniel Dunbar2ec55bc2009-01-12 03:33:58 +0000604
Daniel Dunbara5677512009-01-05 19:53:30 +0000605 # We claim things here so that options for which we silently allow
606 # override only ever claim the used option.
607 if hasPipe:
608 self.claim(hasPipe)
609 # FIXME: Hack, override -pipe till we support it.
Daniel Dunbar7d791fd2009-01-17 02:02:35 +0000610 if hasSaveTemps:
611 self.warning('-pipe ignored because -save-temps specified')
612 hasPipe = None
Daniel Dunbara5677512009-01-05 19:53:30 +0000613 # Claim these here. Its not completely accurate but any warnings
614 # about these being unused are likely to be noise anyway.
615 if hasSaveTemps:
616 self.claim(hasSaveTemps)
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000617
618 if hasTraditionalCPP:
619 self.claim(hasTraditionalCPP)
620 elif hasNoIntegratedCPP:
Daniel Dunbara5677512009-01-05 19:53:30 +0000621 self.claim(hasNoIntegratedCPP)
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000622
Daniel Dunbar76290532009-01-13 06:25:31 +0000623 # FIXME: Move to... somewhere else.
Daniel Dunbara5677512009-01-05 19:53:30 +0000624 class InputInfo:
625 def __init__(self, source, type, baseInput):
626 self.source = source
627 self.type = type
628 self.baseInput = baseInput
629
630 def __repr__(self):
631 return '%s(%r, %r, %r)' % (self.__class__.__name__,
632 self.source, self.type, self.baseInput)
Daniel Dunbar76290532009-01-13 06:25:31 +0000633
634 def isOriginalInput(self):
635 return self.source is self.baseInput
Daniel Dunbara5677512009-01-05 19:53:30 +0000636
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000637 def createJobs(tc, phase,
638 canAcceptPipe=False, atTopLevel=False, arch=None,
639 tcArgs=None):
Daniel Dunbara5677512009-01-05 19:53:30 +0000640 if isinstance(phase, Phases.InputAction):
641 return InputInfo(phase.filename, phase.type, phase.filename)
642 elif isinstance(phase, Phases.BindArchAction):
Daniel Dunbar1e5f3eb2009-01-06 01:35:44 +0000643 archName = args.getValue(phase.arch)
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000644 tc = self.hostInfo.getToolChainForArch(archName)
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000645 return createJobs(tc, phase.inputs[0],
646 canAcceptPipe, atTopLevel, phase.arch,
647 tcArgs=None)
648
649 if tcArgs is None:
650 tcArgs = tc.translateArgs(args, arch)
Daniel Dunbara5677512009-01-05 19:53:30 +0000651
652 assert isinstance(phase, Phases.JobAction)
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000653 tool = tc.selectTool(phase)
Daniel Dunbara5677512009-01-05 19:53:30 +0000654
655 # See if we should use an integrated CPP. We only use an
656 # integrated cpp when we have exactly one input, since this is
657 # the only use case we care about.
658 useIntegratedCPP = False
659 inputList = phase.inputs
660 if (not hasNoIntegratedCPP and
Daniel Dunbar6325fcf2009-01-12 09:23:15 +0000661 not hasTraditionalCPP and
Daniel Dunbara5677512009-01-05 19:53:30 +0000662 not hasSaveTemps and
663 tool.hasIntegratedCPP()):
664 if (len(phase.inputs) == 1 and
Daniel Dunbar06172d62009-01-20 00:47:24 +0000665 isinstance(phase.inputs[0], Phases.JobAction) and
Daniel Dunbara5677512009-01-05 19:53:30 +0000666 isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
667 useIntegratedCPP = True
668 inputList = phase.inputs[0].inputs
669
670 # Only try to use pipes when exactly one input.
671 canAcceptPipe = len(inputList) == 1 and tool.acceptsPipedInput()
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000672 inputs = [createJobs(tc, p, canAcceptPipe, False, arch, tcArgs)
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000673 for p in inputList]
Daniel Dunbara5677512009-01-05 19:53:30 +0000674
675 # Determine if we should output to a pipe.
676 canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
677 outputToPipe = False
678 if canOutputToPipe:
679 # Some things default to writing to a pipe if the final
680 # phase and there was no user override.
681 #
682 # FIXME: What is the best way to handle this?
Daniel Dunbaraf44a622009-01-18 21:35:24 +0000683 if atTopLevel:
684 if (isinstance(phase.phase, Phases.PreprocessPhase) and
685 not finalOutput):
686 outputToPipe = True
Daniel Dunbara5677512009-01-05 19:53:30 +0000687 elif hasPipe:
688 outputToPipe = True
689
690 # Figure out where to put the job (pipes).
691 jobList = jobs
692 if canAcceptPipe and isinstance(inputs[0].source, Jobs.PipedJob):
693 jobList = inputs[0].source
694
695 # Figure out where to put the output.
696 baseInput = inputs[0].baseInput
697 if phase.type == Types.NothingType:
698 output = None
699 elif outputToPipe:
700 if isinstance(jobList, Jobs.PipedJob):
701 output = jobList
702 else:
703 jobList = output = Jobs.PipedJob([])
704 jobs.addJob(output)
705 else:
706 # Figure out what the derived output location would be.
707 #
708 # FIXME: gcc has some special case in here so that it doesn't
709 # create output files if they would conflict with an input.
Daniel Dunbara5677512009-01-05 19:53:30 +0000710 if phase.type is Types.ImageType:
711 namedOutput = "a.out"
712 else:
Daniel Dunbar2ec55bc2009-01-12 03:33:58 +0000713 inputName = args.getValue(baseInput)
Daniel Dunbara5677512009-01-05 19:53:30 +0000714 base,_ = os.path.splitext(inputName)
715 assert phase.type.tempSuffix is not None
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000716 namedOutput = base + '.' + phase.type.tempSuffix
Daniel Dunbara5677512009-01-05 19:53:30 +0000717
718 # Output to user requested destination?
719 if atTopLevel and finalOutput:
720 output = finalOutput
721 # Contruct a named destination?
722 elif atTopLevel or hasSaveTemps:
Daniel Dunbar88990f32009-01-20 05:49:32 +0000723 # As an annoying special case, pch generation
724 # doesn't strip the pathname.
725 if phase.type is Types.PCHType:
726 outputName = namedOutput
727 else:
728 outputName = os.path.basename(namedOutput)
729 output = args.makeSeparateArg(outputName,
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000730 self.parser.oOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000731 else:
732 # Output to temp file...
Daniel Dunbarba6e3232009-01-06 06:12:13 +0000733 fd,filename = tempfile.mkstemp(suffix='.'+phase.type.tempSuffix)
Daniel Dunbar39cbfaa2009-01-07 18:54:26 +0000734 output = args.makeSeparateArg(filename,
735 self.parser.oOption)
Daniel Dunbara5677512009-01-05 19:53:30 +0000736
Daniel Dunbardb439902009-01-07 18:40:45 +0000737 tool.constructJob(phase, arch, jobList, inputs, output, phase.type,
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000738 tcArgs)
Daniel Dunbara5677512009-01-05 19:53:30 +0000739
740 return InputInfo(output, phase.type, baseInput)
741
742 # It is an error to provide a -o option if we are making multiple
743 # output files.
744 if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
Daniel Dunbar94713452009-01-16 23:12:12 +0000745 raise Arguments.InvalidArgumentsError("cannot specify -o when generating multiple files")
Daniel Dunbara5677512009-01-05 19:53:30 +0000746
747 for phase in phases:
Daniel Dunbar11672ec2009-01-13 18:51:26 +0000748 createJobs(self.toolChain, phase,
Daniel Dunbarbee1f0d2009-01-11 22:06:22 +0000749 canAcceptPipe=True, atTopLevel=True)
Daniel Dunbara5677512009-01-05 19:53:30 +0000750
751 return jobs