blob: cda016153e894ccd20258f8bb2960eb07da3e17e [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
Daniel Dunbar4c751dc2009-01-14 01:32:05 +000031 self.cccEcho = False
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +000032 self.cccFallback = False
Daniel Dunbar72999172009-01-29 23:54:06 +000033 self.cccNoClang = self.cccNoClangCXX = self.cccNoClangPreprocessor = False
34 self.cccClangArchs = None
Daniel Dunbar378530c2009-01-05 19:53:30 +000035
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +000036 # Certain options suppress the 'no input files' warning.
37 self.suppressMissingInputWarning = False
38
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000039 # Host queries which can be forcibly over-riden by the user for
40 # testing purposes.
41 #
42 # FIXME: We should make sure these are drawn from a fixed set so
43 # that nothing downstream ever plays a guessing game.
44
45 def getHostBits(self):
46 if self.cccHostBits:
47 return self.cccHostBits
48
49 return platform.architecture()[0].replace('bit','')
50
51 def getHostMachine(self):
52 if self.cccHostMachine:
53 return self.cccHostMachine
54
55 machine = platform.machine()
56 # Normalize names.
57 if machine == 'Power Macintosh':
58 return 'ppc'
Daniel Dunbar405327e2009-01-27 19:29:51 +000059 if machine == 'x86_64':
60 return 'i386'
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000061 return machine
62
63 def getHostSystemName(self):
64 if self.cccHostSystem:
65 return self.cccHostSystem
66
67 return platform.system().lower()
68
Daniel Dunbarc2148562009-01-12 04:21:12 +000069 def getHostReleaseName(self):
70 if self.cccHostRelease:
71 return self.cccHostRelease
72
73 return platform.release()
74
Daniel Dunbar4c751dc2009-01-14 01:32:05 +000075 def getenvBool(self, name):
76 var = os.getenv(name)
77 if not var:
78 return False
79
80 try:
81 return bool(int(var))
82 except:
83 return False
84
Daniel Dunbardbc9ee92009-01-09 22:21:24 +000085 ###
86
Daniel Dunbarf677a602009-01-21 02:03:52 +000087 def getFilePath(self, name, toolChain=None):
88 tc = toolChain or self.toolChain
89 for p in tc.filePathPrefixes:
90 path = os.path.join(p, name)
91 if os.path.exists(path):
92 return path
93 return name
94
95 def getProgramPath(self, name, toolChain=None):
96 tc = toolChain or self.toolChain
97 for p in tc.programPathPrefixes:
98 path = os.path.join(p, name)
99 if os.path.exists(path):
100 return path
101 return name
102
103 ###
104
Daniel Dunbar74727872009-01-06 01:35:44 +0000105 def run(self, argv):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000106 # FIXME: Things to support from environment: GCC_EXEC_PREFIX,
107 # COMPILER_PATH, LIBRARY_PATH, LPATH, CC_PRINT_OPTIONS,
108 # QA_OVERRIDE_GCC3_OPTIONS, ...?
109
110 # FIXME: -V and -b processing
111
112 # Handle some special -ccc- options used for testing which are
113 # only allowed at the beginning of the command line.
114 cccPrintOptions = False
115 cccPrintPhases = False
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000116
117 # FIXME: How to handle override of host? ccc specific options?
118 # Abuse -b?
Daniel Dunbar72999172009-01-29 23:54:06 +0000119 arg = os.getenv('CCC_ADD_ARGS')
120 if arg:
121 args = filter(None, map(str.strip, arg.split(',')))
122 argv = args + argv
Daniel Dunbar4c751dc2009-01-14 01:32:05 +0000123
Daniel Dunbar74727872009-01-06 01:35:44 +0000124 while argv and argv[0].startswith('-ccc-'):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000125 fullOpt,argv = argv[0],argv[1:]
126 opt = fullOpt[5:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000127
128 if opt == 'print-options':
129 cccPrintOptions = True
130 elif opt == 'print-phases':
131 cccPrintPhases = True
Daniel Dunbar7c2f91b2009-01-14 01:03:36 +0000132 elif opt == 'cxx':
133 self.cccCXX = 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 Dunbar72999172009-01-29 23:54:06 +0000138
139 elif opt == 'no-clang':
140 self.cccNoClang = True
141 elif opt == 'no-clang-cxx':
142 self.cccNoClangCXX = True
143 elif opt == 'no-clang-cpp':
144 self.cccNoClangPreprocessor = True
145 elif opt == 'clang-archs':
146 self.cccClangArchs,argv = argv[0].split(','),argv[1:]
147
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000148 elif opt == 'host-bits':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000149 self.cccHostBits,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000150 elif opt == 'host-machine':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000151 self.cccHostMachine,argv = argv[0],argv[1:]
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000152 elif opt == 'host-system':
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000153 self.cccHostSystem,argv = argv[0],argv[1:]
Daniel Dunbarc2148562009-01-12 04:21:12 +0000154 elif opt == 'host-release':
155 self.cccHostRelease,argv = argv[0],argv[1:]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000156 else:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000157 raise Arguments.InvalidArgumentsError("invalid option: %r" % fullOpt)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000158
Daniel Dunbardbc9ee92009-01-09 22:21:24 +0000159 self.hostInfo = HostInfo.getHostInfo(self)
Daniel Dunbar08dea462009-01-10 02:07:54 +0000160 self.toolChain = self.hostInfo.getToolChain()
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000161
Daniel Dunbar74727872009-01-06 01:35:44 +0000162 args = self.parser.parseArgs(argv)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000163
164 # FIXME: Ho hum I have just realized -Xarch_ is broken. We really
165 # need to reparse the Arguments after they have been expanded by
166 # -Xarch. How is this going to work?
167 #
168 # Scratch that, we aren't going to do that; it really disrupts the
169 # organization, doesn't consistently work with gcc-dd, and is
170 # confusing. Instead we are going to enforce that -Xarch_ is only
171 # used with options which do not alter the driver behavior. Let's
172 # hope this is ok, because the current architecture is a little
173 # tied to it.
174
175 if cccPrintOptions:
Daniel Dunbar74727872009-01-06 01:35:44 +0000176 self.printOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000177 sys.exit(0)
178
Daniel Dunbar74727872009-01-06 01:35:44 +0000179 self.handleImmediateOptions(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000180
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000181 if self.hostInfo.useDriverDriver():
Daniel Dunbar74727872009-01-06 01:35:44 +0000182 phases = self.buildPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000183 else:
Daniel Dunbar74727872009-01-06 01:35:44 +0000184 phases = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000185
186 if cccPrintPhases:
Daniel Dunbar74727872009-01-06 01:35:44 +0000187 self.printPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000188 sys.exit(0)
Daniel Dunbar4ed45ea2009-01-09 01:00:40 +0000189
Daniel Dunbar378530c2009-01-05 19:53:30 +0000190 if 0:
191 print Util.pprint(phases)
192
Daniel Dunbar74727872009-01-06 01:35:44 +0000193 jobs = self.bindPhases(phases, args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000194
195 # FIXME: We should provide some basic sanity checking of the
196 # pipeline as a "verification" sort of stage. For example, the
197 # pipeline should never end up writing to an output file in two
198 # places (I think). The pipeline should also never end up writing
199 # to an output file that is an input.
200 #
201 # This is intended to just be a "verify" step, not a functionality
202 # step. It should catch things like the driver driver not
203 # preventing -save-temps, but it shouldn't change behavior (so we
204 # can turn it off in Release-Asserts builds).
205
206 # Print in -### syntax.
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000207 hasHashHashHash = args.getLastArg(self.parser.hashHashHashOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000208 if hasHashHashHash:
209 self.claim(hasHashHashHash)
210 for j in jobs.iterjobs():
211 if isinstance(j, Jobs.Command):
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000212 print >>sys.stderr, ' "%s"' % '" "'.join(j.getArgv())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000213 elif isinstance(j, Jobs.PipedJob):
214 for c in j.commands:
Daniel Dunbard87b6ec2009-01-12 19:36:35 +0000215 print >>sys.stderr, ' "%s" %c' % ('" "'.join(c.getArgv()),
216 "| "[c is j.commands[-1]])
Daniel Dunbar378530c2009-01-05 19:53:30 +0000217 elif not isinstance(j, JobList):
218 raise ValueError,'Encountered unknown job.'
219 sys.exit(0)
220
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000221 vArg = args.getLastArg(self.parser.vOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000222 for j in jobs.iterjobs():
223 if isinstance(j, Jobs.Command):
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000224 if vArg or self.cccEcho:
225 print >>sys.stderr, ' '.join(map(str,j.getArgv()))
Anders Carlsson76613bf2009-01-18 02:54:17 +0000226 sys.stderr.flush()
Daniel Dunbarb421dba2009-01-07 18:40:45 +0000227 res = os.spawnvp(os.P_WAIT, j.executable, j.getArgv())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000228 if res:
229 sys.exit(res)
230 elif isinstance(j, Jobs.PipedJob):
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000231 import subprocess
232 procs = []
233 for sj in j.commands:
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000234 if vArg or self.cccEcho:
235 print >> sys.stderr, ' '.join(map(str,sj.getArgv()))
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000236 sys.stdout.flush()
237
238 if not procs:
239 stdin = None
240 else:
241 stdin = procs[-1].stdout
242 if sj is j.commands[-1]:
243 stdout = None
244 else:
245 stdout = subprocess.PIPE
246 procs.append(subprocess.Popen(sj.getArgv(),
247 executable=sj.executable,
248 stdin=stdin,
249 stdout=stdout))
250 for proc in procs:
251 res = proc.wait()
252 if res:
253 sys.exit(res)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000254 else:
255 raise ValueError,'Encountered unknown job.'
256
257 def claim(self, option):
258 # FIXME: Move to OptionList once introduced and implement.
259 pass
260
261 def warning(self, message):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000262 print >>sys.stderr,'%s: %s' % (self.driverName, message)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000263
Daniel Dunbar74727872009-01-06 01:35:44 +0000264 def printOptions(self, args):
265 for i,arg in enumerate(args):
Daniel Dunbar74727872009-01-06 01:35:44 +0000266 if isinstance(arg, Arguments.MultipleValuesArg):
267 values = list(args.getValues(arg))
268 elif isinstance(arg, Arguments.ValueArg):
269 values = [args.getValue(arg)]
270 elif isinstance(arg, Arguments.JoinedAndSeparateValuesArg):
271 values = [args.getJoinedValue(arg), args.getSeparateValue(arg)]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000272 else:
273 values = []
Daniel Dunbar927a5092009-01-06 02:30:10 +0000274 print 'Option %d - Name: "%s", Values: {%s}' % (i, arg.opt.name,
Daniel Dunbar378530c2009-01-05 19:53:30 +0000275 ', '.join(['"%s"' % v
276 for v in values]))
277
Daniel Dunbar74727872009-01-06 01:35:44 +0000278 def printPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000279 def printPhase(p, f, steps, arch=None):
280 if p in steps:
281 return steps[p]
282 elif isinstance(p, Phases.BindArchAction):
283 for kid in p.inputs:
284 printPhase(kid, f, steps, p.arch)
285 steps[p] = len(steps)
286 return
287
288 if isinstance(p, Phases.InputAction):
289 phaseName = 'input'
Daniel Dunbar74727872009-01-06 01:35:44 +0000290 inputStr = '"%s"' % args.getValue(p.filename)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000291 else:
292 phaseName = p.phase.name
293 inputs = [printPhase(i, f, steps, arch)
294 for i in p.inputs]
295 inputStr = '{%s}' % ', '.join(map(str, inputs))
296 if arch is not None:
Daniel Dunbar74727872009-01-06 01:35:44 +0000297 phaseName += '-' + args.getValue(arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000298 steps[p] = index = len(steps)
299 print "%d: %s, %s, %s" % (index,phaseName,inputStr,p.type.name)
300 return index
301 steps = {}
302 for phase in phases:
303 printPhase(phase, sys.stdout, steps)
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000304
305 def printVersion(self):
306 # FIXME: Print default target triple.
Mike Stump406abf62009-02-11 01:01:17 +0000307 vers = '$HeadURL$'
308 vers = vers.split('/tools/ccc')[0]
Mike Stump6bf3bd42009-02-11 01:11:36 +0000309 vers = vers.split('/clang/tools/clang')[0]
Mike Stump406abf62009-02-11 01:01:17 +0000310 vers = ' (' + vers[10:] + ')'
311 print >>sys.stderr,'ccc version 1.0' + vers
Daniel Dunbar378530c2009-01-05 19:53:30 +0000312
Daniel Dunbar74727872009-01-06 01:35:44 +0000313 def handleImmediateOptions(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000314 # FIXME: Some driver Arguments are consumed right off the bat,
315 # like -dumpversion. Currently the gcc-dd handles these
316 # poorly, so we should be ok handling them upfront instead of
317 # after driver-driver level dispatching.
318 #
319 # FIXME: The actual order of these options in gcc is all over the
320 # place. The -dump ones seem to be first and in specification
321 # order, but there are other levels of precedence. For example,
322 # -print-search-dirs is evaluated before -print-prog-name=,
323 # regardless of order (and the last instance of -print-prog-name=
324 # wins verse itself).
325 #
326 # FIXME: Do we want to report "argument unused" type errors in the
327 # presence of things like -dumpmachine and -print-search-dirs?
328 # Probably not.
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000329 if (args.getLastArg(self.parser.vOption) or
330 args.getLastArg(self.parser.hashHashHashOption)):
331 self.printVersion()
332 self.suppressMissingInputWarning = True
333
Daniel Dunbarf677a602009-01-21 02:03:52 +0000334 arg = (args.getLastArg(self.parser.dumpmachineOption) or
335 args.getLastArg(self.parser.dumpversionOption) or
336 args.getLastArg(self.parser.printSearchDirsOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000337 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000338 raise NotImplementedError('%s unsupported' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000339
Daniel Dunbarf677a602009-01-21 02:03:52 +0000340 arg = (args.getLastArg(self.parser.dumpspecsOption) or
341 args.getLastArg(self.parser.printMultiDirectoryOption) or
Daniel Dunbar73fb9072009-01-23 02:00:46 +0000342 args.getLastArg(self.parser.printMultiOsDirectoryOption) or
Daniel Dunbarf677a602009-01-21 02:03:52 +0000343 args.getLastArg(self.parser.printMultiLibOption))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000344 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000345 raise Arguments.InvalidArgumentsError('%s unsupported by this driver' % arg.opt.name)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000346
347 arg = args.getLastArg(self.parser.printFileNameOption)
348 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000349 print self.getFilePath(args.getValue(arg))
350 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000351
352 arg = args.getLastArg(self.parser.printProgNameOption)
353 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000354 print self.getProgramPath(args.getValue(arg))
355 sys.exit(0)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000356
Daniel Dunbarf677a602009-01-21 02:03:52 +0000357 arg = args.getLastArg(self.parser.printLibgccFileNameOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000358 if arg:
Daniel Dunbarf677a602009-01-21 02:03:52 +0000359 print self.getFilePath('libgcc.a')
360 sys.exit(0)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000361
Daniel Dunbar74727872009-01-06 01:35:44 +0000362 def buildNormalPipeline(self, args):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000363 hasAnalyze = args.getLastArg(self.parser.analyzeOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000364 hasCombine = args.getLastArg(self.parser.combineOption)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000365 hasEmitLLVM = args.getLastArg(self.parser.emitLLVMOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000366 hasSyntaxOnly = args.getLastArg(self.parser.syntaxOnlyOption)
367 hasDashC = args.getLastArg(self.parser.cOption)
368 hasDashE = args.getLastArg(self.parser.EOption)
369 hasDashS = args.getLastArg(self.parser.SOption)
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000370 hasDashM = args.getLastArg(self.parser.MOption)
371 hasDashMM = args.getLastArg(self.parser.MMOption)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000372
373 inputType = None
374 inputTypeOpt = None
375 inputs = []
376 for a in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000377 if a.opt is self.parser.inputOption:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000378 inputValue = args.getValue(a)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000379 if inputType is None:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000380 base,ext = os.path.splitext(inputValue)
Daniel Dunbar6dbfa662009-02-05 23:44:44 +0000381 # stdin is handled specially.
382 if inputValue == '-':
383 if args.getLastArg(self.parser.EOption):
384 # Treat as a C input needing preprocessing
385 # (or Obj-C if over-ridden below).
386 klass = Types.CType
387 else:
388 raise Arguments.InvalidArgumentsError("-E or -x required when input is from standard input")
389 elif ext and ext in Types.kTypeSuffixMap:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000390 klass = Types.kTypeSuffixMap[ext]
391 else:
392 # FIXME: Its not clear why we shouldn't just
393 # revert to unknown. I think this is more likely a
394 # bug / unintended behavior in gcc. Not very
395 # important though.
396 klass = Types.ObjectType
Daniel Dunbar6dbfa662009-02-05 23:44:44 +0000397
398 # -ObjC and -ObjC++ over-ride the default
399 # language, but only for "source files". We
400 # just treat everything that isn't a linker
401 # input as a source file.
402 #
403 # FIXME: Clean this up if we move the phase
404 # sequence into the type.
405 if klass is not Types.ObjectType:
406 if args.getLastArg(self.parser.ObjCOption):
407 klass = Types.ObjCType
408 elif args.getLastArg(self.parser.ObjCXXOption):
409 klass = Types.ObjCType
Daniel Dunbar378530c2009-01-05 19:53:30 +0000410 else:
411 assert inputTypeOpt is not None
412 self.claim(inputTypeOpt)
413 klass = inputType
Daniel Dunbara0026f22009-01-16 23:12:12 +0000414
415 # Check that the file exists. It isn't clear this is
416 # worth doing, since the tool presumably does this
417 # anyway, and this just adds an extra stat to the
418 # equation, but this is gcc compatible.
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000419 if inputValue != '-' and not os.path.exists(inputValue):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000420 self.warning("%s: No such file or directory" % inputValue)
421 else:
422 inputs.append((klass, a))
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000423 elif a.opt.isLinkerInput:
424 # Treat as a linker input.
Daniel Dunbar927a5092009-01-06 02:30:10 +0000425 #
426 # FIXME: This might not be good enough. We may
427 # need to introduce another type for this case, so
428 # that other code which needs to know the inputs
429 # handles this properly. Best not to try and lipo
430 # this, for example.
431 inputs.append((Types.ObjectType, a))
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000432 elif a.opt is self.parser.xOption:
Daniel Dunbar927a5092009-01-06 02:30:10 +0000433 inputTypeOpt = a
434 value = args.getValue(a)
435 if value in Types.kTypeSpecifierMap:
436 inputType = Types.kTypeSpecifierMap[value]
437 else:
438 # FIXME: How are we going to handle diagnostics.
439 self.warning("language %s not recognized" % value)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000440
Daniel Dunbar927a5092009-01-06 02:30:10 +0000441 # FIXME: Its not clear why we shouldn't just
442 # revert to unknown. I think this is more likely a
443 # bug / unintended behavior in gcc. Not very
444 # important though.
Daniel Dunbar6cb42052009-01-13 21:07:43 +0000445 inputType = Types.ObjectType
Daniel Dunbar378530c2009-01-05 19:53:30 +0000446
447 # We claim things here so that options for which we silently allow
448 # override only ever claim the used option.
449 if hasCombine:
450 self.claim(hasCombine)
451
452 finalPhase = Phases.Phase.eOrderPostAssemble
453 finalPhaseOpt = None
454
455 # Determine what compilation mode we are in.
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000456 if hasDashE or hasDashM or hasDashMM:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000457 finalPhase = Phases.Phase.eOrderPreprocess
458 finalPhaseOpt = hasDashE
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000459 elif (hasAnalyze or hasSyntaxOnly or
460 hasEmitLLVM or hasDashS):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000461 finalPhase = Phases.Phase.eOrderCompile
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000462 finalPhaseOpt = (hasAnalyze or hasSyntaxOnly or
463 hasEmitLLVM or hasDashS)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000464 elif hasDashC:
465 finalPhase = Phases.Phase.eOrderAssemble
466 finalPhaseOpt = hasDashC
467
468 if finalPhaseOpt:
469 self.claim(finalPhaseOpt)
470
Daniel Dunbar80e48b72009-01-17 00:53:19 +0000471 # Reject -Z* at the top level for now.
472 arg = args.getLastArg(self.parser.ZOption)
473 if arg:
474 raise Arguments.InvalidArgumentsError("%s: unsupported use of internal gcc option" % ' '.join(args.render(arg)))
475
Daniel Dunbar8fd28cd2009-01-28 19:26:20 +0000476 if not inputs and not self.suppressMissingInputWarning:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000477 raise Arguments.InvalidArgumentsError("no input files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000478
479 actions = []
480 linkerInputs = []
481 # FIXME: This is gross.
482 linkPhase = Phases.LinkPhase()
483 for klass,input in inputs:
484 # Figure out what step to start at.
485
486 # FIXME: This should be part of the input class probably?
487 # Altough it doesn't quite fit there either, things like
488 # asm-with-preprocess don't easily fit into a linear scheme.
489
490 # FIXME: I think we are going to end up wanting to just build
491 # a simple FSA which we run the inputs down.
492 sequence = []
493 if klass.preprocess:
494 sequence.append(Phases.PreprocessPhase())
495 if klass == Types.ObjectType:
496 sequence.append(linkPhase)
497 elif klass.onlyAssemble:
498 sequence.extend([Phases.AssemblePhase(),
499 linkPhase])
500 elif klass.onlyPrecompile:
501 sequence.append(Phases.PrecompilePhase())
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000502 elif hasAnalyze:
503 sequence.append(Phases.AnalyzePhase())
504 elif hasSyntaxOnly:
505 sequence.append(Phases.SyntaxOnlyPhase())
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000506 elif hasEmitLLVM:
507 sequence.append(Phases.EmitLLVMPhase())
Daniel Dunbar378530c2009-01-05 19:53:30 +0000508 else:
509 sequence.extend([Phases.CompilePhase(),
510 Phases.AssemblePhase(),
511 linkPhase])
512
513 if sequence[0].order > finalPhase:
514 assert finalPhaseOpt and finalPhaseOpt.opt
515 # FIXME: Explain what type of input file is. Or just match
516 # gcc warning.
Daniel Dunbar74727872009-01-06 01:35:44 +0000517 self.warning("%s: %s input file unused when %s is present" % (args.getValue(input),
Daniel Dunbar378530c2009-01-05 19:53:30 +0000518 sequence[0].name,
519 finalPhaseOpt.opt.name))
520 else:
521 # Build the pipeline for this file.
522
523 current = Phases.InputAction(input, klass)
524 for transition in sequence:
525 # If the current action produces no output, or we are
526 # past what the user requested, we are done.
527 if (current.type is Types.NothingType or
528 transition.order > finalPhase):
529 break
530 else:
531 if isinstance(transition, Phases.PreprocessPhase):
532 assert isinstance(klass.preprocess, Types.InputType)
533 current = Phases.JobAction(transition,
534 [current],
535 klass.preprocess)
536 elif isinstance(transition, Phases.PrecompilePhase):
537 current = Phases.JobAction(transition,
538 [current],
539 Types.PCHType)
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000540 elif isinstance(transition, Phases.AnalyzePhase):
541 output = Types.PlistType
542 current = Phases.JobAction(transition,
543 [current],
544 output)
545 elif isinstance(transition, Phases.SyntaxOnlyPhase):
546 output = Types.NothingType
547 current = Phases.JobAction(transition,
548 [current],
549 output)
Daniel Dunbar34f60f62009-01-26 17:09:15 +0000550 elif isinstance(transition, Phases.EmitLLVMPhase):
551 if hasDashS:
552 output = Types.LLVMAsmType
553 else:
554 output = Types.LLVMBCType
555 current = Phases.JobAction(transition,
556 [current],
557 output)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000558 elif isinstance(transition, Phases.CompilePhase):
Daniel Dunbar90c72cd2009-01-21 01:07:49 +0000559 output = Types.AsmTypeNoPP
Daniel Dunbar378530c2009-01-05 19:53:30 +0000560 current = Phases.JobAction(transition,
561 [current],
562 output)
563 elif isinstance(transition, Phases.AssemblePhase):
564 current = Phases.JobAction(transition,
565 [current],
566 Types.ObjectType)
567 elif transition is linkPhase:
568 linkerInputs.append(current)
569 current = None
570 break
571 else:
572 raise RuntimeError,'Unrecognized transition: %s.' % transition
573 pass
574
575 if current is not None:
576 assert not isinstance(current, Phases.InputAction)
577 actions.append(current)
578
579 if linkerInputs:
580 actions.append(Phases.JobAction(linkPhase,
581 linkerInputs,
582 Types.ImageType))
583
584 return actions
585
Daniel Dunbar74727872009-01-06 01:35:44 +0000586 def buildPipeline(self, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000587 # FIXME: We need to handle canonicalization of the specified arch.
588
Daniel Dunbar31c80812009-01-20 21:29:14 +0000589 archs = {}
Daniel Dunbar445c46d2009-01-20 01:53:54 +0000590 hasDashM = args.getLastArg(self.parser.MGroup)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000591 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbar74727872009-01-06 01:35:44 +0000592 for arg in args:
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000593 if arg.opt is self.parser.archOption:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000594 # FIXME: Canonicalize this.
595 archName = args.getValue(arg)
596 archs[archName] = arg
597
598 archs = archs.values()
Daniel Dunbar378530c2009-01-05 19:53:30 +0000599 if not archs:
Daniel Dunbarf3d5ca02009-01-13 04:05:40 +0000600 archs.append(args.makeSeparateArg(self.hostInfo.getArchName(args),
Daniel Dunbar1ba90982009-01-07 18:54:26 +0000601 self.parser.archOption))
Daniel Dunbar378530c2009-01-05 19:53:30 +0000602
Daniel Dunbar74727872009-01-06 01:35:44 +0000603 actions = self.buildNormalPipeline(args)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000604
605 # FIXME: Use custom exception for this.
606 #
607 # FIXME: We killed off some others but these aren't yet detected in
608 # a functional manner. If we added information to jobs about which
609 # "auxiliary" files they wrote then we could detect the conflict
610 # these cause downstream.
611 if len(archs) > 1:
612 if hasDashM:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000613 raise Arguments.InvalidArgumentsError("Cannot use -M options with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000614 elif hasSaveTemps:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000615 raise Arguments.InvalidArgumentsError("Cannot use -save-temps with multiple arch flags.")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000616
617 # Execute once per arch.
618 finalActions = []
619 for p in actions:
620 # Make sure we can lipo this kind of output. If not (and it
621 # is an actual output) then we disallow, since we can't
622 # create an output file with the right name without
623 # overwriting it. We could remove this oddity by just
624 # changing the output names to include the arch, which would
625 # also fix -save-temps. Compatibility wins for now.
626 #
627 # FIXME: Is this error substantially less useful than
628 # gcc-dd's? The main problem is that "Cannot use compiler
629 # output with multiple arch flags" won't make sense to most
630 # developers.
631 if (len(archs) > 1 and
632 p.type not in (Types.NothingType,Types.ObjectType,Types.ImageType)):
Daniel Dunbara0026f22009-01-16 23:12:12 +0000633 raise Arguments.InvalidArgumentsError('Cannot use %s output with multiple arch flags.' % p.type.name)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000634
635 inputs = []
636 for arch in archs:
637 inputs.append(Phases.BindArchAction(p, arch))
638
639 # Lipo if necessary. We do it this way because we need to set
640 # the arch flag so that -Xarch_ gets rewritten.
641 if len(inputs) == 1 or p.type == Types.NothingType:
642 finalActions.extend(inputs)
643 else:
644 finalActions.append(Phases.JobAction(Phases.LipoPhase(),
645 inputs,
646 p.type))
647
Daniel Dunbar378530c2009-01-05 19:53:30 +0000648 return finalActions
649
Daniel Dunbar74727872009-01-06 01:35:44 +0000650 def bindPhases(self, phases, args):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000651 jobs = Jobs.JobList()
652
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000653 finalOutput = args.getLastArg(self.parser.oOption)
Daniel Dunbara33176f2009-01-21 18:49:34 +0000654 hasSaveTemps = args.getLastArg(self.parser.saveTempsOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000655 hasNoIntegratedCPP = args.getLastArg(self.parser.noIntegratedCPPOption)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000656 hasTraditionalCPP = args.getLastArg(self.parser.traditionalCPPOption)
Daniel Dunbare9f1a692009-01-06 06:12:13 +0000657 hasPipe = args.getLastArg(self.parser.pipeOption)
Daniel Dunbarfc2ad022009-01-12 03:33:58 +0000658
Daniel Dunbar378530c2009-01-05 19:53:30 +0000659 # We claim things here so that options for which we silently allow
660 # override only ever claim the used option.
661 if hasPipe:
662 self.claim(hasPipe)
663 # FIXME: Hack, override -pipe till we support it.
Daniel Dunbare19ed8e2009-01-17 02:02:35 +0000664 if hasSaveTemps:
665 self.warning('-pipe ignored because -save-temps specified')
666 hasPipe = None
Daniel Dunbar378530c2009-01-05 19:53:30 +0000667 # Claim these here. Its not completely accurate but any warnings
668 # about these being unused are likely to be noise anyway.
669 if hasSaveTemps:
670 self.claim(hasSaveTemps)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000671
672 if hasTraditionalCPP:
673 self.claim(hasTraditionalCPP)
674 elif hasNoIntegratedCPP:
Daniel Dunbar378530c2009-01-05 19:53:30 +0000675 self.claim(hasNoIntegratedCPP)
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000676
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000677 # FIXME: Move to... somewhere else.
Daniel Dunbar378530c2009-01-05 19:53:30 +0000678 class InputInfo:
679 def __init__(self, source, type, baseInput):
680 self.source = source
681 self.type = type
682 self.baseInput = baseInput
683
684 def __repr__(self):
685 return '%s(%r, %r, %r)' % (self.__class__.__name__,
686 self.source, self.type, self.baseInput)
Daniel Dunbare5fb6792009-01-13 06:25:31 +0000687
688 def isOriginalInput(self):
689 return self.source is self.baseInput
Daniel Dunbar378530c2009-01-05 19:53:30 +0000690
Daniel Dunbarb3492762009-01-13 18:51:26 +0000691 def createJobs(tc, phase,
692 canAcceptPipe=False, atTopLevel=False, arch=None,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000693 tcArgs=None, linkingOutput=None):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000694 if isinstance(phase, Phases.InputAction):
695 return InputInfo(phase.filename, phase.type, phase.filename)
696 elif isinstance(phase, Phases.BindArchAction):
Daniel Dunbar74727872009-01-06 01:35:44 +0000697 archName = args.getValue(phase.arch)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000698 tc = self.hostInfo.getToolChainForArch(archName)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000699 return createJobs(tc, phase.inputs[0],
700 canAcceptPipe, atTopLevel, phase.arch,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000701 None, linkingOutput)
Daniel Dunbarb3492762009-01-13 18:51:26 +0000702
703 if tcArgs is None:
704 tcArgs = tc.translateArgs(args, arch)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000705
706 assert isinstance(phase, Phases.JobAction)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000707 tool = tc.selectTool(phase)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000708
709 # See if we should use an integrated CPP. We only use an
710 # integrated cpp when we have exactly one input, since this is
711 # the only use case we care about.
712 useIntegratedCPP = False
713 inputList = phase.inputs
714 if (not hasNoIntegratedCPP and
Daniel Dunbarf86e98a2009-01-12 09:23:15 +0000715 not hasTraditionalCPP and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000716 not hasSaveTemps and
717 tool.hasIntegratedCPP()):
718 if (len(phase.inputs) == 1 and
Daniel Dunbar7d494092009-01-20 00:47:24 +0000719 isinstance(phase.inputs[0], Phases.JobAction) and
Daniel Dunbar378530c2009-01-05 19:53:30 +0000720 isinstance(phase.inputs[0].phase, Phases.PreprocessPhase)):
721 useIntegratedCPP = True
722 inputList = phase.inputs[0].inputs
723
724 # Only try to use pipes when exactly one input.
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000725 attemptToPipeInput = len(inputList) == 1 and tool.acceptsPipedInput()
726 inputs = [createJobs(tc, p, attemptToPipeInput, False,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000727 arch, tcArgs, linkingOutput)
Daniel Dunbar758cf642009-01-11 22:06:22 +0000728 for p in inputList]
Daniel Dunbar378530c2009-01-05 19:53:30 +0000729
730 # Determine if we should output to a pipe.
731 canOutputToPipe = canAcceptPipe and tool.canPipeOutput()
732 outputToPipe = False
733 if canOutputToPipe:
734 # Some things default to writing to a pipe if the final
735 # phase and there was no user override.
736 #
737 # FIXME: What is the best way to handle this?
Daniel Dunbar1b391272009-01-18 21:35:24 +0000738 if atTopLevel:
739 if (isinstance(phase.phase, Phases.PreprocessPhase) and
740 not finalOutput):
741 outputToPipe = True
Daniel Dunbar378530c2009-01-05 19:53:30 +0000742 elif hasPipe:
743 outputToPipe = True
744
745 # Figure out where to put the job (pipes).
746 jobList = jobs
Daniel Dunbar1e46f552009-01-22 23:19:32 +0000747 if isinstance(inputs[0].source, Jobs.PipedJob):
Daniel Dunbar378530c2009-01-05 19:53:30 +0000748 jobList = inputs[0].source
Daniel Dunbar31c80812009-01-20 21:29:14 +0000749
Daniel Dunbar378530c2009-01-05 19:53:30 +0000750 baseInput = inputs[0].baseInput
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000751 output,jobList = self.getOutputName(phase, outputToPipe, jobs, jobList, baseInput,
752 args, atTopLevel, hasSaveTemps, finalOutput)
Daniel Dunbarb421dba2009-01-07 18:40:45 +0000753 tool.constructJob(phase, arch, jobList, inputs, output, phase.type,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000754 tcArgs, linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000755
756 return InputInfo(output, phase.type, baseInput)
757
758 # It is an error to provide a -o option if we are making multiple
759 # output files.
760 if finalOutput and len([a for a in phases if a.type is not Types.NothingType]) > 1:
Daniel Dunbara0026f22009-01-16 23:12:12 +0000761 raise Arguments.InvalidArgumentsError("cannot specify -o when generating multiple files")
Daniel Dunbar378530c2009-01-05 19:53:30 +0000762
763 for phase in phases:
Daniel Dunbar31c80812009-01-20 21:29:14 +0000764 # If we are linking an image for multiple archs then the
765 # linker wants -arch_multiple and -final_output <final image
766 # name>. Unfortunately this requires some gross contortions.
767 #
768 # FIXME: This is a hack; find a cleaner way to integrate this
769 # into the process.
770 linkingOutput = None
771 if (isinstance(phase, Phases.JobAction) and
772 isinstance(phase.phase, Phases.LipoPhase)):
773 finalOutput = args.getLastArg(self.parser.oOption)
774 if finalOutput:
775 linkingOutput = finalOutput
776 else:
777 linkingOutput = args.makeSeparateArg('a.out',
778 self.parser.oOption)
779
Daniel Dunbarb3492762009-01-13 18:51:26 +0000780 createJobs(self.toolChain, phase,
Daniel Dunbar31c80812009-01-20 21:29:14 +0000781 canAcceptPipe=True, atTopLevel=True,
782 linkingOutput=linkingOutput)
Daniel Dunbar378530c2009-01-05 19:53:30 +0000783
784 return jobs
Daniel Dunbar31c80812009-01-20 21:29:14 +0000785
786 def getOutputName(self, phase, outputToPipe, jobs, jobList, baseInput,
787 args, atTopLevel, hasSaveTemps, finalOutput):
788 # Figure out where to put the output.
789 if phase.type == Types.NothingType:
790 output = None
791 elif outputToPipe:
792 if isinstance(jobList, Jobs.PipedJob):
793 output = jobList
794 else:
795 jobList = output = Jobs.PipedJob([])
796 jobs.addJob(output)
797 else:
798 # Figure out what the derived output location would be.
799 #
800 # FIXME: gcc has some special case in here so that it doesn't
801 # create output files if they would conflict with an input.
802 if phase.type is Types.ImageType:
803 namedOutput = "a.out"
804 else:
805 inputName = args.getValue(baseInput)
806 base,_ = os.path.splitext(inputName)
807 assert phase.type.tempSuffix is not None
808 namedOutput = base + '.' + phase.type.tempSuffix
809
810 # Output to user requested destination?
811 if atTopLevel and finalOutput:
812 output = finalOutput
813 # Contruct a named destination?
814 elif atTopLevel or hasSaveTemps:
815 # As an annoying special case, pch generation
816 # doesn't strip the pathname.
817 if phase.type is Types.PCHType:
818 outputName = namedOutput
819 else:
820 outputName = os.path.basename(namedOutput)
821 output = args.makeSeparateArg(outputName,
822 self.parser.oOption)
823 else:
824 # Output to temp file...
825 fd,filename = tempfile.mkstemp(suffix='.'+phase.type.tempSuffix)
826 output = args.makeSeparateArg(filename,
827 self.parser.oOption)
Daniel Dunbard9b7a742009-01-21 00:05:15 +0000828 return output,jobList