blob: 6749dae13293d98d344046c6bf86beb613e565cd [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 Dunbar8fd28cd2009-01-28 19:26:20 +000035 # Certain options suppress the 'no input files' warning.
36 self.suppressMissingInputWarning = False
37
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000038 # Host queries which can be forcibly over-riden by the user for
39 # testing purposes.
40 #
41 # FIXME: We should make sure these are drawn from a fixed set so
42 # that nothing downstream ever plays a guessing game.
43
44 def getHostBits(self):
45 if self.cccHostBits:
46 return self.cccHostBits
47
48 return platform.architecture()[0].replace('bit','')
49
50 def getHostMachine(self):
51 if self.cccHostMachine:
52 return self.cccHostMachine
53
54 machine = platform.machine()
55 # Normalize names.
56 if machine == 'Power Macintosh':
57 return 'ppc'
Daniel Dunbar405327e2009-01-27 19:29:51 +000058 if machine == 'x86_64':
59 return 'i386'
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000060 return machine
61
62 def getHostSystemName(self):
63 if self.cccHostSystem:
64 return self.cccHostSystem
65
66 return platform.system().lower()
67
Daniel Dunbarc2148562009-01-12 04:21:12 +000068 def getHostReleaseName(self):
69 if self.cccHostRelease:
70 return self.cccHostRelease
71
72 return platform.release()
73
Daniel Dunbar4c751dc2009-01-14 01:32:05 +000074 def getenvBool(self, name):
75 var = os.getenv(name)
76 if not var:
77 return False
78
79 try:
80 return bool(int(var))
81 except:
82 return False
83
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000084 ###
85
Daniel Dunbarf677a602009-01-21 02:03:52 +000086 def getFilePath(self, name, toolChain=None):
87 tc = toolChain or self.toolChain
88 for p in tc.filePathPrefixes:
89 path = os.path.join(p, name)
90 if os.path.exists(path):
91 return path
92 return name
93
94 def getProgramPath(self, name, toolChain=None):
95 tc = toolChain or self.toolChain
96 for p in tc.programPathPrefixes:
97 path = os.path.join(p, name)
98 if os.path.exists(path):
99 return path
100 return name
101
102 ###
103
Daniel Dunbar74727872009-01-06 01:35:44 +0000104 def run(self, argv):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000105 # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
106 # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
107 # QA_OVERRIDE_GCC3_OPTIONS, ...?
108
109 # FIXME: -V and -b processing
110
111 # Handle some special -ccc- options used for testing which are
112 # only allowed at the beginning of the command line.
113 cccPrintOptions = False
114 cccPrintPhases = False
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000115
116 # FIXME: How to handle override of host? ccc specific options?
117 # Abuse -b?
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000118 if self.getenvBool('CCC_CLANG'):
119 self.cccClang = True
120 if self.getenvBool('CCC_ECHO'):
121 self.cccEcho = True
122 if self.getenvBool('CCC_FALLBACK'):
123 self.cccFallback = True
124
Daniel Dunbar74727872009-01-06 01:35:44 +0000125 while argv and argv[0].startswith('-ccc-'):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000126 fullOpt,argv = argv[0],argv[1:]
127 opt = fullOpt[5:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000128
129 if opt == 'print-options':
130 cccPrintOptions = True
131 elif opt == 'print-phases':
132 cccPrintPhases = True
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +0000133 elif opt == 'cxx':
134 self.cccCXX = True
135 elif opt == 'clang':
136 self.cccClang = True
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000137 elif opt == 'echo':
138 self.cccEcho = True
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +0000139 elif opt == 'fallback':
140 self.cccFallback = True
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000141 elif opt == 'host-bits':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000142 self.cccHostBits,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000143 elif opt == 'host-machine':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000144 self.cccHostMachine,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000145 elif opt == 'host-system':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000146 self.cccHostSystem,argv = argv[0],argv[1:]
Daniel Dunbarc2148562009-01-12 04:21:12 +0000147 elif opt == 'host-release':
148 self.cccHostRelease,argv = argv[0],argv[1:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000149 else:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000150 raise Arguments.InvalidArgumentsError("invalid option: %r" % fullOpt)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000151
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000152 self.hostInfo = HostInfo.getHostInfo(self)
Daniel Dunbar08dea462009-01-10 02:07:54 +0000153 self.toolChain = self.hostInfo.getToolChain()
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000154
Daniel Dunbar74727872009-01-06 01:35:44 +0000155 args = self.parser.parseArgs(argv)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000156
157 # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
158 # need to reparse the Arguments after they have been expanded by
159 # -Xarch. How is this going to work?
160 #
161 # Scratch that, we aren't going to do that; it really disrupts the
162 # organization, doesn't consistently work with gcc-dd, and is
163 # confusing. Instead we are going to enforce that -Xarch_ is only
164 # used with options which do not alter the driver behavior. Let's
165 # hope this is ok, because the current architecture is a little
166 # tied to it.
167
168 if cccPrintOptions:
Daniel Dunbar74727872009-01-06 01:35:44 +0000169 self.printOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000170 sys.exit(0)
171
Daniel Dunbar74727872009-01-06 01:35:44 +0000172 self.handleImmediateOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000173
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000174 if self.hostInfo.useDriverDriver():
Daniel Dunbar74727872009-01-06 01:35:44 +0000175 phases = self.buildPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000176 else:
Daniel Dunbar74727872009-01-06 01:35:44 +0000177 phases = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000178
179 if cccPrintPhases:
Daniel Dunbar74727872009-01-06 01:35:44 +0000180 self.printPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000181 sys.exit(0)
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000182
Daniel Dunbar378530c2009-01-05 19:53:30 +0000183 if 0:
184 print Util.pprint(phases)
185
Daniel Dunbar74727872009-01-06 01:35:44 +0000186 jobs = self.bindPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000187
188 # FIXME: We should provide some basic sanity checking of the
189 # pipeline as a "verification" sort of stage. For example, the
190 # pipeline should never end up writing to an output file in two
191 # places (I think). The pipeline should also never end up writing
192 # to an output file that is an input.
193 #
194 # This is intended to just be a "verify" step, not a functionality
195 # step. It should catch things like the driver driver not
196 # preventing -save-temps, but it shouldn't change behavior (so we
197 # can turn it off in Release-Asserts builds).
198
199 # Print in -### syntax.
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000200 hasHashHashHash = args.getLastArg(self.parser.hashHashHashOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000201 if hasHashHashHash:
202 self.claim(hasHashHashHash)
203 for j in jobs.iterjobs():
204 if isinstance(j, Jobs.Command):
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000205 print >>sys.stderr, ' "%s"' % '" "'.join(j.getArgv())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000206 elif isinstance(j, Jobs.PipedJob):
207 for c in j.commands:
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000208 print >>sys.stderr, ' "%s" %c' % ('" "'.join(c.getArgv()),
209 "| "[c is j.commands[-1]])
Daniel Dunbar378530c2009-01-05 19:53:30 +0000210 elif not isinstance(j, JobList):
211 raise ValueError,'Encountered unknown job.'
212 sys.exit(0)
213
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000214 vArg = args.getLastArg(self.parser.vOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000215 for j in jobs.iterjobs():
216 if isinstance(j, Jobs.Command):
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000217 if vArg or self.cccEcho:
218 print >>sys.stderr, ' '.join(map(str,j.getArgv()))
Anders Carlsson76613bf2009-01-18 02:54:17 +0000219 sys.stderr.flush()
Daniel Dunbarb421dba2009-01-07 18:40:45 +0000220 res = os.spawnvp(os.P_WAIT, j.executable, j.getArgv())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000221 if res:
222 sys.exit(res)
223 elif isinstance(j, Jobs.PipedJob):
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000224 import subprocess
225 procs = []
226 for sj in j.commands:
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000227 if vArg or self.cccEcho:
228 print >> sys.stderr, ' '.join(map(str,sj.getArgv()))
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000229 sys.stdout.flush()
230
231 if not procs:
232 stdin = None
233 else:
234 stdin = procs[-1].stdout
235 if sj is j.commands[-1]:
236 stdout = None
237 else:
238 stdout = subprocess.PIPE
239 procs.append(subprocess.Popen(sj.getArgv(),
240 executable=sj.executable,
241 stdin=stdin,
242 stdout=stdout))
243 for proc in procs:
244 res = proc.wait()
245 if res:
246 sys.exit(res)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000247 else:
248 raise ValueError,'Encountered unknown job.'
249
250 def claim(self, option):
251 # FIXME: Move to OptionList once introduced and implement.
252 pass
253
254 def warning(self, message):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000255 print >>sys.stderr,'%s: %s' % (self.driverName, message)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000256
Daniel Dunbar74727872009-01-06 01:35:44 +0000257 def printOptions(self, args):
258 for i,arg in enumerate(args):
Daniel Dunbar74727872009-01-06 01:35:44 +0000259 if isinstance(arg, Arguments.MultipleValuesArg):
260 values = list(args.getValues(arg))
261 elif isinstance(arg, Arguments.ValueArg):
262 values = [args.getValue(arg)]
263 elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg):
264 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000265 else:
266 values = []
Daniel Dunbar927a5092009-01-06 02:30:10 +0000267 print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,
Daniel Dunbar378530c2009-01-05 19:53:30 +0000268 ', '.join(['"%s"' % v
269 for v in values]))
270
Daniel Dunbar74727872009-01-06 01:35:44 +0000271 def printPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000272 def printPhase(p, f, steps, arch=None):
273 if p in steps:
274 return steps[p]
275 elif isinstance(p, Phases.BindArchAction):
276 for kid in p.inputs:
277 printPhase(kid, f, steps, p.arch)
278 steps[p] = len(steps)
279 return
280
281 if isinstance(p, Phases.InputAction):
282 phaseName = 'input'
Daniel Dunbar74727872009-01-06 01:35:44 +0000283 inputStr = '"%s"' % args.getValue(p.filename)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000284 else:
285 phaseName = p.phase.name
286 inputs = [printPhase(i, f, steps, arch)
287 for i in p.inputs]
288 inputStr = '{%s}' % ', '.join(map(str, inputs))
289 if arch is not None:
Daniel Dunbar74727872009-01-06 01:35:44 +0000290 phaseName += '-' + args.getValue(arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000291 steps[p] = index = len(steps)
292 print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
293 return index
294 steps = {}
295 for phase in phases:
296 printPhase(phase, sys.stdout, steps)
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000297
298 def printVersion(self):
299 # FIXME: Print default target triple.
300 print >>sys.stderr,'ccc version 1.0'
Daniel Dunbar378530c2009-01-05 19:53:30 +0000301
Daniel Dunbar74727872009-01-06 01:35:44 +0000302 def handleImmediateOptions(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000303 # FIXME: Some driver Arguments are consumed right off the bat,
304 # like -dumpversion. Currently the gcc-dd handles these
305 # poorly, so we should be ok handling them upfront instead of
306 # after driver-driver level dispatching.
307 #
308 # FIXME: The actual order of these options in gcc is all over the
309 # place. The -dump ones seem to be first and in specification
310 # order, but there are other levels of precedence. For example,
311 # -print-search-dirs is evaluated before -print-prog-name=,
312 # regardless of order (and the last instance of -print-prog-name=
313 # wins verse itself).
314 #
315 # FIXME: Do we want to report "argument unused" type errors in the
316 # presence of things like -dumpmachine and -print-search-dirs?
317 # Probably not.
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000318 if (args.getLastArg(self.parser.vOption) or
319 args.getLastArg(self.parser.hashHashHashOption)):
320 self.printVersion()
321 self.suppressMissingInputWarning = True
322
Daniel Dunbarf677a602009-01-21 02:03:52 +0000323 arg = (args.getLastArg(self.parser.dumpmachineOption) or
324 args.getLastArg(self.parser.dumpversionOption) or
325 args.getLastArg(self.parser.printSearchDirsOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000326 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000327 raise NotImplementedError('%s unsupported' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000328
Daniel Dunbarf677a602009-01-21 02:03:52 +0000329 arg = (args.getLastArg(self.parser.dumpspecsOption) or
330 args.getLastArg(self.parser.printMultiDirectoryOption) or
Daniel Dunbar73fb9072009-01-23 02:00:46 +0000331 args.getLastArg(self.parser.printMultiOsDirectoryOption) or
Daniel Dunbarf677a602009-01-21 02:03:52 +0000332 args.getLastArg(self.parser.printMultiLibOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000333 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000334 raise Arguments.InvalidArgumentsError('%s unsupported by this driver' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000335
336 arg = args.getLastArg(self.parser.printFileNameOption)
337 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000338 print self.getFilePath(args.getValue(arg))
339 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000340
341 arg = args.getLastArg(self.parser.printProgNameOption)
342 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000343 print self.getProgramPath(args.getValue(arg))
344 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000345
Daniel Dunbarf677a602009-01-21 02:03:52 +0000346 arg = args.getLastArg(self.parser.printLibgccFileNameOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000347 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000348 print self.getFilePath('libgcc.a')
349 sys.exit(0)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000350
Daniel Dunbar74727872009-01-06 01:35:44 +0000351 def buildNormalPipeline(self, args):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000352 hasAnalyze = args.getLastArg(self.parser.analyzeOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000353 hasCombine = args.getLastArg(self.parser.combineOption)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000354 hasEmitLLVM = args.getLastArg(self.parser.emitLLVMOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000355 hasSyntaxOnly = args.getLastArg(self.parser.syntaxOnlyOption)
356 hasDashC = args.getLastArg(self.parser.cOption)
357 hasDashE = args.getLastArg(self.parser.EOption)
358 hasDashS = args.getLastArg(self.parser.SOption)
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000359 hasDashM = args.getLastArg(self.parser.MOption)
360 hasDashMM = args.getLastArg(self.parser.MMOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000361
362 inputType = None
363 inputTypeOpt = None
364 inputs = []
365 for a in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000366 if a.opt is self.parser.inputOption:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000367 inputValue = args.getValue(a)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000368 if inputType is None:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000369 base,ext = os.path.splitext(inputValue)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000370 if ext and ext in Types.kTypeSuffixMap:
371 klass = Types.kTypeSuffixMap[ext]
372 else:
373 # FIXME: Its not clear why we shouldn't just
374 # revert to unknown. I think this is more likely a
375 # bug / unintended behavior in gcc. Not very
376 # important though.
377 klass = Types.ObjectType
378 else:
379 assert inputTypeOpt is not None
380 self.claim(inputTypeOpt)
381 klass = inputType
Daniel Dunbara0026f22009-01-16 23:12:12 +0000382
383 # Check that the file exists. It isn't clear this is
384 # worth doing, since the tool presumably does this
385 # anyway, and this just adds an extra stat to the
386 # equation, but this is gcc compatible.
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000387 if inputValue != '-' and not os.path.exists(inputValue):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000388 self.warning("%s: No such file or directory" % inputValue)
389 else:
390 inputs.append((klass, a))
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000391 elif a.opt.isLinkerInput:
392 # Treat as a linker input.
Daniel Dunbar927a5092009-01-06 02:30:10 +0000393 #
394 # FIXME: This might not be good enough. We may
395 # need to introduce another type for this case, so
396 # that other code which needs to know the inputs
397 # handles this properly. Best not to try and lipo
398 # this, for example.
399 inputs.append((Types.ObjectType, a))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000400 elif a.opt is self.parser.xOption:
Daniel Dunbar927a5092009-01-06 02:30:10 +0000401 inputTypeOpt = a
402 value = args.getValue(a)
403 if value in Types.kTypeSpecifierMap:
404 inputType = Types.kTypeSpecifierMap[value]
405 else:
406 # FIXME: How are we going to handle diagnostics.
407 self.warning("language %s not recognized" % value)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000408
Daniel Dunbar927a5092009-01-06 02:30:10 +0000409 # FIXME: Its not clear why we shouldn't just
410 # revert to unknown. I think this is more likely a
411 # bug / unintended behavior in gcc. Not very
412 # important though.
Daniel Dunbar6cb42052009-01-13 21:07:43 +0000413 inputType = Types.ObjectType
Daniel Dunbar378530c2009-01-05 19:53:30 +0000414
415 # We claim things here so that options for which we silently allow
416 # override only ever claim the used option.
417 if hasCombine:
418 self.claim(hasCombine)
419
420 finalPhase = Phases.Phase.eOrderPostAssemble
421 finalPhaseOpt = None
422
423 # Determine what compilation mode we are in.
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000424 if hasDashE or hasDashM or hasDashMM:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000425 finalPhase = Phases.Phase.eOrderPreprocess
426 finalPhaseOpt = hasDashE
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000427 elif (hasAnalyze or hasSyntaxOnly or
428 hasEmitLLVM or hasDashS):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000429 finalPhase = Phases.Phase.eOrderCompile
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000430 finalPhaseOpt = (hasAnalyze or hasSyntaxOnly or
431 hasEmitLLVM or hasDashS)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000432 elif hasDashC:
433 finalPhase = Phases.Phase.eOrderAssemble
434 finalPhaseOpt = hasDashC
435
436 if finalPhaseOpt:
437 self.claim(finalPhaseOpt)
438
439 # FIXME: Support -combine.
440 if hasCombine:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000441 raise NotImplementedError,"-combine is not yet supported"
442
Daniel Dunbar80e48b72009-01-17 00:53:19 +0000443 # Reject -Z* at the top level for now.
444 arg = args.getLastArg(self.parser.ZOption)
445 if arg:
446 raise Arguments.InvalidArgumentsError("%s: unsupported use of internal gcc option" % ' '.join(args.render(arg)))
447
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000448 if not inputs and not self.suppressMissingInputWarning:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000449 raise Arguments.InvalidArgumentsError("no input files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000450
451 actions = []
452 linkerInputs = []
453 # FIXME: This is gross.
454 linkPhase = Phases.LinkPhase()
455 for klass,input in inputs:
456 # Figure out what step to start at.
457
458 # FIXME: This should be part of the input class probably?
459 # Altough it doesn't quite fit there either, things like
460 # asm-with-preprocess don't easily fit into a linear scheme.
461
462 # FIXME: I think we are going to end up wanting to just build
463 # a simple FSA which we run the inputs down.
464 sequence = []
465 if klass.preprocess:
466 sequence.append(Phases.PreprocessPhase())
467 if klass == Types.ObjectType:
468 sequence.append(linkPhase)
469 elif klass.onlyAssemble:
470 sequence.extend([Phases.AssemblePhase(),
471 linkPhase])
472 elif klass.onlyPrecompile:
473 sequence.append(Phases.PrecompilePhase())
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000474 elif hasAnalyze:
475 sequence.append(Phases.AnalyzePhase())
476 elif hasSyntaxOnly:
477 sequence.append(Phases.SyntaxOnlyPhase())
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000478 elif hasEmitLLVM:
479 sequence.append(Phases.EmitLLVMPhase())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000480 else:
481 sequence.extend([Phases.CompilePhase(),
482 Phases.AssemblePhase(),
483 linkPhase])
484
485 if sequence[0].order > finalPhase:
486 assert finalPhaseOpt and finalPhaseOpt.opt
487 # FIXME: Explain what type of input file is. Or just match
488 # gcc warning.
Daniel Dunbar74727872009-01-06 01:35:44 +0000489 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input),
Daniel Dunbar378530c2009-01-05 19:53:30 +0000490 sequence[0].name,
491 finalPhaseOpt.opt.name))
492 else:
493 # Build the pipeline for this file.
494
495 current = Phases.InputAction(input, klass)
496 for transition in sequence:
497 # If the current action produces no output, or we are
498 # past what the user requested, we are done.
499 if (current.type is Types.NothingType or
500 transition.order > finalPhase):
501 break
502 else:
503 if isinstance(transition, Phases.PreprocessPhase):
504 assert isinstance(klass.preprocess, Types.InputType)
505 current = Phases.JobAction(transition,
506 [current],
507 klass.preprocess)
508 elif isinstance(transition, Phases.PrecompilePhase):
509 current = Phases.JobAction(transition,
510 [current],
511 Types.PCHType)
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000512 elif isinstance(transition, Phases.AnalyzePhase):
513 output = Types.PlistType
514 current = Phases.JobAction(transition,
515 [current],
516 output)
517 elif isinstance(transition, Phases.SyntaxOnlyPhase):
518 output = Types.NothingType
519 current = Phases.JobAction(transition,
520 [current],
521 output)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000522 elif isinstance(transition, Phases.EmitLLVMPhase):
523 if hasDashS:
524 output = Types.LLVMAsmType
525 else:
526 output = Types.LLVMBCType
527 current = Phases.JobAction(transition,
528 [current],
529 output)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000530 elif isinstance(transition, Phases.CompilePhase):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000531 output = Types.AsmTypeNoPP
Daniel Dunbar378530c2009-01-05 19:53:30 +0000532 current = Phases.JobAction(transition,
533 [current],
534 output)
535 elif isinstance(transition, Phases.AssemblePhase):
536 current = Phases.JobAction(transition,
537 [current],
538 Types.ObjectType)
539 elif transition is linkPhase:
540 linkerInputs.append(current)
541 current = None
542 break
543 else:
544 raise RuntimeError,'Unrecognized transition: %s.' % transition
545 pass
546
547 if current is not None:
548 assert not isinstance(current, Phases.InputAction)
549 actions.append(current)
550
551 if linkerInputs:
552 actions.append(Phases.JobAction(linkPhase,
553 linkerInputs,
554 Types.ImageType))
555
556 return actions
557
Daniel Dunbar74727872009-01-06 01:35:44 +0000558 def buildPipeline(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000559 # FIXME: We need to handle canonicalization of the specified arch.
560
Daniel Dunbar31c80812009-01-20 21:29:14 +0000561 archs = {}
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000562 hasDashM = args.getLastArg(self.parser.MGroup)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000563 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbar74727872009-01-06 01:35:44 +0000564 for arg in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000565 if arg.opt is self.parser.archOption:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000566 # FIXME: Canonicalize this.
567 archName = args.getValue(arg)
568 archs[archName] = arg
569
570 archs = archs.values()
Daniel Dunbar378530c2009-01-05 19:53:30 +0000571 if not archs:
Daniel Dunbarf3d5ca02009-01-13 04:05:40 +0000572 archs.append(args.makeSeparateArg(self.hostInfo.getArchName(args),
Daniel Dunbar1ba90982009-01-07 18:54:26 +0000573 self.parser.archOption))
Daniel Dunbar378530c2009-01-05 19:53:30 +0000574
Daniel Dunbar74727872009-01-06 01:35:44 +0000575 actions = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000576
577 # FIXME: Use custom exception for this.
578 #
579 # FIXME: We killed off some others but these aren't yet detected in
580 # a functional manner. If we added information to jobs about which
581 # "auxiliary" files they wrote then we could detect the conflict
582 # these cause downstream.
583 if len(archs) > 1:
584 if hasDashM:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000585 raise Arguments.InvalidArgumentsError("Cannot use -M options with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000586 elif hasSaveTemps:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000587 raise Arguments.InvalidArgumentsError("Cannot use -save-temps with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000588
589 # Execute once per arch.
590 finalActions = []
591 for p in actions:
592 # Make sure we can lipo this kind of output. If not (and it
593 # is an actual output) then we disallow, since we can't
594 # create an output file with the right name without
595 # overwriting it. We could remove this oddity by just
596 # changing the output names to include the arch, which would
597 # also fix -save-temps. Compatibility wins for now.
598 #
599 # FIXME: Is this error substantially less useful than
600 # gcc-dd's? The main problem is that "Cannot use compiler
601 # output with multiple arch flags" won't make sense to most
602 # developers.
603 if (len(archs) > 1 and
604 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000605 raise Arguments.InvalidArgumentsError('Cannot use %s output with multiple arch flags.' % p.type.name)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000606
607 inputs = []
608 for arch in archs:
609 inputs.append(Phases.BindArchAction(p, arch))
610
611 # Lipo if necessary. We do it this way because we need to set
612 # the arch flag so that -Xarch_ gets rewritten.
613 if len(inputs) == 1 or p.type == Types.NothingType:
614 finalActions.extend(inputs)
615 else:
616 finalActions.append(Phases.JobAction(Phases.LipoPhase(),
617 inputs,
618 p.type))
619
Daniel Dunbar378530c2009-01-05 19:53:30 +0000620 return finalActions
621
Daniel Dunbar74727872009-01-06 01:35:44 +0000622 def bindPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000623 jobs = Jobs.JobList()
624
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000625 finalOutput = args.getLastArg(self.parser.oOption)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000626 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000627 hasNoIntegratedCPP = args.getLastArg(self.parser.noIntegratedCPPOption)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000628 hasTraditionalCPP = args.getLastArg(self.parser.traditionalCPPOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000629 hasPipe = args.getLastArg(self.parser.pipeOption)
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000630
Daniel Dunbar378530c2009-01-05 19:53:30 +0000631 # We claim things here so that options for which we silently allow
632 # override only ever claim the used option.
633 if hasPipe:
634 self.claim(hasPipe)
635 # FIXME: Hack, override -pipe till we support it.
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000636 if hasSaveTemps:
637 self.warning('-pipe ignored because -save-temps specified')
638 hasPipe = None
Daniel Dunbar378530c2009-01-05 19:53:30 +0000639 # Claim these here. Its not completely accurate but any warnings
640 # about these being unused are likely to be noise anyway.
641 if hasSaveTemps:
642 self.claim(hasSaveTemps)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000643
644 if hasTraditionalCPP:
645 self.claim(hasTraditionalCPP)
646 elif hasNoIntegratedCPP:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000647 self.claim(hasNoIntegratedCPP)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000648
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000649 # FIXME: Move to... somewhere else.
Daniel Dunbar378530c2009-01-05 19:53:30 +0000650 class InputInfo:
651 def __init__(self, source, type, baseInput):
652 self.source = source
653 self.type = type
654 self.baseInput = baseInput
655
656 def __repr__(self):
657 return '%s(%r, %r, %r)' % (self.__class__.__name__,
658 self.source, self.type, self.baseInput)
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000659
660 def isOriginalInput(self):
661 return self.source is self.baseInput
Daniel Dunbar378530c2009-01-05 19:53:30 +0000662
Daniel Dunbarb3492762009-01-13 18:51:26 +0000663 def createJobs(tc, phase,
664 canAcceptPipe=False, atTopLevel=False, arch=None,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000665 tcArgs=None, linkingOutput=None):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000666 if isinstance(phase, Phases.InputAction):
667 return InputInfo(phase.filename, phase.type, phase.filename)
668 elif isinstance(phase, Phases.BindArchAction):
Daniel Dunbar74727872009-01-06 01:35:44 +0000669 archName = args.getValue(phase.arch)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000670 tc = self.hostInfo.getToolChainForArch(archName)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000671 return createJobs(tc, phase.inputs[0],
672 canAcceptPipe, atTopLevel, phase.arch,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000673 None, linkingOutput)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000674
675 if tcArgs is None:
676 tcArgs = tc.translateArgs(args, arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000677
678 assert isinstance(phase, Phases.JobAction)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000679 tool = tc.selectTool(phase)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000680
681 # See if we should use an integrated CPP. We only use an
682 # integrated cpp when we have exactly one input, since this is
683 # the only use case we care about.
684 useIntegratedCPP = False
685 inputList = phase.inputs
686 if (not hasNoIntegratedCPP and
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000687 not hasTraditionalCPP and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000688 not hasSaveTemps and
689 tool.hasIntegratedCPP()):
690 if (len(phase.inputs) == 1 and
Daniel Dunbar7d494092009-01-20 00:47:24 +0000691 isinstance(phase.inputs[0], Phases.JobAction) and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000692 isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
693 useIntegratedCPP = True
694 inputList = phase.inputs[0].inputs
695
696 # Only try to use pipes when exactly one input.
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000697 attemptToPipeInput = len(inputList) == 1 and tool.acceptsPipedInput()
698 inputs = [createJobs(tc, p, attemptToPipeInput, False,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000699 arch, tcArgs, linkingOutput)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000700 for p in inputList]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000701
702 # Determine if we should output to a pipe.
703 canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
704 outputToPipe = False
705 if canOutputToPipe:
706 # Some things default to writing to a pipe if the final
707 # phase and there was no user override.
708 #
709 # FIXME: What is the best way to handle this?
Daniel Dunbar1b391272009-01-18 21:35:24 +0000710 if atTopLevel:
711 if (isinstance(phase.phase, Phases.PreprocessPhase) and
712 not finalOutput):
713 outputToPipe = True
Daniel Dunbar378530c2009-01-05 19:53:30 +0000714 elif hasPipe:
715 outputToPipe = True
716
717 # Figure out where to put the job (pipes).
718 jobList = jobs
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000719 if isinstance(inputs[0].source, Jobs.PipedJob):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000720 jobList = inputs[0].source
Daniel Dunbar31c80812009-01-20 21:29:14 +0000721
Daniel Dunbar378530c2009-01-05 19:53:30 +0000722 baseInput = inputs[0].baseInput
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000723 output,jobList = self.getOutputName(phase, outputToPipe, jobs, jobList, baseInput,
724 args, atTopLevel, hasSaveTemps, finalOutput)
Daniel Dunbarb421dba2009-01-07 18:40:45 +0000725 tool.constructJob(phase, arch, jobList, inputs, output, phase.type,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000726 tcArgs, linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000727
728 return InputInfo(output, phase.type, baseInput)
729
730 # It is an error to provide a -o option if we are making multiple
731 # output files.
732 if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000733 raise Arguments.InvalidArgumentsError("cannot specify -o when generating multiple files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000734
735 for phase in phases:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000736 # If we are linking an image for multiple archs then the
737 # linker wants -arch_multiple and -final_output <final image
738 # name>. Unfortunately this requires some gross contortions.
739 #
740 # FIXME: This is a hack; find a cleaner way to integrate this
741 # into the process.
742 linkingOutput = None
743 if (isinstance(phase, Phases.JobAction) and
744 isinstance(phase.phase, Phases.LipoPhase)):
745 finalOutput = args.getLastArg(self.parser.oOption)
746 if finalOutput:
747 linkingOutput = finalOutput
748 else:
749 linkingOutput = args.makeSeparateArg('a.out',
750 self.parser.oOption)
751
Daniel Dunbarb3492762009-01-13 18:51:26 +0000752 createJobs(self.toolChain, phase,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000753 canAcceptPipe=True, atTopLevel=True,
754 linkingOutput=linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000755
756 return jobs
Daniel Dunbar31c80812009-01-20 21:29:14 +0000757
758 def getOutputName(self, phase, outputToPipe, jobs, jobList, baseInput,
759 args, atTopLevel, hasSaveTemps, finalOutput):
760 # Figure out where to put the output.
761 if phase.type == Types.NothingType:
762 output = None
763 elif outputToPipe:
764 if isinstance(jobList, Jobs.PipedJob):
765 output = jobList
766 else:
767 jobList = output = Jobs.PipedJob([])
768 jobs.addJob(output)
769 else:
770 # Figure out what the derived output location would be.
771 #
772 # FIXME: gcc has some special case in here so that it doesn't
773 # create output files if they would conflict with an input.
774 if phase.type is Types.ImageType:
775 namedOutput = "a.out"
776 else:
777 inputName = args.getValue(baseInput)
778 base,_ = os.path.splitext(inputName)
779 assert phase.type.tempSuffix is not None
780 namedOutput = base + '.' + phase.type.tempSuffix
781
782 # Output to user requested destination?
783 if atTopLevel and finalOutput:
784 output = finalOutput
785 # Contruct a named destination?
786 elif atTopLevel or hasSaveTemps:
787 # As an annoying special case, pch generation
788 # doesn't strip the pathname.
789 if phase.type is Types.PCHType:
790 outputName = namedOutput
791 else:
792 outputName = os.path.basename(namedOutput)
793 output = args.makeSeparateArg(outputName,
794 self.parser.oOption)
795 else:
796 # Output to temp file...
797 fd,filename = tempfile.mkstemp(suffix='.'+phase.type.tempSuffix)
798 output = args.makeSeparateArg(filename,
799 self.parser.oOption)
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000800 return output,jobList