blob: 095745facc65f8141134532d4be66b4b3b64c877 [file] [log] [blame]
Daniel Dunbar378530c2009-01-05 19:53:30 +00001import os
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +00002import platform
Daniel Dunbar378530c2009-01-05 19:53:30 +00003import sys
4import tempfile
5from pprint import pprint
6
7###
8
9import Arguments
10import Jobs
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +000011import HostInfo
Daniel Dunbar378530c2009-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 Dunbar378530c2009-01-05 19:53:30 +000022class Driver(object):
Daniel Dunbara0026f22009-01-16 23:12:12 +000023 def __init__(self, driverName, driverDir):
24 self.driverName = driverName
25 self.driverDir = driverDir
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +000026 self.hostInfo = None
Daniel Dunbare9f1a692009-01-06 06:12:13 +000027 self.parser = Arguments.OptionParser()
Daniel Dunbar7c2f91b2009-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 Dunbar4c751dc2009-01-14 01:32:05 +000032 self.cccEcho = False
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +000033 self.cccFallback = False
Daniel Dunbar378530c2009-01-05 19:53:30 +000034
Daniel Dunbardbc9ee92009-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'
Daniel Dunbar405327e2009-01-27 19:29:51 +000055 if machine == 'x86_64':
56 return 'i386'
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000057 return machine
58
59 def getHostSystemName(self):
60 if self.cccHostSystem:
61 return self.cccHostSystem
62
63 return platform.system().lower()
64
Daniel Dunbarc2148562009-01-12 04:21:12 +000065 def getHostReleaseName(self):
66 if self.cccHostRelease:
67 return self.cccHostRelease
68
69 return platform.release()
70
Daniel Dunbar4c751dc2009-01-14 01:32:05 +000071 def getenvBool(self, name):
72 var = os.getenv(name)
73 if not var:
74 return False
75
76 try:
77 return bool(int(var))
78 except:
79 return False
80
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000081 ###
82
Daniel Dunbarf677a602009-01-21 02:03:52 +000083 def getFilePath(self, name, toolChain=None):
84 tc = toolChain or self.toolChain
85 for p in tc.filePathPrefixes:
86 path = os.path.join(p, name)
87 if os.path.exists(path):
88 return path
89 return name
90
91 def getProgramPath(self, name, toolChain=None):
92 tc = toolChain or self.toolChain
93 for p in tc.programPathPrefixes:
94 path = os.path.join(p, name)
95 if os.path.exists(path):
96 return path
97 return name
98
99 ###
100
Daniel Dunbar74727872009-01-06 01:35:44 +0000101 def run(self, argv):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000102 # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
103 # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
104 # QA_OVERRIDE_GCC3_OPTIONS, ...?
105
106 # FIXME: -V and -b processing
107
108 # Handle some special -ccc- options used for testing which are
109 # only allowed at the beginning of the command line.
110 cccPrintOptions = False
111 cccPrintPhases = False
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000112
113 # FIXME: How to handle override of host? ccc specific options?
114 # Abuse -b?
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000115 if self.getenvBool('CCC_CLANG'):
116 self.cccClang = True
117 if self.getenvBool('CCC_ECHO'):
118 self.cccEcho = True
119 if self.getenvBool('CCC_FALLBACK'):
120 self.cccFallback = True
121
Daniel Dunbar74727872009-01-06 01:35:44 +0000122 while argv and argv[0].startswith('-ccc-'):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000123 fullOpt,argv = argv[0],argv[1:]
124 opt = fullOpt[5:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000125
126 if opt == 'print-options':
127 cccPrintOptions = True
128 elif opt == 'print-phases':
129 cccPrintPhases = True
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +0000130 elif opt == 'cxx':
131 self.cccCXX = True
132 elif opt == 'clang':
133 self.cccClang = True
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000134 elif opt == 'echo':
135 self.cccEcho = True
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +0000136 elif opt == 'fallback':
137 self.cccFallback = True
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000138 elif opt == 'host-bits':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000139 self.cccHostBits,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000140 elif opt == 'host-machine':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000141 self.cccHostMachine,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000142 elif opt == 'host-system':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000143 self.cccHostSystem,argv = argv[0],argv[1:]
Daniel Dunbarc2148562009-01-12 04:21:12 +0000144 elif opt == 'host-release':
145 self.cccHostRelease,argv = argv[0],argv[1:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000146 else:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000147 raise Arguments.InvalidArgumentsError("invalid option: %r" % fullOpt)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000148
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000149 self.hostInfo = HostInfo.getHostInfo(self)
Daniel Dunbar08dea462009-01-10 02:07:54 +0000150 self.toolChain = self.hostInfo.getToolChain()
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000151
Daniel Dunbar74727872009-01-06 01:35:44 +0000152 args = self.parser.parseArgs(argv)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000153
154 # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
155 # need to reparse the Arguments after they have been expanded by
156 # -Xarch. How is this going to work?
157 #
158 # Scratch that, we aren't going to do that; it really disrupts the
159 # organization, doesn't consistently work with gcc-dd, and is
160 # confusing. Instead we are going to enforce that -Xarch_ is only
161 # used with options which do not alter the driver behavior. Let's
162 # hope this is ok, because the current architecture is a little
163 # tied to it.
164
165 if cccPrintOptions:
Daniel Dunbar74727872009-01-06 01:35:44 +0000166 self.printOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000167 sys.exit(0)
168
Daniel Dunbar74727872009-01-06 01:35:44 +0000169 self.handleImmediateOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000170
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000171 if self.hostInfo.useDriverDriver():
Daniel Dunbar74727872009-01-06 01:35:44 +0000172 phases = self.buildPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000173 else:
Daniel Dunbar74727872009-01-06 01:35:44 +0000174 phases = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000175
176 if cccPrintPhases:
Daniel Dunbar74727872009-01-06 01:35:44 +0000177 self.printPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000178 sys.exit(0)
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000179
Daniel Dunbar378530c2009-01-05 19:53:30 +0000180 if 0:
181 print Util.pprint(phases)
182
Daniel Dunbar74727872009-01-06 01:35:44 +0000183 jobs = self.bindPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000184
185 # FIXME: We should provide some basic sanity checking of the
186 # pipeline as a "verification" sort of stage. For example, the
187 # pipeline should never end up writing to an output file in two
188 # places (I think). The pipeline should also never end up writing
189 # to an output file that is an input.
190 #
191 # This is intended to just be a "verify" step, not a functionality
192 # step. It should catch things like the driver driver not
193 # preventing -save-temps, but it shouldn't change behavior (so we
194 # can turn it off in Release-Asserts builds).
195
196 # Print in -### syntax.
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000197 hasHashHashHash = args.getLastArg(self.parser.hashHashHashOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000198 if hasHashHashHash:
199 self.claim(hasHashHashHash)
200 for j in jobs.iterjobs():
201 if isinstance(j, Jobs.Command):
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000202 print >>sys.stderr, ' "%s"' % '" "'.join(j.getArgv())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000203 elif isinstance(j, Jobs.PipedJob):
204 for c in j.commands:
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000205 print >>sys.stderr, ' "%s" %c' % ('" "'.join(c.getArgv()),
206 "| "[c is j.commands[-1]])
Daniel Dunbar378530c2009-01-05 19:53:30 +0000207 elif not isinstance(j, JobList):
208 raise ValueError,'Encountered unknown job.'
209 sys.exit(0)
210
211 for j in jobs.iterjobs():
212 if isinstance(j, Jobs.Command):
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000213 if self.cccEcho:
Anders Carlsson76613bf2009-01-18 02:54:17 +0000214 print >>sys.stderr, ' '.join(map(repr,j.getArgv()))
215 sys.stderr.flush()
Daniel Dunbarb421dba2009-01-07 18:40:45 +0000216 res = os.spawnvp(os.P_WAIT, j.executable, j.getArgv())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000217 if res:
218 sys.exit(res)
219 elif isinstance(j, Jobs.PipedJob):
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000220 import subprocess
221 procs = []
222 for sj in j.commands:
223 if self.cccEcho:
Anders Carlsson76613bf2009-01-18 02:54:17 +0000224 print >> sys.stderr, ' '.join(map(repr,sj.getArgv()))
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000225 sys.stdout.flush()
226
227 if not procs:
228 stdin = None
229 else:
230 stdin = procs[-1].stdout
231 if sj is j.commands[-1]:
232 stdout = None
233 else:
234 stdout = subprocess.PIPE
235 procs.append(subprocess.Popen(sj.getArgv(),
236 executable=sj.executable,
237 stdin=stdin,
238 stdout=stdout))
239 for proc in procs:
240 res = proc.wait()
241 if res:
242 sys.exit(res)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000243 else:
244 raise ValueError,'Encountered unknown job.'
245
246 def claim(self, option):
247 # FIXME: Move to OptionList once introduced and implement.
248 pass
249
250 def warning(self, message):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000251 print >>sys.stderr,'%s: %s' % (self.driverName, message)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000252
Daniel Dunbar74727872009-01-06 01:35:44 +0000253 def printOptions(self, args):
254 for i,arg in enumerate(args):
Daniel Dunbar74727872009-01-06 01:35:44 +0000255 if isinstance(arg, Arguments.MultipleValuesArg):
256 values = list(args.getValues(arg))
257 elif isinstance(arg, Arguments.ValueArg):
258 values = [args.getValue(arg)]
259 elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg):
260 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000261 else:
262 values = []
Daniel Dunbar927a5092009-01-06 02:30:10 +0000263 print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,
Daniel Dunbar378530c2009-01-05 19:53:30 +0000264 ', '.join(['"%s"' % v
265 for v in values]))
266
Daniel Dunbar74727872009-01-06 01:35:44 +0000267 def printPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000268 def printPhase(p, f, steps, arch=None):
269 if p in steps:
270 return steps[p]
271 elif isinstance(p, Phases.BindArchAction):
272 for kid in p.inputs:
273 printPhase(kid, f, steps, p.arch)
274 steps[p] = len(steps)
275 return
276
277 if isinstance(p, Phases.InputAction):
278 phaseName = 'input'
Daniel Dunbar74727872009-01-06 01:35:44 +0000279 inputStr = '"%s"' % args.getValue(p.filename)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000280 else:
281 phaseName = p.phase.name
282 inputs = [printPhase(i, f, steps, arch)
283 for i in p.inputs]
284 inputStr = '{%s}' % ', '.join(map(str, inputs))
285 if arch is not None:
Daniel Dunbar74727872009-01-06 01:35:44 +0000286 phaseName += '-' + args.getValue(arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000287 steps[p] = index = len(steps)
288 print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
289 return index
290 steps = {}
291 for phase in phases:
292 printPhase(phase, sys.stdout, steps)
293
Daniel Dunbar74727872009-01-06 01:35:44 +0000294 def handleImmediateOptions(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000295 # FIXME: Some driver Arguments are consumed right off the bat,
296 # like -dumpversion. Currently the gcc-dd handles these
297 # poorly, so we should be ok handling them upfront instead of
298 # after driver-driver level dispatching.
299 #
300 # FIXME: The actual order of these options in gcc is all over the
301 # place. The -dump ones seem to be first and in specification
302 # order, but there are other levels of precedence. For example,
303 # -print-search-dirs is evaluated before -print-prog-name=,
304 # regardless of order (and the last instance of -print-prog-name=
305 # wins verse itself).
306 #
307 # FIXME: Do we want to report "argument unused" type errors in the
308 # presence of things like -dumpmachine and -print-search-dirs?
309 # Probably not.
Daniel Dunbarf677a602009-01-21 02:03:52 +0000310 arg = (args.getLastArg(self.parser.dumpmachineOption) or
311 args.getLastArg(self.parser.dumpversionOption) or
312 args.getLastArg(self.parser.printSearchDirsOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000313 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000314 raise NotImplementedError('%s unsupported' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000315
Daniel Dunbarf677a602009-01-21 02:03:52 +0000316 arg = (args.getLastArg(self.parser.dumpspecsOption) or
317 args.getLastArg(self.parser.printMultiDirectoryOption) or
Daniel Dunbar73fb9072009-01-23 02:00:46 +0000318 args.getLastArg(self.parser.printMultiOsDirectoryOption) or
Daniel Dunbarf677a602009-01-21 02:03:52 +0000319 args.getLastArg(self.parser.printMultiLibOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000320 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000321 raise Arguments.InvalidArgumentsError('%s unsupported by this driver' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000322
323 arg = args.getLastArg(self.parser.printFileNameOption)
324 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000325 print self.getFilePath(args.getValue(arg))
326 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000327
328 arg = args.getLastArg(self.parser.printProgNameOption)
329 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000330 print self.getProgramPath(args.getValue(arg))
331 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000332
Daniel Dunbarf677a602009-01-21 02:03:52 +0000333 arg = args.getLastArg(self.parser.printLibgccFileNameOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000334 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000335 print self.getFilePath('libgcc.a')
336 sys.exit(0)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000337
Daniel Dunbar74727872009-01-06 01:35:44 +0000338 def buildNormalPipeline(self, args):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000339 hasAnalyze = args.getLastArg(self.parser.analyzeOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000340 hasCombine = args.getLastArg(self.parser.combineOption)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000341 hasEmitLLVM = args.getLastArg(self.parser.emitLLVMOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000342 hasSyntaxOnly = args.getLastArg(self.parser.syntaxOnlyOption)
343 hasDashC = args.getLastArg(self.parser.cOption)
344 hasDashE = args.getLastArg(self.parser.EOption)
345 hasDashS = args.getLastArg(self.parser.SOption)
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000346 hasDashM = args.getLastArg(self.parser.MOption)
347 hasDashMM = args.getLastArg(self.parser.MMOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000348
349 inputType = None
350 inputTypeOpt = None
351 inputs = []
352 for a in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000353 if a.opt is self.parser.inputOption:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000354 inputValue = args.getValue(a)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000355 if inputType is None:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000356 base,ext = os.path.splitext(inputValue)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000357 if ext and ext in Types.kTypeSuffixMap:
358 klass = Types.kTypeSuffixMap[ext]
359 else:
360 # FIXME: Its not clear why we shouldn't just
361 # revert to unknown. I think this is more likely a
362 # bug / unintended behavior in gcc. Not very
363 # important though.
364 klass = Types.ObjectType
365 else:
366 assert inputTypeOpt is not None
367 self.claim(inputTypeOpt)
368 klass = inputType
Daniel Dunbara0026f22009-01-16 23:12:12 +0000369
370 # Check that the file exists. It isn't clear this is
371 # worth doing, since the tool presumably does this
372 # anyway, and this just adds an extra stat to the
373 # equation, but this is gcc compatible.
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000374 if inputValue != '-' and not os.path.exists(inputValue):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000375 self.warning("%s: No such file or directory" % inputValue)
376 else:
377 inputs.append((klass, a))
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000378 elif a.opt.isLinkerInput:
379 # Treat as a linker input.
Daniel Dunbar927a5092009-01-06 02:30:10 +0000380 #
381 # FIXME: This might not be good enough. We may
382 # need to introduce another type for this case, so
383 # that other code which needs to know the inputs
384 # handles this properly. Best not to try and lipo
385 # this, for example.
386 inputs.append((Types.ObjectType, a))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000387 elif a.opt is self.parser.xOption:
Daniel Dunbar927a5092009-01-06 02:30:10 +0000388 inputTypeOpt = a
389 value = args.getValue(a)
390 if value in Types.kTypeSpecifierMap:
391 inputType = Types.kTypeSpecifierMap[value]
392 else:
393 # FIXME: How are we going to handle diagnostics.
394 self.warning("language %s not recognized" % value)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000395
Daniel Dunbar927a5092009-01-06 02:30:10 +0000396 # FIXME: Its not clear why we shouldn't just
397 # revert to unknown. I think this is more likely a
398 # bug / unintended behavior in gcc. Not very
399 # important though.
Daniel Dunbar6cb42052009-01-13 21:07:43 +0000400 inputType = Types.ObjectType
Daniel Dunbar378530c2009-01-05 19:53:30 +0000401
402 # We claim things here so that options for which we silently allow
403 # override only ever claim the used option.
404 if hasCombine:
405 self.claim(hasCombine)
406
407 finalPhase = Phases.Phase.eOrderPostAssemble
408 finalPhaseOpt = None
409
410 # Determine what compilation mode we are in.
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000411 if hasDashE or hasDashM or hasDashMM:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000412 finalPhase = Phases.Phase.eOrderPreprocess
413 finalPhaseOpt = hasDashE
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000414 elif (hasAnalyze or hasSyntaxOnly or
415 hasEmitLLVM or hasDashS):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000416 finalPhase = Phases.Phase.eOrderCompile
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000417 finalPhaseOpt = (hasAnalyze or hasSyntaxOnly or
418 hasEmitLLVM or hasDashS)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000419 elif hasDashC:
420 finalPhase = Phases.Phase.eOrderAssemble
421 finalPhaseOpt = hasDashC
422
423 if finalPhaseOpt:
424 self.claim(finalPhaseOpt)
425
426 # FIXME: Support -combine.
427 if hasCombine:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000428 raise NotImplementedError,"-combine is not yet supported"
429
Daniel Dunbar80e48b72009-01-17 00:53:19 +0000430 # Reject -Z* at the top level for now.
431 arg = args.getLastArg(self.parser.ZOption)
432 if arg:
433 raise Arguments.InvalidArgumentsError("%s: unsupported use of internal gcc option" % ' '.join(args.render(arg)))
434
Daniel Dunbara0026f22009-01-16 23:12:12 +0000435 if (not inputs and
436 not args.getLastArg(self.parser.hashHashHashOption)):
437 raise Arguments.InvalidArgumentsError("no input files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000438
439 actions = []
440 linkerInputs = []
441 # FIXME: This is gross.
442 linkPhase = Phases.LinkPhase()
443 for klass,input in inputs:
444 # Figure out what step to start at.
445
446 # FIXME: This should be part of the input class probably?
447 # Altough it doesn't quite fit there either, things like
448 # asm-with-preprocess don't easily fit into a linear scheme.
449
450 # FIXME: I think we are going to end up wanting to just build
451 # a simple FSA which we run the inputs down.
452 sequence = []
453 if klass.preprocess:
454 sequence.append(Phases.PreprocessPhase())
455 if klass == Types.ObjectType:
456 sequence.append(linkPhase)
457 elif klass.onlyAssemble:
458 sequence.extend([Phases.AssemblePhase(),
459 linkPhase])
460 elif klass.onlyPrecompile:
461 sequence.append(Phases.PrecompilePhase())
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000462 elif hasAnalyze:
463 sequence.append(Phases.AnalyzePhase())
464 elif hasSyntaxOnly:
465 sequence.append(Phases.SyntaxOnlyPhase())
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000466 elif hasEmitLLVM:
467 sequence.append(Phases.EmitLLVMPhase())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000468 else:
469 sequence.extend([Phases.CompilePhase(),
470 Phases.AssemblePhase(),
471 linkPhase])
472
473 if sequence[0].order > finalPhase:
474 assert finalPhaseOpt and finalPhaseOpt.opt
475 # FIXME: Explain what type of input file is. Or just match
476 # gcc warning.
Daniel Dunbar74727872009-01-06 01:35:44 +0000477 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input),
Daniel Dunbar378530c2009-01-05 19:53:30 +0000478 sequence[0].name,
479 finalPhaseOpt.opt.name))
480 else:
481 # Build the pipeline for this file.
482
483 current = Phases.InputAction(input, klass)
484 for transition in sequence:
485 # If the current action produces no output, or we are
486 # past what the user requested, we are done.
487 if (current.type is Types.NothingType or
488 transition.order > finalPhase):
489 break
490 else:
491 if isinstance(transition, Phases.PreprocessPhase):
492 assert isinstance(klass.preprocess, Types.InputType)
493 current = Phases.JobAction(transition,
494 [current],
495 klass.preprocess)
496 elif isinstance(transition, Phases.PrecompilePhase):
497 current = Phases.JobAction(transition,
498 [current],
499 Types.PCHType)
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000500 elif isinstance(transition, Phases.AnalyzePhase):
501 output = Types.PlistType
502 current = Phases.JobAction(transition,
503 [current],
504 output)
505 elif isinstance(transition, Phases.SyntaxOnlyPhase):
506 output = Types.NothingType
507 current = Phases.JobAction(transition,
508 [current],
509 output)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000510 elif isinstance(transition, Phases.EmitLLVMPhase):
511 if hasDashS:
512 output = Types.LLVMAsmType
513 else:
514 output = Types.LLVMBCType
515 current = Phases.JobAction(transition,
516 [current],
517 output)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000518 elif isinstance(transition, Phases.CompilePhase):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000519 output = Types.AsmTypeNoPP
Daniel Dunbar378530c2009-01-05 19:53:30 +0000520 current = Phases.JobAction(transition,
521 [current],
522 output)
523 elif isinstance(transition, Phases.AssemblePhase):
524 current = Phases.JobAction(transition,
525 [current],
526 Types.ObjectType)
527 elif transition is linkPhase:
528 linkerInputs.append(current)
529 current = None
530 break
531 else:
532 raise RuntimeError,'Unrecognized transition: %s.' % transition
533 pass
534
535 if current is not None:
536 assert not isinstance(current, Phases.InputAction)
537 actions.append(current)
538
539 if linkerInputs:
540 actions.append(Phases.JobAction(linkPhase,
541 linkerInputs,
542 Types.ImageType))
543
544 return actions
545
Daniel Dunbar74727872009-01-06 01:35:44 +0000546 def buildPipeline(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000547 # FIXME: We need to handle canonicalization of the specified arch.
548
Daniel Dunbar31c80812009-01-20 21:29:14 +0000549 archs = {}
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000550 hasDashM = args.getLastArg(self.parser.MGroup)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000551 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbar74727872009-01-06 01:35:44 +0000552 for arg in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000553 if arg.opt is self.parser.archOption:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000554 # FIXME: Canonicalize this.
555 archName = args.getValue(arg)
556 archs[archName] = arg
557
558 archs = archs.values()
Daniel Dunbar378530c2009-01-05 19:53:30 +0000559 if not archs:
Daniel Dunbarf3d5ca02009-01-13 04:05:40 +0000560 archs.append(args.makeSeparateArg(self.hostInfo.getArchName(args),
Daniel Dunbar1ba90982009-01-07 18:54:26 +0000561 self.parser.archOption))
Daniel Dunbar378530c2009-01-05 19:53:30 +0000562
Daniel Dunbar74727872009-01-06 01:35:44 +0000563 actions = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000564
565 # FIXME: Use custom exception for this.
566 #
567 # FIXME: We killed off some others but these aren't yet detected in
568 # a functional manner. If we added information to jobs about which
569 # "auxiliary" files they wrote then we could detect the conflict
570 # these cause downstream.
571 if len(archs) > 1:
572 if hasDashM:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000573 raise Arguments.InvalidArgumentsError("Cannot use -M options with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000574 elif hasSaveTemps:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000575 raise Arguments.InvalidArgumentsError("Cannot use -save-temps with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000576
577 # Execute once per arch.
578 finalActions = []
579 for p in actions:
580 # Make sure we can lipo this kind of output. If not (and it
581 # is an actual output) then we disallow, since we can't
582 # create an output file with the right name without
583 # overwriting it. We could remove this oddity by just
584 # changing the output names to include the arch, which would
585 # also fix -save-temps. Compatibility wins for now.
586 #
587 # FIXME: Is this error substantially less useful than
588 # gcc-dd's? The main problem is that "Cannot use compiler
589 # output with multiple arch flags" won't make sense to most
590 # developers.
591 if (len(archs) > 1 and
592 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000593 raise Arguments.InvalidArgumentsError('Cannot use %s output with multiple arch flags.' % p.type.name)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000594
595 inputs = []
596 for arch in archs:
597 inputs.append(Phases.BindArchAction(p, arch))
598
599 # Lipo if necessary. We do it this way because we need to set
600 # the arch flag so that -Xarch_ gets rewritten.
601 if len(inputs) == 1 or p.type == Types.NothingType:
602 finalActions.extend(inputs)
603 else:
604 finalActions.append(Phases.JobAction(Phases.LipoPhase(),
605 inputs,
606 p.type))
607
Daniel Dunbar378530c2009-01-05 19:53:30 +0000608 return finalActions
609
Daniel Dunbar74727872009-01-06 01:35:44 +0000610 def bindPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000611 jobs = Jobs.JobList()
612
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000613 finalOutput = args.getLastArg(self.parser.oOption)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000614 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000615 hasNoIntegratedCPP = args.getLastArg(self.parser.noIntegratedCPPOption)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000616 hasTraditionalCPP = args.getLastArg(self.parser.traditionalCPPOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000617 hasPipe = args.getLastArg(self.parser.pipeOption)
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000618
Daniel Dunbar378530c2009-01-05 19:53:30 +0000619 # We claim things here so that options for which we silently allow
620 # override only ever claim the used option.
621 if hasPipe:
622 self.claim(hasPipe)
623 # FIXME: Hack, override -pipe till we support it.
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000624 if hasSaveTemps:
625 self.warning('-pipe ignored because -save-temps specified')
626 hasPipe = None
Daniel Dunbar378530c2009-01-05 19:53:30 +0000627 # Claim these here. Its not completely accurate but any warnings
628 # about these being unused are likely to be noise anyway.
629 if hasSaveTemps:
630 self.claim(hasSaveTemps)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000631
632 if hasTraditionalCPP:
633 self.claim(hasTraditionalCPP)
634 elif hasNoIntegratedCPP:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000635 self.claim(hasNoIntegratedCPP)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000636
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000637 # FIXME: Move to... somewhere else.
Daniel Dunbar378530c2009-01-05 19:53:30 +0000638 class InputInfo:
639 def __init__(self, source, type, baseInput):
640 self.source = source
641 self.type = type
642 self.baseInput = baseInput
643
644 def __repr__(self):
645 return '%s(%r, %r, %r)' % (self.__class__.__name__,
646 self.source, self.type, self.baseInput)
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000647
648 def isOriginalInput(self):
649 return self.source is self.baseInput
Daniel Dunbar378530c2009-01-05 19:53:30 +0000650
Daniel Dunbarb3492762009-01-13 18:51:26 +0000651 def createJobs(tc, phase,
652 canAcceptPipe=False, atTopLevel=False, arch=None,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000653 tcArgs=None, linkingOutput=None):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000654 if isinstance(phase, Phases.InputAction):
655 return InputInfo(phase.filename, phase.type, phase.filename)
656 elif isinstance(phase, Phases.BindArchAction):
Daniel Dunbar74727872009-01-06 01:35:44 +0000657 archName = args.getValue(phase.arch)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000658 tc = self.hostInfo.getToolChainForArch(archName)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000659 return createJobs(tc, phase.inputs[0],
660 canAcceptPipe, atTopLevel, phase.arch,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000661 None, linkingOutput)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000662
663 if tcArgs is None:
664 tcArgs = tc.translateArgs(args, arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000665
666 assert isinstance(phase, Phases.JobAction)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000667 tool = tc.selectTool(phase)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000668
669 # See if we should use an integrated CPP. We only use an
670 # integrated cpp when we have exactly one input, since this is
671 # the only use case we care about.
672 useIntegratedCPP = False
673 inputList = phase.inputs
674 if (not hasNoIntegratedCPP and
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000675 not hasTraditionalCPP and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000676 not hasSaveTemps and
677 tool.hasIntegratedCPP()):
678 if (len(phase.inputs) == 1 and
Daniel Dunbar7d494092009-01-20 00:47:24 +0000679 isinstance(phase.inputs[0], Phases.JobAction) and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000680 isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
681 useIntegratedCPP = True
682 inputList = phase.inputs[0].inputs
683
684 # Only try to use pipes when exactly one input.
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000685 attemptToPipeInput = len(inputList) == 1 and tool.acceptsPipedInput()
686 inputs = [createJobs(tc, p, attemptToPipeInput, False,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000687 arch, tcArgs, linkingOutput)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000688 for p in inputList]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000689
690 # Determine if we should output to a pipe.
691 canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
692 outputToPipe = False
693 if canOutputToPipe:
694 # Some things default to writing to a pipe if the final
695 # phase and there was no user override.
696 #
697 # FIXME: What is the best way to handle this?
Daniel Dunbar1b391272009-01-18 21:35:24 +0000698 if atTopLevel:
699 if (isinstance(phase.phase, Phases.PreprocessPhase) and
700 not finalOutput):
701 outputToPipe = True
Daniel Dunbar378530c2009-01-05 19:53:30 +0000702 elif hasPipe:
703 outputToPipe = True
704
705 # Figure out where to put the job (pipes).
706 jobList = jobs
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000707 if isinstance(inputs[0].source, Jobs.PipedJob):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000708 jobList = inputs[0].source
Daniel Dunbar31c80812009-01-20 21:29:14 +0000709
Daniel Dunbar378530c2009-01-05 19:53:30 +0000710 baseInput = inputs[0].baseInput
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000711 output,jobList = self.getOutputName(phase, outputToPipe, jobs, jobList, baseInput,
712 args, atTopLevel, hasSaveTemps, finalOutput)
Daniel Dunbarb421dba2009-01-07 18:40:45 +0000713 tool.constructJob(phase, arch, jobList, inputs, output, phase.type,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000714 tcArgs, linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000715
716 return InputInfo(output, phase.type, baseInput)
717
718 # It is an error to provide a -o option if we are making multiple
719 # output files.
720 if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000721 raise Arguments.InvalidArgumentsError("cannot specify -o when generating multiple files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000722
723 for phase in phases:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000724 # If we are linking an image for multiple archs then the
725 # linker wants -arch_multiple and -final_output <final image
726 # name>. Unfortunately this requires some gross contortions.
727 #
728 # FIXME: This is a hack; find a cleaner way to integrate this
729 # into the process.
730 linkingOutput = None
731 if (isinstance(phase, Phases.JobAction) and
732 isinstance(phase.phase, Phases.LipoPhase)):
733 finalOutput = args.getLastArg(self.parser.oOption)
734 if finalOutput:
735 linkingOutput = finalOutput
736 else:
737 linkingOutput = args.makeSeparateArg('a.out',
738 self.parser.oOption)
739
Daniel Dunbarb3492762009-01-13 18:51:26 +0000740 createJobs(self.toolChain, phase,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000741 canAcceptPipe=True, atTopLevel=True,
742 linkingOutput=linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000743
744 return jobs
Daniel Dunbar31c80812009-01-20 21:29:14 +0000745
746 def getOutputName(self, phase, outputToPipe, jobs, jobList, baseInput,
747 args, atTopLevel, hasSaveTemps, finalOutput):
748 # Figure out where to put the output.
749 if phase.type == Types.NothingType:
750 output = None
751 elif outputToPipe:
752 if isinstance(jobList, Jobs.PipedJob):
753 output = jobList
754 else:
755 jobList = output = Jobs.PipedJob([])
756 jobs.addJob(output)
757 else:
758 # Figure out what the derived output location would be.
759 #
760 # FIXME: gcc has some special case in here so that it doesn't
761 # create output files if they would conflict with an input.
762 if phase.type is Types.ImageType:
763 namedOutput = "a.out"
764 else:
765 inputName = args.getValue(baseInput)
766 base,_ = os.path.splitext(inputName)
767 assert phase.type.tempSuffix is not None
768 namedOutput = base + '.' + phase.type.tempSuffix
769
770 # Output to user requested destination?
771 if atTopLevel and finalOutput:
772 output = finalOutput
773 # Contruct a named destination?
774 elif atTopLevel or hasSaveTemps:
775 # As an annoying special case, pch generation
776 # doesn't strip the pathname.
777 if phase.type is Types.PCHType:
778 outputName = namedOutput
779 else:
780 outputName = os.path.basename(namedOutput)
781 output = args.makeSeparateArg(outputName,
782 self.parser.oOption)
783 else:
784 # Output to temp file...
785 fd,filename = tempfile.mkstemp(suffix='.'+phase.type.tempSuffix)
786 output = args.makeSeparateArg(filename,
787 self.parser.oOption)
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000788 return output,jobList